From 3db1f3db14c5f6f7ab5fc739047f76c7af52be13 Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Fri, 18 Oct 2024 13:26:01 +0530 Subject: [PATCH 1/8] HDDS-11160. Improve Insights page UI --- .../src/constants/breadcrumbs.constants.tsx | 2 +- .../v2/components/breadcrumbs/breadcrumbs.tsx | 54 ++++ .../src/v2/components/navBar/navBar.tsx | 4 +- .../{duPieChart => plots}/duPieChart.tsx | 0 .../plots/insightsContainerPlot.tsx | 149 +++++++++++ .../v2/components/plots/insightsFilePlot.tsx | 244 ++++++++++++++++++ .../src/v2/components/select/multiSelect.tsx | 56 ++-- .../insights/containerMismatchTable.tsx | 189 ++++++++++++++ .../insights/deletePendingDirsTable.tsx | 0 .../insights/deletePendingKeysTable.tsx | 174 +++++++++++++ .../insights/deletedContainerKeysTable.tsx | 120 +++++++++ .../tables/insights/expandedKeyTable.tsx | 91 +++++++ .../insights/expandedPendingKeysTable.tsx | 82 ++++++ .../tables/insights/openKeysTable.tsx | 0 .../v2/constants/breadcrumbs.constants.tsx | 34 +++ .../src/v2/constants/limit.constants.tsx | 37 +++ .../src/v2/pages/buckets/buckets.tsx | 21 +- .../src/v2/pages/diskUsage/diskUsage.tsx | 2 +- .../src/v2/pages/insights/insights.tsx | 196 ++++++++++++++ .../src/v2/pages/insights/omInsights.tsx | 139 ++++++++++ .../src/v2/pages/volumes/volumes.tsx | 8 +- .../ozone-recon-web/src/v2/routes-v2.tsx | 10 + .../src/v2/types/insights.types.ts | 178 +++++++++++++ 23 files changed, 1736 insertions(+), 54 deletions(-) create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/breadcrumbs/breadcrumbs.tsx rename hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/{duPieChart => plots}/duPieChart.tsx (100%) create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsContainerPlot.tsx create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsFilePlot.tsx create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedKeyTable.tsx create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedPendingKeysTable.tsx create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/breadcrumbs.constants.tsx create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/limit.constants.tsx create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/insights.tsx create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/insights.types.ts diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/constants/breadcrumbs.constants.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/constants/breadcrumbs.constants.tsx index bc81e86dcb3c..a9f7a53eda44 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/constants/breadcrumbs.constants.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/constants/breadcrumbs.constants.tsx @@ -31,5 +31,5 @@ export const breadcrumbNameMap: IBreadcrumbNameMap = { '/Insights': 'Insights', '/DiskUsage': 'Disk Usage', '/Heatmap': 'Heatmap', - '/Om': 'Om', + '/Om': 'Om' }; diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/breadcrumbs/breadcrumbs.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/breadcrumbs/breadcrumbs.tsx new file mode 100644 index 000000000000..1c1e68479486 --- /dev/null +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/breadcrumbs/breadcrumbs.tsx @@ -0,0 +1,54 @@ +/* + * 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 { Breadcrumb } from 'antd'; +import { HomeOutlined } from '@ant-design/icons'; +import { Link, useLocation } from 'react-router-dom'; + +import { breadcrumbNameMap } from '@/v2/constants/breadcrumbs.constants'; + +const Breadcrumbs: React.FC<{}> = () => { + const location = useLocation(); + //Split and filter to remove empty strings + const pathSnippets = location.pathname.split('/').filter(i => i); + + const extraBreadcrumbItems = pathSnippets.map((_: string, index: number) => { + const url = `/${pathSnippets.slice(0, index + 1).join('/')}`; + return ( + + + {breadcrumbNameMap[url]} + + + ) + }); + + const breadcrumbItems = [( + + + + )].concat(extraBreadcrumbItems); + + return ( + + {breadcrumbItems} + + ); +} +export default Breadcrumbs; diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/navBar/navBar.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/navBar/navBar.tsx index 3da4104634c8..4e62d60caccb 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/navBar/navBar.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/navBar/navBar.tsx @@ -125,10 +125,10 @@ const NavBar: React.FC = ({ Insights - }> OM DB Insights - + ), ( diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/duPieChart/duPieChart.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/duPieChart.tsx similarity index 100% rename from hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/duPieChart/duPieChart.tsx rename to hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/duPieChart.tsx diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsContainerPlot.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsContainerPlot.tsx new file mode 100644 index 000000000000..851c355e765c --- /dev/null +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsContainerPlot.tsx @@ -0,0 +1,149 @@ +/* + * 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 filesize from 'filesize'; +import { EChartsOption } from 'echarts'; + +import EChart from '@/v2/components/eChart/eChart'; +import { ContainerCountResponse, ContainerPlotData } from '@/v2/types/insights.types'; + +type ContainerSizeDistributionProps = { + containerCountResponse: ContainerCountResponse[]; + containerSizeError: string | undefined; +} + +const size = filesize.partial({ standard: 'iec', round: 0 }); + +const ContainerSizeDistribution: React.FC = ({ + containerCountResponse, + containerSizeError +}) => { + + const [containerPlotData, setContainerPlotData] = React.useState({ + containerCountValues: [], + containerCountMap: new Map() + }); + + function updatePlotData() { + const containerCountMap: Map = containerCountResponse.reduce( + (map: Map, current) => { + const containerSize = current.containerSize; + const oldCount = map.get(containerSize) ?? 0; + map.set(containerSize, oldCount + current.count); + return map; + }, + new Map() + ); + + const containerCountValues = Array.from(containerCountMap.keys()).map(value => { + const upperbound = size(value); + const upperboundPwr = Math.log2(value); + + const lowerbound = upperboundPwr > 10 ? size(2 ** (upperboundPwr - 1)) : size(0); + return `${lowerbound} - ${upperbound}`; + }); + + setContainerPlotData({ + containerCountValues: containerCountValues, + containerCountMap: containerCountMap + }); + } + + React.useEffect(() => { + updatePlotData(); + }, []); + + const { containerCountMap, containerCountValues } = containerPlotData; + + const containerPlotOptions: EChartsOption = { + tooltip: { + trigger: 'item', + formatter: ({ data }) => { + return `Size Range: ${data.name}
Count: ${data.value}` + } + }, + legend: { + orient: 'vertical', + left: 'right' + }, + series: { + type: 'pie', + radius: '50%', + data: Array.from(containerCountMap?.values() ?? []).map((value, idx) => { + return { + value: value, + name: containerCountValues[idx] ?? '' + } + }), + }, + graphic: (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. ${containerSizeError}`, + font: '20px sans-serif' + } + } + ] + } : undefined + } + + return (<> + + ) +} + +export default ContainerSizeDistribution; \ No newline at end of file diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsFilePlot.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsFilePlot.tsx new file mode 100644 index 000000000000..680dd0e2db0d --- /dev/null +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsFilePlot.tsx @@ -0,0 +1,244 @@ +/* + * 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 filesize from 'filesize'; +import { EChartsOption } from 'echarts'; +import { ValueType } from 'react-select'; + +import EChart from '@/v2/components/eChart/eChart'; +import MultiSelect, { Option } from '@/v2/components/select/multiSelect'; +import { FileCountResponse, FilePlotData } from '@/v2/types/insights.types'; + + +//-----Types------ +type FileSizeDistributionProps = { + volumeOptions: Option[]; + volumeBucketMap: Map>; + fileCountResponse: FileCountResponse[]; + fileCountError: string | undefined; +} + +const size = filesize.partial({ standard: 'iec', round: 0 }); + +const dropdownStyles: React.CSSProperties = { + display: 'flex', + justifyContent: 'space-between' +} + +const FileSizeDistribution: React.FC = ({ + volumeOptions = [], + volumeBucketMap, + fileCountResponse, + fileCountError +}) => { + + const [bucketOptions, setBucketOptions] = React.useState([]); + const [selectedBuckets, setSelectedBuckets] = React.useState([]); + const [selectedVolumes, setSelectedVolumes] = React.useState([]); + const [isBucketSelectionEnabled, setBucketSelectionEnabled] = React.useState(false); + + const [filePlotData, setFilePlotData] = React.useState({ + fileCountValues: [], + fileCountMap: new Map() + }); + + function handleVolumeChange(selectedVolumes: ValueType) { + + // Disable bucket selection options if more than one volume is selected or no volumes present + // If there is only one volume then the bucket selection is enabled + const bucketSelectionDisabled = ((selectedVolumes as Option[])?.length > 1 + && volumeBucketMap.size !== 1); + + let bucketOptions: Option[] = []; + + // Update buckets if only one volume is selected + if (selectedVolumes?.length === 1) { + const selectedVolume = selectedVolumes[0].value; + if (volumeBucketMap.has(selectedVolume)) { + bucketOptions = Array.from( + volumeBucketMap.get(selectedVolume)! + ).map(bucket => ({ + label: bucket, + value: bucket + })); + } + } + setBucketOptions([...bucketOptions]); + setSelectedVolumes(selectedVolumes as Option[]); + setSelectedBuckets([...bucketOptions]); + setBucketSelectionEnabled(!bucketSelectionDisabled); + } + + function handleBucketChange(selectedBuckets: ValueType) { + setSelectedBuckets(selectedBuckets as Option[]); + } + + function updatePlotData() { + // Aggregate count across volumes and buckets for use in plot + let filteredData = fileCountResponse; + const selectedVolumeValues = new Set(selectedVolumes.map(option => option.value)); + const selectedBucketValues = new Set(selectedBuckets.map(option => option.value)); + if (selectedVolumes.length > 0) { + // Not all volumes are selected, need to filter based on the selected values + filteredData = filteredData.filter(data => selectedVolumeValues.has(data.volume)); + } + if (selectedBuckets.length > 0) { + // Not all buckcets are selected, filter based on the selected values + filteredData = filteredData.filter(data => selectedBucketValues.has(data.bucket)); + } + + // This is a map of 'size : count of the size' + const fileCountMap: Map = filteredData.reduce( + (map: Map, current) => { + const fileSize = current.fileSize; + const oldCount = map.get(fileSize) ?? 0; + map.set(fileSize, oldCount + current.count); + return map; + }, + new Map + ); + + // Calculate the previous power of 2 to find the lower bound of the range + // Ex: for 2048, the lower bound is 1024 + const fileCountValues = Array.from(fileCountMap.keys()).map(value => { + const upperbound = size(value); + const upperboundPwr = Math.log2(value); + // For 1024 i.e 2^10, the lower bound is 0, so we start binning after 2^10 + const lowerbound = upperboundPwr > 10 ? size(2 ** (upperboundPwr - 1)) : size(0); + return `${lowerbound} - ${upperbound}`; + }); + + setFilePlotData({ + fileCountValues: fileCountValues, + fileCountMap: fileCountMap + }); + } + + // If the response is updated or the volume-bucket data is updated, update plot + React.useEffect(() => { + updatePlotData(); + handleVolumeChange(volumeOptions); + }, [ + fileCountResponse, volumeBucketMap + ]); + + // If the selected volumes and buckets change, update plot + React.useEffect(() => { + updatePlotData(); + }, [selectedVolumes, selectedBuckets]) + + const { fileCountValues, fileCountMap } = filePlotData; + + const filePlotOptions: EChartsOption = { + xAxis: { + type: 'category', + data: fileCountValues ?? [] + }, + yAxis: { + type: 'value' + }, + tooltip: { + trigger: 'item', + formatter: ({ name, value }) => { + return `Size Range: ${name}
Count: ${value}` + } + }, + series: { + itemStyle: { + color: '#04AD78' + }, + data: Array.from(fileCountMap?.values() ?? []), + type: 'bar' + }, + graphic: (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. ${fileCountError}`, + font: '20px sans-serif' + } + } + ] + } : undefined + } + + return (<> +
+ { }} + fixedColumn='' + columnLength={volumeOptions.length} + style={{ + control: (baseStyles, state) => ({ + ...baseStyles, + minWidth: 345 + }) + }} /> + { }} + fixedColumn='' + columnLength={bucketOptions.length} + isDisabled={!isBucketSelectionEnabled} + style={{ + control: (baseStyles, state) => ({ + ...baseStyles, + minWidth: 345 + }) + }} /> +
+ + ) +} + +export default FileSizeDistribution; \ No newline at end of file diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/multiSelect.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/multiSelect.tsx index 07b3f9eafa15..3dfe19f9b456 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/multiSelect.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/multiSelect.tsx @@ -23,7 +23,8 @@ import { components, OptionProps, ValueType, - ValueContainerProps + ValueContainerProps, + StylesConfig } from 'react-select'; import { selectStyles } from "@/v2/constants/select.constants"; @@ -41,6 +42,7 @@ interface MultiSelectProps extends ReactSelectProps { placeholder: string; fixedColumn: string; columnLength: number; + style?: StylesConfig; onChange: (arg0: ValueType) => void; onTagClose: (arg0: string) => void; } @@ -72,9 +74,11 @@ const MultiSelect: React.FC = ({ selected = [], maxSelected = 5, placeholder = 'Columns', + isDisabled = false, fixedColumn, columnLength, tagRef, + style, onTagClose = () => { }, // Assign default value as a void function onChange = () => { }, // Assign default value as a void function ...props @@ -90,34 +94,40 @@ const MultiSelect: React.FC = ({ ? child : null )} - {placeholder}: {selected.length} selected + {isDisabled + ? placeholder + : `${placeholder}: ${selected.length} selected` +} ); }; + const finalStyles = {...selectStyles, ...style ?? {}} + return ( option.value === fixedColumn} - onChange={(selected: ValueType) => { - if (selected?.length === options.length) return onChange!(options); - return onChange!(selected); - }} - styles={selectStyles} /> + {...props} + isMulti={true} + closeMenuOnSelect={false} + hideSelectedOptions={false} + isClearable={false} + isSearchable={false} + controlShouldRenderValue={false} + classNamePrefix='multi-select' + options={options} + components={{ + ValueContainer, + Option + }} + placeholder={placeholder} + value={selected} + isOptionDisabled={(option) => option.value === fixedColumn} + isDisabled={isDisabled} + onChange={(selected: ValueType) => { + if (selected?.length === options.length) return onChange!(options); + return onChange!(selected); + }} + styles={finalStyles} /> ) } diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx new file mode 100644 index 000000000000..1c2d64131d80 --- /dev/null +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx @@ -0,0 +1,189 @@ +/* + * 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 { AxiosError } from 'axios'; +import { + Dropdown, + Menu, + Popover, + Table +} from 'antd'; +import { + ColumnsType, + TablePaginationConfig +} from 'antd/es/table'; +import { + MenuProps as FilterMenuProps +} from 'antd/es/menu'; +import { FilterFilled } from '@ant-design/icons'; + +import { showDataFetchError } from '@/utils/common'; +import { AxiosGetHelper } from '@/utils/axiosRequestHelper'; + +import { + Container, + MismatchContainersResponse, + Pipelines +} from '@/v2/types/insights.types'; +import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants'; +import SingleSelect, { Option } from '@/v2/components/select/singleSelect'; +import { ValueType } from 'react-select'; + + +//-----Types----- +type ContainerMismatchTableProps = { + paginationConfig: TablePaginationConfig; + limit: Option; + handleLimitChange: (arg0: ValueType) => void; + expandedRowRender: (arg0: any) => JSX.Element; + onRowExpand: (arg0: boolean, arg1: any) => void; +} + + +const ContainerMismatchTable: React.FC = ({ + paginationConfig, + limit, + onRowExpand, + expandedRowRender, + handleLimitChange +}) => { + + const [loading, setLoading] = React.useState(false); + const [data, setData] = React.useState(); + + const cancelSignal = React.useRef(); + + const handleExistAtChange: FilterMenuProps['onClick'] = ({ key }) => { + if (key === 'OM') { + fetchMismatchContainers('SCM'); + } else { + fetchMismatchContainers('OM'); + } + } + + const COLUMNS: ColumnsType = [ + { + title: 'Container ID', + dataIndex: 'containerId', + key: 'containerId', + width: '20%' + + }, + { + title: 'Count Of Keys', + dataIndex: 'numberOfKeys', + key: 'numberOfKeys', + sorter: (a: Container, b: Container) => a.numberOfKeys - b.numberOfKeys + }, + { + title: 'Pipelines', + dataIndex: 'pipelines', + key: 'pipelines', + render: (pipelines: Pipelines[]) => { + const renderPipelineIds = (pipelineIds: Pipelines[]) => { + return pipelineIds?.map(pipeline => ( +
+ {pipeline.id.id} +
+ )); + } + return ( + + {pipelines.length} pipelines + + ) + } + }, + { + title: <> + + OM + SCM + + }> + + + , + dataIndex: 'existsAt' + } + ]; + + function fetchMismatchContainers(missingIn: string) { + setLoading(true); + const { request, controller } = AxiosGetHelper( + `/api/v1/containers/mismatch?limit=${limit.value}&missingIn=${missingIn}`, + cancelSignal.current + ); + + cancelSignal.current = controller; + request.then(response => { + const mismatchedContainers: MismatchContainersResponse = response?.data; + setData(mismatchedContainers?.containerDiscrepancyInfo ?? []); + setLoading(false); + }).catch(error => { + setLoading(false); + showDataFetchError((error as AxiosError).toString()); + }) + } + + React.useEffect(() => { + //Fetch containers missing in OM by default + fetchMismatchContainers('OM'); + + return (() => { + cancelSignal.current && cancelSignal.current.abort(); + }) + }, [limit]); + + return ( + <> +
+
+ +
+
+ + + ) +} + +export default ContainerMismatchTable; \ No newline at end of file diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx new file mode 100644 index 000000000000..a8f14506df0a --- /dev/null +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx @@ -0,0 +1,174 @@ +/* +* 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 { AxiosError } from 'axios'; +import Table, { + ColumnsType, + TablePaginationConfig +} from 'antd/es/table'; + +import { AxiosGetHelper } from '@/utils/axiosRequestHelper'; +import { byteToSize, showDataFetchError } from '@/utils/common'; +import { + DeletePendingKey, + DeletePendingKeysResponse +} from '@/v2/types/insights.types'; +import SingleSelect, { Option } from '@/v2/components/select/singleSelect'; +import { ValueType } from 'react-select'; +import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants'; +import ExpandedPendingKeysTable from '@/v2/components/tables/insights/expandedPendingKeysTable'; + +//-----Types------ +type DeletePendingKeysTableProps = { + paginationConfig: TablePaginationConfig + limit: Option; + handleLimitChange: (arg0: ValueType) => void; +} + +type DeletePendingKeysColumns = { + fileName: string; + keyName: string; + dataSize: number; + keyCount: number; +} + +type ExpandedDeletePendingKeys = { + omKeyInfoList: DeletePendingKey[] +} + +//------Constants------ +const COLUMNS: ColumnsType = [ + { + title: 'Key Name', + dataIndex: 'fileName', + key: 'fileName' + }, + { + title: 'Path', + dataIndex: 'keyName', + key: 'keyName', + }, + { + title: 'Total Data Size', + dataIndex: 'dataSize', + key: 'dataSize', + render: (dataSize: number) => byteToSize(dataSize, 1) + }, + { + title: 'Total Key Count', + dataIndex: 'keyCount', + key: 'keyCount', + } +]; + +let expandedDeletePendingKeys: ExpandedDeletePendingKeys[] = []; + +//-----Components------ +const DeletePendingKeysTable: React.FC = ({ + paginationConfig, + limit, + handleLimitChange +}) => { + const [loading, setLoading] = React.useState(false); + const [data, setData] = React.useState(); + + const cancelSignal = React.useRef(); + + function expandedRowRender(record: DeletePendingKeysColumns) { + console.log(expandedDeletePendingKeys); + const filteredData = expandedDeletePendingKeys?.flatMap((info) => ( + info.omKeyInfoList?.filter((key) => key.keyName === record.keyName) + )); + return ( + + ) + } + + function fetchDeletePendingKeys() { + setLoading(true); + const { request, controller } = AxiosGetHelper( + `/api/v1/keys/deletePending?limit=${limit}`, + cancelSignal.current + ); + cancelSignal.current = controller; + + request.then(response => { + const deletePendingKeys: DeletePendingKeysResponse = response?.data; + let deletedKeyData = []; + // Sum up the data size and organize related key information + deletedKeyData = deletePendingKeys?.deletedKeyInfo.flatMap((keyInfo) => { + expandedDeletePendingKeys.push(keyInfo); + let count = 0; + let item: DeletePendingKey = keyInfo.omKeyInfoList?.reduce((obj, curr) => { + count += 1; + return { ...curr, dataSize: obj.dataSize + curr.dataSize }; + }, { ...keyInfo.omKeyInfoList[0], dataSize: 0 }); + + return { + dataSize: item.dataSize, + fileName: item.fileName, + keyName: item.keyName, + path: item.path, + keyCount: count + } + }); + setData(deletedKeyData); + setLoading(false); + }).catch(error => { + setLoading(false); + showDataFetchError((error as AxiosError).toString()); + }) + } + + React.useEffect(() => { + fetchDeletePendingKeys(); + + return (() => { + cancelSignal.current && cancelSignal.current.abort(); + }) + }, [limit]); + + return ( + <> +
+
+ +
+
+
+ + ) +} + +export default DeletePendingKeysTable; \ No newline at end of file diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx new file mode 100644 index 000000000000..5d3935614188 --- /dev/null +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx @@ -0,0 +1,120 @@ +/* + * 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 { AxiosError } from 'axios'; +import { Container, DeletedContainerKeysResponse, Pipelines } from '@/v2/types/insights.types'; +import Table, { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +import { AxiosGetHelper } from '@/utils/axiosRequestHelper'; +import { showDataFetchError } from '@/utils/common'; + +//------Types------- +type DeletedContainerKeysTableProps = { + paginationConfig: TablePaginationConfig; + limit: string; + onRowExpand: () => void; + expandedRowRender: (arg0: any) => JSX.Element; +} + +//------Constants------ +const COLUMNS: ColumnsType = [ + { + title: 'Container ID', + dataIndex: 'containerId', + key: 'containerId', + width: '20%' + }, + { + title: 'Count Of Keys', + dataIndex: 'numberOfKeys', + key: 'numberOfKeys', + sorter: (a: Container, b: Container) => a.numberOfKeys - b.numberOfKeys + }, + { + title: 'Pipelines', + dataIndex: 'pipelines', + key: 'pipelines', + render: (pipelines: Pipelines[]) => ( +
+ {pipelines && pipelines.map((pipeline: any) => ( +
+ {pipeline.id.id} +
+ ))} +
+ ) + } +]; + +//-----Components------ +const DeletedContainerKeysTable: React.FC = ({ + expandedRowRender, + onRowExpand, + paginationConfig, + limit = '1000' +}) => { + + const [loading, setLoading] = React.useState(false); + const [data, setData] = React.useState(); + + const cancelSignal = React.useRef(); + + function fetchDeletedKeys() { + const { request, controller } = AxiosGetHelper( + `/api/v1/containers/mismatch/deleted?limit=${limit}`, + cancelSignal.current + ) + cancelSignal.current = controller; + + request.then(response => { + setLoading(true); + const deletedContainerKeys: DeletedContainerKeysResponse = response?.data; + setData(deletedContainerKeys?.containers ?? []); + setLoading(false); + }).catch(error => { + setLoading(false); + showDataFetchError((error as AxiosError).toString()); + }); + } + + React.useEffect(() => { + fetchDeletedKeys(); + + return(() => { + cancelSignal.current && cancelSignal.current.abort(); + }) + }, []); + + + return ( +
+ ) +} + +export default DeletedContainerKeysTable; \ No newline at end of file diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedKeyTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedKeyTable.tsx new file mode 100644 index 000000000000..a8ab7334ab4c --- /dev/null +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedKeyTable.tsx @@ -0,0 +1,91 @@ +/* +* 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 moment from 'moment'; +import filesize from 'filesize'; +import Table, { + ColumnsType, + TablePaginationConfig +} from 'antd/es/table'; + +import { MismatchKeys } from '@/v2/types/insights.types'; + + +const size = filesize.partial({ standard: 'iec' }); + +type ExpandedKeyTableProps = { + loading: boolean; + data: MismatchKeys[]; + paginationConfig: TablePaginationConfig; +} + +//-----Constants----- +const COLUMNS: ColumnsType = [ + { + title: 'Volume', + dataIndex: 'Volume', + key: 'Volume' + }, + { + title: 'Bucket', + dataIndex: 'Bucket', + key: 'Bucket' + }, + { + title: 'Key', + dataIndex: 'Key', + key: 'Key' + }, + { + title: 'Size', + dataIndex: 'DataSize', + key: 'DataSize', + render: (dataSize: number) =>
{size(dataSize)}
+ }, + { + title: 'Date Created', + dataIndex: 'CreationTime', + key: 'CreationTime', + render: (date: string) => moment(date).format('lll') + }, + { + title: 'Date Modified', + dataIndex: 'ModificationTime', + key: 'ModificationTime', + render: (date: string) => moment(date).format('lll') + } +]; + +const ExpandedKeyTable: React.FC = ({ + loading, + data, + paginationConfig +}) => { + return ( +
+ ) +} + +export default ExpandedKeyTable; \ No newline at end of file diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedPendingKeysTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedPendingKeysTable.tsx new file mode 100644 index 000000000000..3643c02c9916 --- /dev/null +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedPendingKeysTable.tsx @@ -0,0 +1,82 @@ +/* +* 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 Table, { + ColumnsType, + TablePaginationConfig +} from 'antd/es/table'; + +import { byteToSize } from '@/utils/common'; +import { getFormattedTime } from '@/v2/utils/momentUtils'; + +import { DeletePendingKey } from '@/v2/types/insights.types'; + +//--------Types-------- +type ExpandedPendingKeysTableProps = { + data: DeletePendingKey[]; + paginationConfig: TablePaginationConfig; +} + +//--------Constants-------- +const COLUMNS: ColumnsType = [{ + title: 'Data Size', + dataIndex: 'dataSize', + key: 'dataSize', + render: (dataSize: any) => dataSize = dataSize > 0 ? byteToSize(dataSize, 1) : dataSize +}, +{ + title: 'Replicated Data Size', + dataIndex: 'replicatedSize', + key: 'replicatedSize', + render: (replicatedSize: any) => replicatedSize = replicatedSize > 0 ? byteToSize(replicatedSize, 1) : replicatedSize +}, +{ + title: 'Creation Time', + dataIndex: 'creationTime', + key: 'creationTime', + render: (creationTime: number) => { + return getFormattedTime(creationTime, 'll LTS'); + } +}, +{ + title: 'Modification Time', + dataIndex: 'modificationTime', + key: 'modificationTime', + render: (modificationTime: number) => { + return getFormattedTime(modificationTime, 'll LTS'); + } +}] + + +//--------Component-------- +const ExpandedPendingKeysTable: React.FC = ({ + data, + paginationConfig +}) => { + return ( +
+ ) +} + +export default ExpandedPendingKeysTable; \ No newline at end of file diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/breadcrumbs.constants.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/breadcrumbs.constants.tsx new file mode 100644 index 000000000000..8f5be189c2c1 --- /dev/null +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/breadcrumbs.constants.tsx @@ -0,0 +1,34 @@ +/* + * 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. + */ + +type BreadcrumbNameMap = { + [path: string]: string; +} + +export const breadcrumbNameMap: BreadcrumbNameMap = { + '/Overview': 'Overview', + '/Volumes': 'Volumes', + '/Buckets': 'Buckets', + '/Datanodes': 'Datanodes', + '/Pipelines': 'Pipelines', + '/Containers': 'Containers', + '/Insights': 'Insights', + '/DiskUsage': 'Disk Usage', + '/Heatmap': 'Heatmap', + '/OmInsights': 'OM DB Insights' +}; diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/limit.constants.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/limit.constants.tsx new file mode 100644 index 000000000000..b76c51c89603 --- /dev/null +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/limit.constants.tsx @@ -0,0 +1,37 @@ +/* + * 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 { Option } from '@/v2/components/select/singleSelect'; + +export const LIMIT_OPTIONS: Option[] = [ + { + label: '1000', + value: '1000' + }, + { + label: '5000', + value: '5000' + }, + { + label: '10000', + value: '10000' + }, + { + label: '20000', + value: '20000' + } +]; diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx index 1590f36a4aaa..de79f78edb72 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx @@ -31,6 +31,7 @@ import BucketsTable, { COLUMNS } from '@/v2/components/tables/bucketsTable'; import { AutoReloadHelper } from '@/utils/autoReloadHelper'; import { AxiosGetHelper, cancelRequests } from "@/utils/axiosRequestHelper"; import { showDataFetchError } from '@/utils/common'; +import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants'; import { useDebounce } from '@/v2/hooks/debounce.hook'; import { @@ -41,26 +42,6 @@ import { import './buckets.less'; - -const LIMIT_OPTIONS: Option[] = [ - { - label: '1000', - value: '1000' - }, - { - label: '5000', - value: '5000' - }, - { - label: '10000', - value: '10000' - }, - { - label: '20000', - value: '20000' - } -] - const SearchableColumnOpts = [{ label: 'Bucket', value: 'name' diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/diskUsage/diskUsage.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/diskUsage/diskUsage.tsx index 1e92780619b1..92bc0a0d3523 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/diskUsage/diskUsage.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/diskUsage/diskUsage.tsx @@ -27,7 +27,7 @@ import { import { ValueType } from 'react-select'; import DUMetadata from '@/v2/components/duMetadata/duMetadata'; -import DUPieChart from '@/v2/components/duPieChart/duPieChart'; +import DUPieChart from '@/v2/components/plots/duPieChart'; import SingleSelect, { Option } from '@/v2/components/select/singleSelect'; import DUBreadcrumbNav from '@/v2/components/duBreadcrumbNav/duBreadcrumbNav'; import { showDataFetchError } from '@/utils/common'; diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/insights.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/insights.tsx new file mode 100644 index 000000000000..f2a2c3e3f7d1 --- /dev/null +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/insights.tsx @@ -0,0 +1,196 @@ +/* + * 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, { useState } from 'react'; +import axios, { + CanceledError, + AxiosError +} from 'axios'; +import { Row, Col, Card, Result } from 'antd'; + +import { showDataFetchError } from '@/utils/common'; +import { PromiseAllSettledGetHelper } from '@/utils/axiosRequestHelper'; + +import { Option } from '@/v2/components/select/multiSelect'; +import FileSizeDistribution from '@/v2/components/plots/insightsFilePlot'; +import ContainerSizeDistribution from '@/v2/components/plots/insightsContainerPlot'; + +import { + FileCountResponse, + InsightsState, + PlotResponse, +} from '@/v2/types/insights.types'; + +const Insights: React.FC<{}> = () => { + + const [loading, setLoading] = useState(false); + const [state, setState] = useState({ + volumeBucketMap: new Map>(), + volumeOptions: [], + fileCountError: undefined, + containerSizeError: undefined + }); + const [plotResponse, setPlotResponse] = useState({ + fileCountResponse: [{ + volume: '', + bucket: '', + fileSize: 0, + count: 0 + }], + containerCountResponse: [{ + containerSize: 0, + count: 0 + }] + }); + + const cancelInsightSignal = React.useRef(); + + function loadData() { + setLoading(true); + const { requests, controller } = PromiseAllSettledGetHelper([ + '/api/v1/utilization/fileCount', + '/api/v1/utilization/containerCount' + ], cancelInsightSignal.current); + + cancelInsightSignal.current = controller; + requests.then(axios.spread(( + fileCountResponse: Awaited>, + containerCountResponse: Awaited> + ) => { + let fileAPIError; + let containerAPIError; + let responseError = [ + fileCountResponse, + containerCountResponse + ].filter((resp) => resp.status === 'rejected'); + + if (responseError.length !== 0) { + responseError.forEach((err) => { + if (err.reason.toString().includes('CancelledError')) { + throw new CanceledError('canceled', 'ERR_CANCELED'); + } else { + if (err.reason.config.url.includes("fileCount")) { + fileAPIError = err.reason.toString(); + } else { + containerAPIError = err.reason.toString(); + } + } + }); + } + + // Construct volume -> bucket[] map for populating filters + // Ex: vol1 -> [bucket1, bucket2], vol2 -> [bucket1] + const volumeBucketMap: Map> = fileCountResponse.value?.data?.reduce( + (map: Map>, current: FileCountResponse) => { + const volume = current.volume; + const bucket = current.bucket; + if (map.has(volume)) { + const buckets = Array.from(map.get(volume)!); + map.set(volume, new Set([...buckets, bucket])); + } else { + map.set(volume, new Set().add(bucket)); + } + return map; + }, + new Map>() + ); + const volumeOptions: Option[] = Array.from(volumeBucketMap.keys()).map(k => ({ + label: k, + value: k + })); + + setState({ + ...state, + volumeBucketMap: volumeBucketMap, + volumeOptions: volumeOptions, + fileCountError: fileAPIError, + containerSizeError: containerAPIError + }); + setPlotResponse({ + fileCountResponse: fileCountResponse.value?.data ?? [{ + volume: '', + bucket: '', + fileSize: 0, + count: 0 + }], + containerCountResponse: containerCountResponse.value?.data ?? [{ + containerSize: 0, + count: 0 + }] + }); + setLoading(false); + })).catch(error => { + setLoading(false); + showDataFetchError((error as AxiosError).toString()); + }) + } + + React.useEffect(() => { + loadData(); + + return (() => { + cancelInsightSignal.current && cancelInsightSignal.current.abort(); + }) + }, []); + + return ( + <> +
+ Insights +
+
+ { + loading + ? + : <> + +
+ + {plotResponse.fileCountResponse?.length > 0 + ? + : } + + + + + + {plotResponse.containerCountResponse?.length > 0 + ? + : } + + + + + } + + + ) + +} + +export default Insights; \ No newline at end of file diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx new file mode 100644 index 000000000000..cc4c8359df6b --- /dev/null +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx @@ -0,0 +1,139 @@ +/* + * 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 { AxiosGetHelper } from '@/utils/axiosRequestHelper'; +import { showDataFetchError } from '@/utils/common'; +import { Option } from '@/v2/components/select/singleSelect'; +import ContainerMismatchTable from '@/v2/components/tables/insights/containerMismatchTable'; +import DeletePendingKeysTable from '@/v2/components/tables/insights/deletePendingKeysTable'; +import ExpandedKeyTable from '@/v2/components/tables/insights/expandedKeyTable'; +import { Container, ExpandedRow, ExpandedRowState, MismatchContainersResponse, MismatchKeys, MismatchKeysResponse } from '@/v2/types/insights.types'; +import { InfoCircleOutlined } from '@ant-design/icons'; +import { Tabs, Tooltip } from 'antd'; +import { TablePaginationConfig } from 'antd/es/table'; +import { AxiosError } from 'axios'; +import React, { useState } from 'react'; +import { ValueType } from 'react-select'; + +const OMDBInsights: React.FC<{}> = () => { + + const [loading, setLoading] = React.useState(false); + const [expandedRowData, setExpandedRowData] = React.useState({}); + const [selectedLimit, setSelectedLimit] = React.useState
+ ) +} + +export default DeletePendingDirTable; \ No newline at end of file diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx index a8f14506df0a..655b397f81a1 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx @@ -22,17 +22,18 @@ import Table, { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +import { ValueType } from 'react-select'; +import SingleSelect, { Option } from '@/v2/components/select/singleSelect'; +import ExpandedPendingKeysTable from '@/v2/components/tables/insights/expandedPendingKeysTable'; import { AxiosGetHelper } from '@/utils/axiosRequestHelper'; import { byteToSize, showDataFetchError } from '@/utils/common'; +import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants'; + import { DeletePendingKey, DeletePendingKeysResponse } from '@/v2/types/insights.types'; -import SingleSelect, { Option } from '@/v2/components/select/singleSelect'; -import { ValueType } from 'react-select'; -import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants'; -import ExpandedPendingKeysTable from '@/v2/components/tables/insights/expandedPendingKeysTable'; //-----Types------ type DeletePendingKeysTableProps = { @@ -105,7 +106,7 @@ const DeletePendingKeysTable: React.FC = ({ function fetchDeletePendingKeys() { setLoading(true); const { request, controller } = AxiosGetHelper( - `/api/v1/keys/deletePending?limit=${limit}`, + `/api/v1/keys/deletePending?limit=${limit.value}`, cancelSignal.current ); cancelSignal.current = controller; @@ -144,7 +145,7 @@ const DeletePendingKeysTable: React.FC = ({ return (() => { cancelSignal.current && cancelSignal.current.abort(); }) - }, [limit]); + }, [limit.value]); return ( <> @@ -166,7 +167,9 @@ const DeletePendingKeysTable: React.FC = ({ columns={COLUMNS} loading={loading} pagination={paginationConfig} - rowKey='keyName' /> + rowKey='keyName' + locale={{ filterTitle: '' }} + scroll={{ x: 'max-content' }} /> ) } diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx index 5d3935614188..6304333c3604 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx @@ -18,16 +18,29 @@ import React from 'react'; import { AxiosError } from 'axios'; -import { Container, DeletedContainerKeysResponse, Pipelines } from '@/v2/types/insights.types'; -import Table, { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +import Table, { + ColumnsType, + TablePaginationConfig +} from 'antd/es/table'; +import { ValueType } from 'react-select'; + +import SingleSelect, { Option } from '@/v2/components/select/singleSelect'; import { AxiosGetHelper } from '@/utils/axiosRequestHelper'; import { showDataFetchError } from '@/utils/common'; +import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants'; + +import { + Container, + DeletedContainerKeysResponse, + Pipelines +} from '@/v2/types/insights.types'; //------Types------- type DeletedContainerKeysTableProps = { paginationConfig: TablePaginationConfig; - limit: string; - onRowExpand: () => void; + limit: Option; + handleLimitChange: (arg0: ValueType) => void; + onRowExpand: (arg0: boolean, arg1: any) => void; expandedRowRender: (arg0: any) => JSX.Element; } @@ -63,10 +76,11 @@ const COLUMNS: ColumnsType = [ //-----Components------ const DeletedContainerKeysTable: React.FC = ({ - expandedRowRender, - onRowExpand, + limit, paginationConfig, - limit = '1000' + handleLimitChange, + onRowExpand, + expandedRowRender }) => { const [loading, setLoading] = React.useState(false); @@ -76,7 +90,7 @@ const DeletedContainerKeysTable: React.FC = ({ function fetchDeletedKeys() { const { request, controller } = AxiosGetHelper( - `/api/v1/containers/mismatch/deleted?limit=${limit}`, + `/api/v1/containers/mismatch/deleted?limit=${limit.value}`, cancelSignal.current ) cancelSignal.current = controller; @@ -95,25 +109,37 @@ const DeletedContainerKeysTable: React.FC = ({ React.useEffect(() => { fetchDeletedKeys(); - return(() => { + return (() => { cancelSignal.current && cancelSignal.current.abort(); }) - }, []); + }, [limit.value]); return ( -
+ <> +
+
+ +
+
+
+ ) } diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx index e69de29bb2d1..31d31e2a4052 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx @@ -0,0 +1,195 @@ +/* +* 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 { AxiosError } from 'axios'; +import { + Dropdown, + Menu, + Table +} from 'antd'; +import { + ColumnsType, + TablePaginationConfig +} from 'antd/es/table'; +import { MenuProps } from 'antd/es/menu'; +import { FilterFilled } from '@ant-design/icons'; +import { ValueType } from 'react-select'; + +import SingleSelect, { Option } from '@/v2/components/select/singleSelect'; +import { AxiosGetHelper } from '@/utils/axiosRequestHelper'; +import { byteToSize, showDataFetchError } from '@/utils/common'; +import { getFormattedTime } from '@/v2/utils/momentUtils'; +import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants'; + +import { OpenKeys, OpenKeysResponse } from '@/v2/types/insights.types'; + + +//--------Types-------- +type OpenKeysTableProps = { + limit: Option; + paginationConfig: TablePaginationConfig; + handleLimitChange: (arg0: ValueType) => void; +} + +const OpenKeysTable: React.FC = ({ + limit, + paginationConfig, + handleLimitChange +}) => { + const [loading, setLoading] = React.useState(false); + const [data, setData] = React.useState(); + + const cancelSignal = React.useRef(); + + function fetchOpenKeys(isFso: boolean) { + setLoading(true); + + const { request, controller } = AxiosGetHelper( + `/api/v1/keys/open?includeFso=${isFso}&includeNonFso=${!isFso}&limit=${limit.value}`, + cancelSignal.current + ); + cancelSignal.current = controller; + + request.then(response => { + const openKeys: OpenKeysResponse = response?.data ?? { 'fso': [] }; + let allOpenKeys: OpenKeys[]; + if (isFso) { + allOpenKeys = openKeys['fso']?.map((key: OpenKeys) => ({ + ...key, + type: 'FSO' + })) ?? []; + } else { + allOpenKeys = openKeys['nonFSO']?.map((key: OpenKeys) => ({ + ...key, + type: 'Non FSO' + })) ?? []; + } + + setData(allOpenKeys); + setLoading(false); + }).catch(error => { + setLoading(false); + showDataFetchError((error as AxiosError).toString()); + }); + } + + const handleKeyTypeChange: MenuProps['onClick'] = (e) => { + if (e.key === 'fso') { + fetchOpenKeys(true); + } else { + fetchOpenKeys(false); + } + } + + const COLUMNS: ColumnsType = [{ + title: 'Key Name', + dataIndex: 'path', + key: 'path' + }, + { + title: 'Size', + dataIndex: 'size', + key: 'size', + render: (size: any) => size = byteToSize(size, 1) + }, + { + title: 'Path', + dataIndex: 'key', + key: 'key', + width: '270px' + }, + { + title: 'In state since', + dataIndex: 'inStateSince', + key: 'inStateSince', + render: (inStateSince: number) => { + return getFormattedTime(inStateSince, 'll LTS'); + } + }, + { + title: 'Replication Factor', + dataIndex: 'replicationInfo', + key: 'replicationfactor', + render: (replicationInfo: any) => ( +
+ {Object.values(replicationInfo)[0]} +
+ ) + }, + { + title: 'Replication Type', + dataIndex: 'replicationInfo', + key: 'replicationtype', + render: (replicationInfo: any) => ( +
+ { +
+ {Object.values(replicationInfo)[2]} +
+ } +
+ ) + }, { + title: <> + + FSO + Non-FSO + + }> + + + , + dataIndex: 'type', + key: 'type', + render: (type: string) =>
{type}
+ }]; + + React.useEffect(() => { + // Fetch FSO open keys by default + fetchOpenKeys(true); + + return (() => cancelSignal.current && cancelSignal.current.abort()); + }, [limit.value]); + + return ( + <> +
+
+ +
+
+
+ + ); +} + +export default OpenKeysTable; \ No newline at end of file diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx index cc4c8359df6b..726407317215 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx @@ -20,8 +20,11 @@ import { AxiosGetHelper } from '@/utils/axiosRequestHelper'; import { showDataFetchError } from '@/utils/common'; import { Option } from '@/v2/components/select/singleSelect'; import ContainerMismatchTable from '@/v2/components/tables/insights/containerMismatchTable'; +import DeletedContainerKeysTable from '@/v2/components/tables/insights/deletedContainerKeysTable'; +import DeletePendingDirTable from '@/v2/components/tables/insights/deletePendingDirsTable'; import DeletePendingKeysTable from '@/v2/components/tables/insights/deletePendingKeysTable'; import ExpandedKeyTable from '@/v2/components/tables/insights/expandedKeyTable'; +import OpenKeysTable from '@/v2/components/tables/insights/openKeysTable'; import { Container, ExpandedRow, ExpandedRowState, MismatchContainersResponse, MismatchKeys, MismatchKeysResponse } from '@/v2/types/insights.types'; import { InfoCircleOutlined } from '@ant-design/icons'; import { Tabs, Tooltip } from 'antd'; @@ -111,16 +114,21 @@ const OMDBInsights: React.FC<{}> = () => { paginationConfig={paginationConfig} expandedRowRender={expandedRowRender} onRowExpand={onRowExpandClick} + handleLimitChange={handleLimitChange} /> + + + - Keys Pending for Deletion - + }> @@ -129,7 +137,29 @@ const OMDBInsights: React.FC<{}> = () => { paginationConfig={paginationConfig} handleLimitChange={handleLimitChange} /> - + + Deleted Container Keys + + + + + }> + + + + + diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/insights.types.ts b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/insights.types.ts index 475cc8b1988d..d608a2bc8d12 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/insights.types.ts +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/insights.types.ts @@ -127,7 +127,7 @@ export type OpenKeysResponse = { replicatedDataSize: number; unreplicatedDataSize: number; fso?: OpenKeys[]; - nonFso?: OpenKeys[]; + nonFSO?: OpenKeys[]; } //Keys pending deletion @@ -167,6 +167,27 @@ export type DeletePendingKeysResponse = { }[]; } +//Directories Pending for Deletion +export type DeletedDirInfo = { + key: string; + path: string; + inStateSince: number; + size: number; + replicatedSize: number; + replicationInfo: ReplicationConfig; + creationTime: number; + modificationTime: number; + isKey: boolean; +} + +export type DeletedDirReponse = { + lastKey: string; + replicatedDataSize: number; + unreplicatedDataSize: number; + deletedDirInfo: DeletedDirInfo[]; + status: string; +} + export type ExpandedRow = { [key: number]: ExpandedRowState; } From 71fd58f1f938a795444b8210d2c11e37f6639b83 Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Fri, 18 Oct 2024 18:09:22 +0530 Subject: [PATCH 3/8] Add search filter --- .../insights/containerMismatchTable.tsx | 22 ++++++++++++++++--- .../insights/deletePendingDirsTable.tsx | 22 ++++++++++++++++++- .../insights/deletePendingKeysTable.tsx | 19 +++++++++++++++- .../insights/deletedContainerKeysTable.tsx | 19 +++++++++++++++- .../tables/insights/expandedKeyTable.tsx | 2 ++ .../insights/expandedPendingKeysTable.tsx | 1 - .../tables/insights/openKeysTable.tsx | 20 ++++++++++++++++- 7 files changed, 97 insertions(+), 8 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx index 14fb15c34660..b275a40fa0d9 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx @@ -34,9 +34,11 @@ import { import { FilterFilled } from '@ant-design/icons'; import { ValueType } from 'react-select'; +import Search from '@/v2/components/search/search'; import SingleSelect, { Option } from '@/v2/components/select/singleSelect'; import { showDataFetchError } from '@/utils/common'; import { AxiosGetHelper } from '@/utils/axiosRequestHelper'; +import { useDebounce } from '@/v2/hooks/debounce.hook'; import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants'; import { @@ -46,7 +48,6 @@ import { } from '@/v2/types/insights.types'; - //-----Types----- type ContainerMismatchTableProps = { paginationConfig: TablePaginationConfig; @@ -56,7 +57,7 @@ type ContainerMismatchTableProps = { onRowExpand: (arg0: boolean, arg1: any) => void; } - +//-----Components------ const ContainerMismatchTable: React.FC = ({ paginationConfig, limit, @@ -67,8 +68,10 @@ const ContainerMismatchTable: React.FC = ({ const [loading, setLoading] = React.useState(false); const [data, setData] = React.useState(); + const [searchTerm, setSearchTerm] = React.useState(''); const cancelSignal = React.useRef(); + const debouncedSearch = useDebounce(searchTerm, 300); const handleExistAtChange: FilterMenuProps['onClick'] = ({ key }) => { if (key === 'OM') { @@ -78,6 +81,12 @@ const ContainerMismatchTable: React.FC = ({ } } + function filterData(data: Container[] | undefined) { + return data?.filter( + (data: Container) => data.containerId.toString().includes(debouncedSearch) + ); + } + const COLUMNS: ColumnsType = [ { title: 'Container ID', @@ -170,6 +179,13 @@ const ContainerMismatchTable: React.FC = ({ placeholder='Limit' onChange={handleLimitChange} /> + ) => setSearchTerm(e.target.value) + } + onChange={() => {}}/>
= ({ expandedRowRender: expandedRowRender, onExpand: onRowExpand }} - dataSource={data} + dataSource={filterData(data)} columns={COLUMNS} loading={loading} pagination={paginationConfig} diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx index 5085bf85e99e..f0c6fc8161e4 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx @@ -24,20 +24,24 @@ import Table, { } from 'antd/es/table'; import { ValueType } from 'react-select'; +import Search from '@/v2/components/search/search'; import SingleSelect, { Option } from '@/v2/components/select/singleSelect'; import { AxiosGetHelper } from '@/utils/axiosRequestHelper'; import { byteToSize, showDataFetchError } from '@/utils/common'; import { getFormattedTime } from '@/v2/utils/momentUtils'; +import { useDebounce } from '@/v2/hooks/debounce.hook'; import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants'; import { DeletedDirInfo } from '@/v2/types/insights.types'; +//-----Types------ type DeletePendingDirTableProps = { paginationConfig: TablePaginationConfig limit: Option; handleLimitChange: (arg0: ValueType) => void; } +//-----Constants------ const COLUMNS: ColumnsType = [{ title: 'Directory Name', dataIndex: 'key', @@ -63,6 +67,7 @@ const COLUMNS: ColumnsType = [{ render: (dataSize: number) => byteToSize(dataSize, 1) }]; +//-----Components------ const DeletePendingDirTable: React.FC = ({ limit, paginationConfig, @@ -71,8 +76,16 @@ const DeletePendingDirTable: React.FC = ({ const [loading, setLoading] = React.useState(false); const [data, setData] = React.useState(); + const [searchTerm, setSearchTerm] = React.useState(''); const cancelSignal = React.useRef(); + const debouncedSearch = useDebounce(searchTerm, 300); + + function filterData(data: DeletedDirInfo[] | undefined) { + return data?.filter( + (data: DeletedDirInfo) => data.key.includes(debouncedSearch) + ); + } function loadData() { setLoading(true); @@ -107,10 +120,17 @@ const DeletePendingDirTable: React.FC = ({ placeholder='Limit' onChange={handleLimitChange} /> + ) => setSearchTerm(e.target.value) + } + onChange={() => { }} />
= ({ }) => { const [loading, setLoading] = React.useState(false); const [data, setData] = React.useState(); + const [searchTerm, setSearchTerm] = React.useState(''); const cancelSignal = React.useRef(); + const debouncedSearch = useDebounce(searchTerm, 300); + + function filterData(data: DeletePendingKeysColumns[] | undefined) { + return data?.filter( + (data: DeletePendingKeysColumns) => data.keyName.includes(debouncedSearch) + ); + } function expandedRowRender(record: DeletePendingKeysColumns) { console.log(expandedDeletePendingKeys); @@ -157,13 +167,20 @@ const DeletePendingKeysTable: React.FC = ({ placeholder='Limit' onChange={handleLimitChange} /> + ) => setSearchTerm(e.target.value) + } + onChange={() => { }} />
= ({ const [loading, setLoading] = React.useState(false); const [data, setData] = React.useState(); + const [searchTerm, setSearchTerm] = React.useState(''); const cancelSignal = React.useRef(); + const debouncedSearch = useDebounce(searchTerm, 300); + + function filterData(data: Container[] | undefined) { + return data?.filter( + (data: Container) => data.containerId.toString().includes(debouncedSearch) + ); + } function fetchDeletedKeys() { const { request, controller } = AxiosGetHelper( @@ -125,6 +135,13 @@ const DeletedContainerKeysTable: React.FC = ({ placeholder='Limit' onChange={handleLimitChange} /> + ) => setSearchTerm(e.target.value) + } + onChange={() => { }} />
= ({ expandedRowRender: expandedRowRender, onExpand: onRowExpand }} - dataSource={data} + dataSource={filterData(data)} columns={COLUMNS} loading={loading} pagination={paginationConfig} diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedKeyTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedKeyTable.tsx index a8ab7334ab4c..8b54937e4732 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedKeyTable.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedKeyTable.tsx @@ -29,6 +29,7 @@ import { MismatchKeys } from '@/v2/types/insights.types'; const size = filesize.partial({ standard: 'iec' }); +//-----Types------ type ExpandedKeyTableProps = { loading: boolean; data: MismatchKeys[]; @@ -72,6 +73,7 @@ const COLUMNS: ColumnsType = [ } ]; +//-----Components------ const ExpandedKeyTable: React.FC = ({ loading, data, diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedPendingKeysTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedPendingKeysTable.tsx index 3643c02c9916..accb390303be 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedPendingKeysTable.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/expandedPendingKeysTable.tsx @@ -63,7 +63,6 @@ const COLUMNS: ColumnsType = [{ } }] - //--------Component-------- const ExpandedPendingKeysTable: React.FC = ({ data, diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx index 31d31e2a4052..02c73c77528d 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx @@ -31,10 +31,12 @@ import { MenuProps } from 'antd/es/menu'; import { FilterFilled } from '@ant-design/icons'; import { ValueType } from 'react-select'; +import Search from '@/v2/components/search/search'; import SingleSelect, { Option } from '@/v2/components/select/singleSelect'; import { AxiosGetHelper } from '@/utils/axiosRequestHelper'; import { byteToSize, showDataFetchError } from '@/utils/common'; import { getFormattedTime } from '@/v2/utils/momentUtils'; +import { useDebounce } from '@/v2/hooks/debounce.hook'; import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants'; import { OpenKeys, OpenKeysResponse } from '@/v2/types/insights.types'; @@ -47,6 +49,7 @@ type OpenKeysTableProps = { handleLimitChange: (arg0: ValueType) => void; } +//-----Components------ const OpenKeysTable: React.FC = ({ limit, paginationConfig, @@ -54,8 +57,16 @@ const OpenKeysTable: React.FC = ({ }) => { const [loading, setLoading] = React.useState(false); const [data, setData] = React.useState(); + const [searchTerm, setSearchTerm] = React.useState(''); const cancelSignal = React.useRef(); + const debouncedSearch = useDebounce(searchTerm, 300); + + function filterData(data: OpenKeys[] | undefined) { + return data?.filter( + (data: OpenKeys) => data.path.includes(debouncedSearch) + ); + } function fetchOpenKeys(isFso: boolean) { setLoading(true); @@ -179,9 +190,16 @@ const OpenKeysTable: React.FC = ({ placeholder='Limit' onChange={handleLimitChange} /> + ) => setSearchTerm(e.target.value) + } + onChange={() => { }} />
Date: Tue, 22 Oct 2024 12:10:28 +0530 Subject: [PATCH 4/8] addressed review comments --- .../webapps/recon/ozone-recon-web/src/app.tsx | 3 +- .../v2/components/breadcrumbs/breadcrumbs.tsx | 1 + .../v2/components/plots/insightsFilePlot.tsx | 8 ++- .../src/v2/pages/insights/insights.less | 36 +++++++++++ .../src/v2/pages/insights/omInsights.tsx | 63 +++++++++++++++---- 5 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/insights.less diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.tsx index 78954ebb5a54..ceb633f603e7 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.tsx @@ -22,6 +22,7 @@ import { Switch as AntDSwitch, Layout } from 'antd'; import NavBar from './components/navBar/navBar'; import NavBarV2 from '@/v2/components/navBar/navBar'; import Breadcrumbs from './components/breadcrumbs/breadcrumbs'; +import BreadcrumbsV2 from '@/v2/components/breadcrumbs/breadcrumbs'; import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom'; import { routes } from '@/routes'; import { routesV2 } from '@/v2/routes-v2'; @@ -70,7 +71,7 @@ class App extends React.Component, IAppState> {
- + {(enableNewUI) ? : } New UI
} diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/breadcrumbs/breadcrumbs.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/breadcrumbs/breadcrumbs.tsx index 1c1e68479486..8e1c34decb7a 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/breadcrumbs/breadcrumbs.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/breadcrumbs/breadcrumbs.tsx @@ -51,4 +51,5 @@ const Breadcrumbs: React.FC<{}> = () => { ); } + export default Breadcrumbs; diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsFilePlot.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsFilePlot.tsx index 680dd0e2db0d..1e385e1c4697 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsFilePlot.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsFilePlot.tsx @@ -94,9 +94,15 @@ const FileSizeDistribution: React.FC = ({ let filteredData = fileCountResponse; const selectedVolumeValues = new Set(selectedVolumes.map(option => option.value)); const selectedBucketValues = new Set(selectedBuckets.map(option => option.value)); - if (selectedVolumes.length > 0) { + if (selectedVolumes.length >= 0) { // Not all volumes are selected, need to filter based on the selected values filteredData = filteredData.filter(data => selectedVolumeValues.has(data.volume)); + + // We have selected a volume but all the buckets are deselected + if (selectedVolumes.length === 1 && selectedBuckets.length === 0) { + // Since no buckets are selected there is no data + filteredData = []; + } } if (selectedBuckets.length > 0) { // Not all buckcets are selected, filter based on the selected values diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/insights.less b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/insights.less new file mode 100644 index 000000000000..dfc0ef31438d --- /dev/null +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/insights.less @@ -0,0 +1,36 @@ +/* +* 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. +*/ + +.content-div { + min-height: unset; + + .table-header-section { + display: flex; + justify-content: space-between; + align-items: center; + + .table-filter-section { + font-size: 14px; + font-weight: normal; + display: flex; + column-gap: 8px; + padding: 16px 8px; + align-items: center; + } + } +} diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx index 726407317215..241ad6fc7e80 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx @@ -16,8 +16,13 @@ * limitations under the License. */ -import { AxiosGetHelper } from '@/utils/axiosRequestHelper'; -import { showDataFetchError } from '@/utils/common'; +import React from 'react'; +import { AxiosError } from 'axios'; +import { ValueType } from 'react-select'; +import { Tabs, Tooltip } from 'antd'; +import { TablePaginationConfig } from 'antd/es/table'; +import { InfoCircleOutlined } from '@ant-design/icons'; + import { Option } from '@/v2/components/select/singleSelect'; import ContainerMismatchTable from '@/v2/components/tables/insights/containerMismatchTable'; import DeletedContainerKeysTable from '@/v2/components/tables/insights/deletedContainerKeysTable'; @@ -25,13 +30,18 @@ import DeletePendingDirTable from '@/v2/components/tables/insights/deletePending import DeletePendingKeysTable from '@/v2/components/tables/insights/deletePendingKeysTable'; import ExpandedKeyTable from '@/v2/components/tables/insights/expandedKeyTable'; import OpenKeysTable from '@/v2/components/tables/insights/openKeysTable'; -import { Container, ExpandedRow, ExpandedRowState, MismatchContainersResponse, MismatchKeys, MismatchKeysResponse } from '@/v2/types/insights.types'; -import { InfoCircleOutlined } from '@ant-design/icons'; -import { Tabs, Tooltip } from 'antd'; -import { TablePaginationConfig } from 'antd/es/table'; -import { AxiosError } from 'axios'; -import React, { useState } from 'react'; -import { ValueType } from 'react-select'; +import { showDataFetchError } from '@/utils/common'; +import { AxiosGetHelper } from '@/utils/axiosRequestHelper'; + +import { + Container, + ExpandedRow, + ExpandedRowState, + MismatchKeysResponse +} from '@/v2/types/insights.types'; + +import './insights.less'; + const OMDBInsights: React.FC<{}> = () => { @@ -108,7 +118,16 @@ const OMDBInsights: React.FC<{}> = () => {
- + + Container Mismatch Info + + + + + }> = () => { onRowExpand={onRowExpandClick} handleLimitChange={handleLimitChange} /> - + + Open Keys + + + + + }> + handleLimitChange={handleLimitChange} /> @@ -154,7 +182,16 @@ const OMDBInsights: React.FC<{}> = () => { onRowExpand={onRowExpandClick} expandedRowRender={expandedRowRender} /> - + + Directories Pending for Deletion + + + + + }> Date: Tue, 22 Oct 2024 19:32:21 +0530 Subject: [PATCH 5/8] Fixed review comments --- .../src/v2/components/navBar/navBar.tsx | 4 ++-- .../v2/components/overviewCard/overviewSummaryCard.tsx | 10 +++++++--- .../tables/insights/deletePendingKeysTable.tsx | 3 +-- .../src/v2/components/tables/volumesTable.tsx | 1 - .../src/v2/constants/breadcrumbs.constants.tsx | 2 +- .../src/v2/pages/insights/omInsights.tsx | 4 +++- .../ozone-recon-web/src/v2/pages/overview/overview.tsx | 6 ++++-- .../webapps/recon/ozone-recon-web/src/v2/routes-v2.tsx | 2 +- 8 files changed, 19 insertions(+), 13 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/navBar/navBar.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/navBar/navBar.tsx index 4e62d60caccb..3da4104634c8 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/navBar/navBar.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/navBar/navBar.tsx @@ -125,10 +125,10 @@ const NavBar: React.FC = ({ Insights - }> OM DB Insights - + ), ( diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewSummaryCard.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewSummaryCard.tsx index e383512f20e8..8736b3e0d290 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewSummaryCard.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewSummaryCard.tsx @@ -39,6 +39,7 @@ type OverviewTableCardProps = { data?: string | React.ReactElement; linkToUrl?: string; showHeader?: boolean; + state?: Record; } // ------------- Styles -------------- // @@ -63,15 +64,18 @@ const OverviewSummaryCard: React.FC = ({ columns = [], tableData = [], linkToUrl = '', - showHeader = false + showHeader = false, + state }) => { - const titleElement = (linkToUrl) ? (
{title} View Insights diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx index 58e973146761..835da4f1f9aa 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx @@ -102,7 +102,6 @@ const DeletePendingKeysTable: React.FC = ({ } function expandedRowRender(record: DeletePendingKeysColumns) { - console.log(expandedDeletePendingKeys); const filteredData = expandedDeletePendingKeys?.flatMap((info) => ( info.omKeyInfoList?.filter((key) => key.keyName === record.keyName) )); @@ -125,7 +124,7 @@ const DeletePendingKeysTable: React.FC = ({ const deletePendingKeys: DeletePendingKeysResponse = response?.data; let deletedKeyData = []; // Sum up the data size and organize related key information - deletedKeyData = deletePendingKeys?.deletedKeyInfo.flatMap((keyInfo) => { + deletedKeyData = deletePendingKeys?.deletedKeyInfo?.flatMap((keyInfo) => { expandedDeletePendingKeys.push(keyInfo); let count = 0; let item: DeletePendingKey = keyInfo.omKeyInfoList?.reduce((obj, curr) => { diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/volumesTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/volumesTable.tsx index 4de0d713fce6..ecfbf730a2a9 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/volumesTable.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/volumesTable.tsx @@ -96,7 +96,6 @@ const VolumesTable: React.FC = ({ React.useEffect(() => { // On table mount add the actions column - console.log("Adding new column"); const actionsColumn: ColumnType = { title: 'Actions', key: 'actions', diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/breadcrumbs.constants.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/breadcrumbs.constants.tsx index 8f5be189c2c1..807a68cc8d25 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/breadcrumbs.constants.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/breadcrumbs.constants.tsx @@ -30,5 +30,5 @@ export const breadcrumbNameMap: BreadcrumbNameMap = { '/Insights': 'Insights', '/DiskUsage': 'Disk Usage', '/Heatmap': 'Heatmap', - '/OmInsights': 'OM DB Insights' + '/Om': 'OM DB Insights' }; diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx index 241ad6fc7e80..732af0aa00e7 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx @@ -41,6 +41,7 @@ import { } from '@/v2/types/insights.types'; import './insights.less'; +import { useLocation } from 'react-router-dom'; const OMDBInsights: React.FC<{}> = () => { @@ -53,6 +54,7 @@ const OMDBInsights: React.FC<{}> = () => { }); const rowExpandSignal = React.useRef(); + const {state: { activeTab } = {}} = useLocation>(); const paginationConfig: TablePaginationConfig = { showTotal: (total: number, range) => `${range[0]}-${range[1]} of ${total}` @@ -117,7 +119,7 @@ const OMDBInsights: React.FC<{}> = () => {
- + Container Mismatch Info diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/overview/overview.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/overview/overview.tsx index 1568cb4b3ed3..688c98148c48 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/overview/overview.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/overview/overview.tsx @@ -485,7 +485,8 @@ const Overview: React.FC<{}> = () => { ) } ]} - linkToUrl='/Om' /> + linkToUrl='/Om' + state={{activeTab: '2'}} />
= () => { ) } ]} - linkToUrl='/Om' /> + linkToUrl='/Om' + state={{activeTab: '3'}} /> diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/routes-v2.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/routes-v2.tsx index 7e55a876737d..9f27b7cae765 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/routes-v2.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/routes-v2.tsx @@ -61,7 +61,7 @@ export const routesV2 = [ component: Insights }, { - path: '/OmInsights', + path: '/Om', component: OMDBInsights } ]; From 31581534586372326b8d2387e53c159256348854 Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Wed, 23 Oct 2024 12:45:50 +0530 Subject: [PATCH 6/8] Fixed data duplication issue --- .../src/v2/components/tables/insights/deletePendingKeysTable.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx index 835da4f1f9aa..65ada4956411 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx @@ -150,6 +150,7 @@ const DeletePendingKeysTable: React.FC = ({ React.useEffect(() => { fetchDeletePendingKeys(); + expandedDeletePendingKeys = []; return (() => { cancelSignal.current && cancelSignal.current.abort(); From 1230f5580e7ba0051f19d325030ee57faf317137 Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Wed, 23 Oct 2024 22:35:55 +0530 Subject: [PATCH 7/8] Added info tooltip to the Exist at column --- .../insights/containerMismatchTable.tsx | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx index b275a40fa0d9..818eca37f8ef 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx @@ -22,7 +22,8 @@ import { Dropdown, Menu, Popover, - Table + Table, + Tooltip } from 'antd'; import { ColumnsType, @@ -31,7 +32,7 @@ import { import { MenuProps as FilterMenuProps } from 'antd/es/menu'; -import { FilterFilled } from '@ant-design/icons'; +import { FilterFilled, InfoCircleOutlined } from '@ant-design/icons'; import { ValueType } from 'react-select'; import Search from '@/v2/components/search/search'; @@ -135,8 +136,15 @@ const ContainerMismatchTable: React.FC = ({ SCM }> - + + + SCM: Container exists at SCM but missing at OM.
+ OM: Container exist at OM but missing at SCM. + }> + +
, dataIndex: 'existsAt' } @@ -183,9 +191,9 @@ const ContainerMismatchTable: React.FC = ({ disabled={(data?.length ?? 0) < 1} searchInput={searchTerm} onSearchChange={ - (e: React.ChangeEvent) => setSearchTerm(e.target.value) + (e: React.ChangeEvent) => setSearchTerm(e.target.value) } - onChange={() => {}}/> + onChange={() => { }} />
= ({ pagination={paginationConfig} rowKey='containerId' locale={{ filterTitle: '' }} - scroll={{ x: 'max-content' }} /> + scroll={{ x: 'max-content' }} /> ) } From 313cf4c4654f2051391b3daf43a7281f53ef4c6d Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Thu, 24 Oct 2024 10:30:25 +0530 Subject: [PATCH 8/8] Fixed sorting for the plot --- .../src/v2/components/plots/insightsFilePlot.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsFilePlot.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsFilePlot.tsx index 1e385e1c4697..bb6453ed7c19 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsFilePlot.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/plots/insightsFilePlot.tsx @@ -132,7 +132,8 @@ const FileSizeDistribution: React.FC = ({ setFilePlotData({ fileCountValues: fileCountValues, - fileCountMap: fileCountMap + // set the sorted value by size for the map + fileCountMap: new Map([...fileCountMap.entries()].sort((a, b) => a[0] - b[0])) }); } @@ -154,7 +155,7 @@ const FileSizeDistribution: React.FC = ({ const filePlotOptions: EChartsOption = { xAxis: { type: 'category', - data: fileCountValues ?? [] + data: [...fileCountValues] ?? [] }, yAxis: { type: 'value'