From 46722fb73a7e0b9bcaae380bf40cb9ae609e993a Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Sat, 2 Mar 2024 15:16:24 +0800 Subject: [PATCH 1/6] HDDS-10494. Recon datanode page to support sort by other storage metrics --- .../src/views/datanodes/datanodes.tsx | 469 ++++++++++-------- 1 file changed, 265 insertions(+), 204 deletions(-) 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 c42bd8c1f91b..8c3068178944 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,26 +17,27 @@ */ import React from 'react'; -import {Table, Icon, Tooltip, Popover} from 'antd'; +import {Icon, Popover, Table, Tooltip} from 'antd'; import {PaginationConfig} from 'antd/lib/pagination'; import moment from 'moment'; import {ReplicationIcon} from 'utils/themeIcons'; import StorageBar from 'components/storageBar/storageBar'; import { - DatanodeState, - DatanodeStateList, DatanodeOpState, DatanodeOpStateList, + DatanodeState, + DatanodeStateList, IStorageReport } from 'types/datanode.types'; import './datanodes.less'; import {AutoReloadHelper} from 'utils/autoReloadHelper'; import AutoReloadPanel from 'components/autoReloadPanel/autoReloadPanel'; -import {MultiSelect, IOption} from 'components/multiSelect/multiSelect'; +import {IOption, MultiSelect} 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} from 'utils/axiosRequestHelper'; +import {ColumnProps} from "antd/es/table"; interface IDatanodeResponse { hostname: string; @@ -96,6 +97,7 @@ interface IDatanodesState { lastUpdated: number; selectedColumns: IOption[]; columnOptions: IOption[]; + filteredInfo: Partial>; } const renderDatanodeState = (state: DatanodeState) => { @@ -120,204 +122,11 @@ const renderDatanodeOpState = (opState: DatanodeOpState) => { return {icon} {opState}; }; -const COLUMNS = [ - { - title: 'Hostname', - dataIndex: 'hostname', - key: 'hostname', - isVisible: true, - isSearchable: true, - sorter: (a: IDatanode, b: IDatanode) => a.hostname.localeCompare(b.hostname, undefined, {numeric: true}), - defaultSortOrder: 'ascend' as const, - fixed: 'left' - }, - { - title: 'State', - dataIndex: 'state', - key: 'state', - isVisible: true, - isSearchable: true, - filterMultiple: true, - filters: DatanodeStateList && DatanodeStateList.map(state => ({text: state, value: state})), - onFilter: (value: DatanodeState, record: IDatanode) => record.state === value, - render: (text: DatanodeState) => renderDatanodeState(text), - sorter: (a: IDatanode, b: IDatanode) => a.state.localeCompare(b.state) - }, - { - title: 'Operational State', - dataIndex: 'opState', - key: 'opState', - isVisible: true, - isSearchable: true, - filterMultiple: true, - filters: DatanodeOpStateList && DatanodeOpStateList.map(state => ({text: state, value: state})), - onFilter: (value: DatanodeOpState, record: IDatanode) => record.opState === value, - render: (text: DatanodeOpState) => renderDatanodeOpState(text), - sorter: (a: IDatanode, b: IDatanode) => a.opState.localeCompare(b.opState) - }, - - { - title: 'Uuid', - dataIndex: 'uuid', - key: 'uuid', - isVisible: true, - isSearchable: true, - sorter: (a: IDatanode, b: IDatanode) => a.uuid.localeCompare(b.uuid), - defaultSortOrder: 'ascend' as const - }, - { - title: 'Storage Capacity', - dataIndex: 'storageUsed', - key: 'storageUsed', - isVisible: true, - sorter: (a: IDatanode, b: IDatanode) => a.storageRemaining - b.storageRemaining, - render: (text: string, record: IDatanode) => ( - - )}, - { - title: 'Last Heartbeat', - dataIndex: 'lastHeartbeat', - key: 'lastHeartbeat', - isVisible: true, - sorter: (a: IDatanode, b: IDatanode) => a.lastHeartbeat - b.lastHeartbeat, - render: (heartbeat: number) => { - return heartbeat > 0 ? getTimeDiffFromTimestamp(heartbeat) : 'NA'; - } - }, - { - title: 'Pipeline ID(s)', - dataIndex: 'pipelines', - key: 'pipelines', - isVisible: true, - render: (pipelines: IPipeline[], record: IDatanode) => { - let firstThreePipelinesIDs = []; - let remainingPipelinesIDs: any[] = []; - firstThreePipelinesIDs = pipelines && pipelines.filter((element, index) => index < 3); - remainingPipelinesIDs = pipelines && pipelines.slice(3, pipelines.length); - - const RenderPipelineIds = ({ pipelinesIds }) => { - return pipelinesIds && pipelinesIds.map((pipeline: any, index: any) => ( -
- - {pipeline.pipelineID} -
- )) - } - - return ( - <> - { - - } - { - remainingPipelinesIDs.length > 0 && - } title="Remaining pipelines" placement="rightTop" trigger="hover"> - {`... and ${remainingPipelinesIDs.length} more pipelines`} - - } - - ); - } - }, - { - title: - - Leader Count  - - - - , - dataIndex: 'leaderCount', - key: 'leaderCount', - isVisible: true, - isSearchable: true, - sorter: (a: IDatanode, b: IDatanode) => a.leaderCount - b.leaderCount - }, - { - title: 'Containers', - dataIndex: 'containers', - key: 'containers', - isVisible: true, - isSearchable: true, - sorter: (a: IDatanode, b: IDatanode) => a.containers - b.containers - }, - { - title: - - Open Containers  - - - - , - dataIndex: 'openContainers', - key: 'openContainers', - isVisible: true, - isSearchable: true, - sorter: (a: IDatanode, b: IDatanode) => a.openContainers - b.openContainers - }, - { - title: 'Version', - dataIndex: 'version', - key: 'version', - isVisible: true, - isSearchable: true, - sorter: (a: IDatanode, b: IDatanode) => a.version.localeCompare(b.version), - defaultSortOrder: 'ascend' as const - }, - { - title: 'Setup Time', - dataIndex: 'setupTime', - key: 'setupTime', - isVisible: true, - sorter: (a: IDatanode, b: IDatanode) => a.setupTime - b.setupTime, - render: (uptime: number) => { - return uptime > 0 ? moment(uptime).format('ll LTS') : 'NA'; - } - }, - { - title: 'Revision', - dataIndex: 'revision', - key: 'revision', - isVisible: true, - isSearchable: true, - sorter: (a: IDatanode, b: IDatanode) => a.revision.localeCompare(b.revision), - defaultSortOrder: 'ascend' as const - }, - { - title: 'Build Date', - dataIndex: 'buildDate', - key: 'buildDate', - isVisible: true, - isSearchable: true, - sorter: (a: IDatanode, b: IDatanode) => a.buildDate.localeCompare(b.buildDate), - defaultSortOrder: 'ascend' as const - }, - { - title: 'Network Location', - dataIndex: 'networkLocation', - key: 'networkLocation', - isVisible: true, - isSearchable: true, - sorter: (a: IDatanode, b: IDatanode) => a.networkLocation.localeCompare(b.networkLocation), - defaultSortOrder: 'ascend' as const - } -]; - const allColumnsOption: IOption = { label: 'Select all', value: '*' }; -const defaultColumns: IOption[] = COLUMNS.map(column => ({ - label: column.key, - value: column.key -})); const getTimeDiffFromTimestamp = (timestamp: number): string => { const timestampDate = new Date(timestamp); @@ -343,21 +152,272 @@ let cancelSignal: AbortController; export class Datanodes extends React.Component, IDatanodesState> { autoReload: AutoReloadHelper; + columns: ColumnProps[]; + defaultColumns: IOption[]; constructor(props = {}) { super(props); + this.columns = [ + { + title: 'Hostname', + dataIndex: 'hostname', + key: 'hostname', + isVisible: true, + isSearchable: true, + sorter: (a: IDatanode, b: IDatanode) => a.hostname.localeCompare(b.hostname, undefined, {numeric: true}), + defaultSortOrder: 'ascend' as const, + fixed: 'left' + }, + { + title: 'State', + dataIndex: 'state', + key: 'state', + isVisible: true, + isSearchable: true, + filterMultiple: true, + filters: DatanodeStateList && DatanodeStateList.map(state => ({text: state, value: state})), + onFilter: (value: DatanodeState, record: IDatanode) => record.state === value, + render: (text: DatanodeState) => renderDatanodeState(text), + sorter: (a: IDatanode, b: IDatanode) => a.state.localeCompare(b.state) + }, + { + title: 'Operational State', + dataIndex: 'opState', + key: 'opState', + isVisible: true, + isSearchable: true, + filterMultiple: true, + filters: DatanodeOpStateList && DatanodeOpStateList.map(state => ({text: state, value: state})), + onFilter: (value: DatanodeOpState, record: IDatanode) => record.opState === value, + render: (text: DatanodeOpState) => renderDatanodeOpState(text), + sorter: (a: IDatanode, b: IDatanode) => a.opState.localeCompare(b.opState) + }, + { + title: 'Uuid', + dataIndex: 'uuid', + key: 'uuid', + isVisible: true, + isSearchable: true, + sorter: (a: IDatanode, b: IDatanode) => a.uuid.localeCompare(b.uuid), + defaultSortOrder: 'ascend' as const + }, + { + title: 'Storage Capacity', + dataIndex: 'storageCapacity', + key: 'storageCapacity', + isVisible: true, + filters: [ + { + text: 'Storage Remaining (default)', + value: 'storageRemaining', + }, + { + text: 'Storage Used', + value: 'storageUsed', + }, + { + text: 'Storage Committed', + value: 'storageCommitted' + }, + { + text: 'Storage Total', + value: 'storageTotal', + }, + + ], + filterIcon: () => ( + + ), + filterMultiple: false, + onFilter: () => { + // This filter is only used for placeholder, so return true regardless of the filter + return true; + }, + sorter: (a: IDatanode, b: IDatanode) => { + let sortBy = "storageRemaining" + if (this.state.filteredInfo.storageCapacity && + this.state.filteredInfo.storageCapacity.length > 0) { + sortBy = this.state.filteredInfo.storageCapacity[0] + } + if (!sortBy) { + return a.storageRemaining - b.storageRemaining + } + + if (sortBy === "storageUsed") { + return a.storageUsed - b.storageUsed; + } else if (sortBy === "storageCommitted") { + return a.storageCommitted - b.storageCommitted; + } else if (sortBy === "storageTotal") { + return a.storageTotal- b.storageTotal; + } else { + return a.storageRemaining - b.storageRemaining + } + }, + render: (text: string, record: IDatanode) => ( + + )}, + { + title: 'Last Heartbeat', + dataIndex: 'lastHeartbeat', + key: 'lastHeartbeat', + isVisible: true, + sorter: (a: IDatanode, b: IDatanode) => a.lastHeartbeat - b.lastHeartbeat, + render: (heartbeat: number) => { + return heartbeat > 0 ? getTimeDiffFromTimestamp(heartbeat) : 'NA'; + } + }, + { + title: 'Pipeline ID(s)', + dataIndex: 'pipelines', + key: 'pipelines', + isVisible: true, + render: (pipelines: IPipeline[], record: IDatanode) => { + let firstThreePipelinesIDs = []; + let remainingPipelinesIDs: any[] = []; + firstThreePipelinesIDs = pipelines && pipelines.filter((element, index) => index < 3); + remainingPipelinesIDs = pipelines && pipelines.slice(3, pipelines.length); + + const RenderPipelineIds = ({ pipelinesIds }) => { + return pipelinesIds && pipelinesIds.map((pipeline: any, index: any) => ( +
+ + {pipeline.pipelineID} +
+ )) + } + + return ( + <> + { + + } + { + remainingPipelinesIDs.length > 0 && + } title="Remaining pipelines" placement="rightTop" trigger="hover"> + {`... and ${remainingPipelinesIDs.length} more pipelines`} + + } + + ); + } + }, + { + title: + + Leader Count  + + + + , + dataIndex: 'leaderCount', + key: 'leaderCount', + isVisible: true, + isSearchable: true, + sorter: (a: IDatanode, b: IDatanode) => a.leaderCount - b.leaderCount + }, + { + title: 'Containers', + dataIndex: 'containers', + key: 'containers', + isVisible: true, + isSearchable: true, + sorter: (a: IDatanode, b: IDatanode) => a.containers - b.containers + }, + { + title: + + Open Containers  + + + + , + dataIndex: 'openContainers', + key: 'openContainers', + isVisible: true, + isSearchable: true, + sorter: (a: IDatanode, b: IDatanode) => a.openContainers - b.openContainers + }, + { + title: 'Version', + dataIndex: 'version', + key: 'version', + isVisible: true, + isSearchable: true, + sorter: (a: IDatanode, b: IDatanode) => a.version.localeCompare(b.version), + defaultSortOrder: 'ascend' as const + }, + { + title: 'Setup Time', + dataIndex: 'setupTime', + key: 'setupTime', + isVisible: true, + sorter: (a: IDatanode, b: IDatanode) => a.setupTime - b.setupTime, + render: (uptime: number) => { + return uptime > 0 ? moment(uptime).format('ll LTS') : 'NA'; + } + }, + { + title: 'Revision', + dataIndex: 'revision', + key: 'revision', + isVisible: true, + isSearchable: true, + sorter: (a: IDatanode, b: IDatanode) => a.revision.localeCompare(b.revision), + defaultSortOrder: 'ascend' as const + }, + { + title: 'Build Date', + dataIndex: 'buildDate', + key: 'buildDate', + isVisible: true, + isSearchable: true, + sorter: (a: IDatanode, b: IDatanode) => a.buildDate.localeCompare(b.buildDate), + defaultSortOrder: 'ascend' as const + }, + { + title: 'Network Location', + dataIndex: 'networkLocation', + key: 'networkLocation', + isVisible: true, + isSearchable: true, + sorter: (a: IDatanode, b: IDatanode) => a.networkLocation.localeCompare(b.networkLocation), + defaultSortOrder: 'ascend' as const + } + ]; + + this.defaultColumns = this.columns.map(column => ({ + label: column.key, + value: column.key + })); + this.state = { loading: false, dataSource: [], totalCount: 0, lastUpdated: 0, selectedColumns: [], - columnOptions: defaultColumns + columnOptions: this.defaultColumns, + filteredInfo: {}, }; + + this.autoReload = new AutoReloadHelper(this._loadData); } - _handleColumnChange = (selected: ValueType, _action: ActionMeta) => { + _handleChange = (pagination: PaginationConfig, + filters: Partial>) => { + this.setState({ + filteredInfo: filters, + }); + } + + _handleColumnChange = (selected: ValueType, _action: ActionMeta) => { const selectedColumns = (selected == null ? [] : selected as IOption[]); this.setState({ selectedColumns @@ -365,11 +425,10 @@ export class Datanodes extends React.Component, IDatanode }; _getSelectedColumns = (selected: IOption[]) => { - const selectedColumns = selected.length > 0 ? selected : COLUMNS.filter(column => column.isVisible).map(column => ({ + return selected.length > 0 ? selected : this.columns.filter(column => column.isVisible).map(column => ({ label: column.key, value: column.key })); - return selectedColumns; }; _loadData = () => { @@ -377,7 +436,7 @@ export class Datanodes extends React.Component, IDatanode loading: true, selectedColumns: this._getSelectedColumns(prevState.selectedColumns) })); - + const { request, controller } = AxiosGetHelper('/api/v1/datanodes', cancelSignal); cancelSignal = controller; request.then(response => { @@ -443,6 +502,7 @@ export class Datanodes extends React.Component, IDatanode showSizeChanger: true, onShowSizeChange: this.onShowSizeChange }; + return (
@@ -472,7 +532,8 @@ export class Datanodes extends React.Component, IDatanode
((filtered, column) => { + onChange={this._handleChange} + columns={this.columns.reduce((filtered, column) => { if (selectedColumns.some(e => e.value === column.key)) { if (column.isSearchable) { const newColumn = { From 7921c92d6ca82c04e2496779a50d9a1551fa3839 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 11 Mar 2024 10:58:22 +0800 Subject: [PATCH 2/6] Add sort by storage utilization metric --- .../ozone-recon-web/src/views/datanodes/datanodes.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) 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 8c3068178944..b4749557186e 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 @@ -34,7 +34,7 @@ import {AutoReloadHelper} from 'utils/autoReloadHelper'; import AutoReloadPanel from 'components/autoReloadPanel/autoReloadPanel'; import {IOption, MultiSelect} from 'components/multiSelect/multiSelect'; import {ActionMeta, ValueType} from 'react-select'; -import {showDataFetchError} from 'utils/common'; +import {getCapacityPercent, showDataFetchError} from 'utils/common'; import {ColumnSearch} from 'utils/columnSearch'; import {AxiosGetHelper} from 'utils/axiosRequestHelper'; import {ColumnProps} from "antd/es/table"; @@ -223,7 +223,10 @@ export class Datanodes extends React.Component, IDatanode text: 'Storage Total', value: 'storageTotal', }, - + { + text: 'Storage Utilization', + value: 'storageUtilization', + } ], filterIcon: () => ( @@ -249,6 +252,10 @@ export class Datanodes extends React.Component, IDatanode return a.storageCommitted - b.storageCommitted; } else if (sortBy === "storageTotal") { return a.storageTotal- b.storageTotal; + } else if (sortBy === 'storageUtilization') { + // See totalUsed calculation in storageBar.tsx + return getCapacityPercent(a.storageTotal - a.storageRemaining, a.storageRemaining) - + getCapacityPercent(b.storageTotal - b.storageRemaining, b.storageRemaining) } else { return a.storageRemaining - b.storageRemaining } From 0d1d4a6da943a0b5b1bce81c85d71ffdb8e888ab Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 11 Mar 2024 13:46:25 +0800 Subject: [PATCH 3/6] Revert auto import reordering --- .../ozone-recon-web/src/views/datanodes/datanodes.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 b4749557186e..c40783b6eed1 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,26 +17,26 @@ */ import React from 'react'; -import {Icon, Popover, Table, Tooltip} from 'antd'; +import {Table, Icon, Tooltip, Popover} from 'antd'; import {PaginationConfig} from 'antd/lib/pagination'; import moment from 'moment'; import {ReplicationIcon} from 'utils/themeIcons'; import StorageBar from 'components/storageBar/storageBar'; import { - DatanodeOpState, - DatanodeOpStateList, DatanodeState, DatanodeStateList, + DatanodeOpState, + DatanodeOpStateList, IStorageReport } from 'types/datanode.types'; import './datanodes.less'; import {AutoReloadHelper} from 'utils/autoReloadHelper'; import AutoReloadPanel from 'components/autoReloadPanel/autoReloadPanel'; -import {IOption, MultiSelect} from 'components/multiSelect/multiSelect'; +import {MultiSelect, IOption} from 'components/multiSelect/multiSelect'; import {ActionMeta, ValueType} from 'react-select'; import {getCapacityPercent, showDataFetchError} from 'utils/common'; import {ColumnSearch} from 'utils/columnSearch'; -import {AxiosGetHelper} from 'utils/axiosRequestHelper'; +import { AxiosGetHelper } from 'utils/axiosRequestHelper'; import {ColumnProps} from "antd/es/table"; interface IDatanodeResponse { From 2db7e683619ca96d5f226d3937a3ae092374d36a Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Tue, 12 Mar 2024 14:20:20 +0800 Subject: [PATCH 4/6] Fix formula --- .../recon/ozone-recon-web/src/views/datanodes/datanodes.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 c40783b6eed1..2a01ba5d9113 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 @@ -254,8 +254,8 @@ export class Datanodes extends React.Component, IDatanode return a.storageTotal- b.storageTotal; } else if (sortBy === 'storageUtilization') { // See totalUsed calculation in storageBar.tsx - return getCapacityPercent(a.storageTotal - a.storageRemaining, a.storageRemaining) - - getCapacityPercent(b.storageTotal - b.storageRemaining, b.storageRemaining) + return getCapacityPercent(a.storageTotal - a.storageRemaining, a.storageTotal) - + getCapacityPercent(b.storageTotal - b.storageRemaining, b.storageTotal) } else { return a.storageRemaining - b.storageRemaining } From 5b70276a88f271de06e36f45475fd92d36dfe252 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Wed, 13 Mar 2024 10:22:20 +0800 Subject: [PATCH 5/6] Update tooltip and descriptions for storage committed and utilization --- .../src/components/storageBar/storageBar.less | 4 ++++ .../src/components/storageBar/storageBar.tsx | 6 ++++-- .../recon/ozone-recon-web/src/views/datanodes/datanodes.tsx | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/storageBar/storageBar.less b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/storageBar/storageBar.less index ecba534cc08c..f5cf7aec5b1c 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/storageBar/storageBar.less +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/storageBar/storageBar.less @@ -50,3 +50,7 @@ .committed-bg { color: @progress-dark-grey; } + +.percentage-bg { + color: linear-gradient(to right, @progress-green 50%, @progress-blue 50%); +} diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/storageBar/storageBar.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/storageBar/storageBar.tsx index 9263c6817beb..199ff3351d29 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/storageBar/storageBar.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/storageBar/storageBar.tsx @@ -51,12 +51,14 @@ class StorageBar extends React.Component { const {total, used, remaining, committed, showMeta} = this.props; const nonOzoneUsed = total - remaining - used; const totalUsed = total - remaining; + const percentageUsed = getCapacityPercent(totalUsed, total) const tooltip = (
+
Total storage utilization ({percentageUsed}%)
Ozone Used ({size(used)})
Non Ozone Used ({size(nonOzoneUsed)})
Remaining ({size(remaining)})
-
Container Pre-allocated ({size(committed)})
+
Committed ({size(committed)})
); const metaElement = showMeta ?
{size(used)} + {size(nonOzoneUsed)} / {size(total)}
: null; @@ -66,7 +68,7 @@ class StorageBar extends React.Component { {metaElement} 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 2a01ba5d9113..b97fab7db47b 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 @@ -224,7 +224,7 @@ export class Datanodes extends React.Component, IDatanode value: 'storageTotal', }, { - text: 'Storage Utilization', + text: 'Total Storage Utilization %', value: 'storageUtilization', } ], From d63f0799055d2cd9eb7e45c11275d28975a426f7 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Wed, 13 Mar 2024 11:11:08 +0800 Subject: [PATCH 6/6] Add more filter metric from the storage bar --- .../src/components/storageBar/storageBar.tsx | 8 ++-- .../ozone-recon-web/src/utils/common.tsx | 3 ++ .../src/views/datanodes/datanodes.tsx | 38 ++++++++++++------- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/storageBar/storageBar.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/storageBar/storageBar.tsx index 199ff3351d29..82d330ef4c1a 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/storageBar/storageBar.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/storageBar/storageBar.tsx @@ -22,7 +22,7 @@ import {withRouter} from 'react-router-dom'; import {RouteComponentProps} from 'react-router'; import {FilledIcon} from 'utils/themeIcons'; import Tooltip from 'antd/lib/tooltip'; -import {getCapacityPercent} from 'utils/common'; +import {getCapacityPercent, getNonOzoneUsed, getTotalUsed} from 'utils/common'; import filesize from 'filesize'; import './storageBar.less'; @@ -49,16 +49,16 @@ class StorageBar extends React.Component { render() { const {total, used, remaining, committed, showMeta} = this.props; - const nonOzoneUsed = total - remaining - used; - const totalUsed = total - remaining; + const nonOzoneUsed = getNonOzoneUsed(total, remaining, used); + const totalUsed = getTotalUsed(total, remaining); const percentageUsed = getCapacityPercent(totalUsed, total) const tooltip = (
Total storage utilization ({percentageUsed}%)
Ozone Used ({size(used)})
Non Ozone Used ({size(nonOzoneUsed)})
+
Pre-allocated ({size(committed)})
Remaining ({size(remaining)})
-
Committed ({size(committed)})
); const metaElement = showMeta ?
{size(used)} + {size(nonOzoneUsed)} / {size(total)}
: null; diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/common.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/common.tsx index 9f9c099201d6..a8b1a13e88b1 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/common.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/common.tsx @@ -21,6 +21,9 @@ import {notification} from 'antd'; export const getCapacityPercent = (used: number, total: number) => Math.round((used / total) * 100); +export const getNonOzoneUsed = (total: number, remaining: number, ozoneUsed: number) => getTotalUsed(total, remaining) - ozoneUsed; +export const getTotalUsed = (total: number, remaining: number) => total - remaining; + export const timeFormat = (time: number) => time > 0 ? moment(time).format('lll') : 'NA'; 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 b97fab7db47b..42b9e3e07a93 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 @@ -34,7 +34,7 @@ import {AutoReloadHelper} from 'utils/autoReloadHelper'; import AutoReloadPanel from 'components/autoReloadPanel/autoReloadPanel'; import {MultiSelect, IOption} from 'components/multiSelect/multiSelect'; import {ActionMeta, ValueType} from 'react-select'; -import {getCapacityPercent, showDataFetchError} from 'utils/common'; +import {getCapacityPercent, getNonOzoneUsed, getTotalUsed, showDataFetchError} from 'utils/common'; import {ColumnSearch} from 'utils/columnSearch'; import { AxiosGetHelper } from 'utils/axiosRequestHelper'; import {ColumnProps} from "antd/es/table"; @@ -212,21 +212,29 @@ export class Datanodes extends React.Component, IDatanode value: 'storageRemaining', }, { - text: 'Storage Used', - value: 'storageUsed', + text: 'Storage Used - Ozone', + value: 'storageUsedOzone', }, { - text: 'Storage Committed', + text: 'Storage Used - Non-Ozone', + value: 'storageUsedNonOzone', + }, + { + text: 'Storage Total Used', + value: 'storageUsedTotal', + }, + { + text: 'Storage Total Used (%)', + value: 'storageUsedTotalUtilization', + }, + { + text: 'Storage Pre-allocated', value: 'storageCommitted' }, { text: 'Storage Total', value: 'storageTotal', }, - { - text: 'Total Storage Utilization %', - value: 'storageUtilization', - } ], filterIcon: () => ( @@ -246,16 +254,20 @@ export class Datanodes extends React.Component, IDatanode return a.storageRemaining - b.storageRemaining } - if (sortBy === "storageUsed") { + if (sortBy === "storageUsedOzone") { return a.storageUsed - b.storageUsed; + } else if (sortBy === "storageUsedNonOzone") { + return getNonOzoneUsed(a.storageTotal, a.storageRemaining, a.storageUsed) - + getNonOzoneUsed(b.storageTotal, b.storageRemaining, a.storageUsed); + } else if (sortBy === "storageUsedTotal") { + return getTotalUsed(a.storageTotal, a.storageRemaining) - getTotalUsed(a.storageTotal, a.storageRemaining) + } else if (sortBy === 'storageUsedTotalUtilization') { + return getCapacityPercent(a.storageTotal - a.storageRemaining, a.storageTotal) - + getCapacityPercent(b.storageTotal - b.storageRemaining, b.storageTotal) } else if (sortBy === "storageCommitted") { return a.storageCommitted - b.storageCommitted; } else if (sortBy === "storageTotal") { return a.storageTotal- b.storageTotal; - } else if (sortBy === 'storageUtilization') { - // See totalUsed calculation in storageBar.tsx - return getCapacityPercent(a.storageTotal - a.storageRemaining, a.storageTotal) - - getCapacityPercent(b.storageTotal - b.storageRemaining, b.storageTotal) } else { return a.storageRemaining - b.storageRemaining }