This is an automated email from the ASF dual-hosted git repository. siyao pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push: new ae59f8ae5f HDDS-7248. Recon: Expand the container status page to show all unhealthy container states (#3837) ae59f8ae5f is described below commit ae59f8ae5f2149ba1a7749bd3073803345333742 Author: smitajoshi12 <112169209+smitajosh...@users.noreply.github.com> AuthorDate: Fri Oct 21 12:41:45 2022 +0530 HDDS-7248. Recon: Expand the container status page to show all unhealthy container states (#3837) --- .../hadoop/ozone/recon/api/ContainerEndpoint.java | 1 + .../webapps/recon/ozone-recon-web/api/db.json | 330 ++++++++++++++++++++- .../webapps/recon/ozone-recon-web/api/routes.json | 8 +- .../src/components/navBar/navBar.tsx | 5 + .../views/missingContainers/missingContainers.tsx | 227 +++++++++----- 5 files changed, 502 insertions(+), 69 deletions(-) diff --git a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/ContainerEndpoint.java b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/ContainerEndpoint.java index 0832700e96..ce8cc70589 100644 --- a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/ContainerEndpoint.java +++ b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/ContainerEndpoint.java @@ -242,6 +242,7 @@ public class ContainerEndpoint { */ @GET @Path("/missing") + @Deprecated public Response getMissingContainers( @DefaultValue(DEFAULT_FETCH_COUNT) @QueryParam(RECON_QUERY_LIMIT) int limit diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/db.json b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/db.json index 04036416f4..1639302e23 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/db.json +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/db.json @@ -1260,5 +1260,333 @@ "lastUpdatedTimestamp": 1663421094507, "lastUpdatedSeqNumber": 0 } - ] + ], + "containerUnhealthyMissing": { + "missingCount": 0, + "underReplicatedCount": 1, + "overReplicatedCount": 0, + "misReplicatedCount": 0, + "containers": [ + { + "containerID": 1, + "containerState": "MISSING", + "unhealthySince": 1665590446222, + "expectedReplicaCount": 3, + "actualReplicaCount": 0, + "replicaDeltaCount": 3, + "reason": null, + "keys": 1, + "pipelineID": "a10ffab6-8ed5-414a-aaf5-79890ff3e8a1", + "replicas": [ + { + "containerId": 2, + "datanodeUuid": "15526f1b-76f2-4d8f-876c-c343c94ea476", + "datanodeHost": "ozone_datanode_2.ozone_missing1", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590397315, + "lastBcsId": 2 + }, + { + "containerId": 2, + "datanodeUuid": "f55476ab-4687-464d-a100-1c65de4366e3", + "datanodeHost": "ozone_datanode_3.ozone_missing2", + "firstSeenTime": 1665588176616, + "lastSeenTime": 1665590392293, + "lastBcsId": 2 + }, + { + "containerId": 2, + "datanodeUuid": "7a457bcb-d63e-49cc-b3ff-8b22bf48d130", + "datanodeHost": "ozone_datanode_1.ozone_missing3", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590272289, + "lastBcsId": 0 + } + ] + }, + { + "containerID": 2, + "containerState": "MISSING", + "unhealthySince": 1665590446222, + "expectedReplicaCount": 3, + "actualReplicaCount": 0, + "replicaDeltaCount": 3, + "reason": null, + "keys": 1, + "pipelineID": "a10ffab6-8ed5-414a-aaf5-79890ff3e8a1", + "replicas": [ + { + "containerId": 3, + "datanodeUuid": "15526f1b-76f2-4d8f-876c-c343c94ea476", + "datanodeHost": "ozone_datanode_2.ozone_missing1", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590397315, + "lastBcsId": 2 + }, + { + "containerId": 3, + "datanodeUuid": "f55476ab-4687-464d-a100-1c65de4366e3", + "datanodeHost": "ozone_datanode_3.ozone_missing2", + "firstSeenTime": 1665588176616, + "lastSeenTime": 1665590392293, + "lastBcsId": 2 + }, + { + "containerId": 3, + "datanodeUuid": "7a457bcb-d63e-49cc-b3ff-8b22bf48d130", + "datanodeHost": "ozone_datanode_1.ozone_missing3", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590272289, + "lastBcsId": 0 + } + ] + } + ] + }, + "containerUnhealthyUnderReplicated": { + "missingCount": 0, + "underReplicatedCount": 1, + "overReplicatedCount": 0, + "misReplicatedCount": 0, + "containers": [ + { + "containerID": 2, + "containerState": "UNDER_REPLICATED", + "unhealthySince": 1665590446222, + "expectedReplicaCount": 3, + "actualReplicaCount": 2, + "replicaDeltaCount": 1, + "reason": null, + "keys": 1, + "pipelineID": "a10ffab6-8ed5-414a-aaf5-79890ff3e8a1", + "replicas": [ + { + "containerId": 2, + "datanodeUuid": "15526f1b-76f2-4d8f-876c-c343c94ea476", + "datanodeHost": "ozone_datanode_2.ozone_UnderReplicated2", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590397315, + "lastBcsId": 2 + }, + { + "containerId": 2, + "datanodeUuid": "f55476ab-4687-464d-a100-1c65de4366e3", + "datanodeHost": "ozone_datanode_3.ozone_underreplicated2", + "firstSeenTime": 1665588176616, + "lastSeenTime": 1665590392293, + "lastBcsId": 2 + }, + { + "containerId": 2, + "datanodeUuid": "7a457bcb-d63e-49cc-b3ff-8b22bf48d130", + "datanodeHost": "ozone_datanode_1.ozone_underreplicated2", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590272289, + "lastBcsId": 0 + } + ] + }, + { + "containerID": 3, + "containerState": "UNDER_REPLICATED", + "unhealthySince": 1665590446222, + "expectedReplicaCount": 4, + "actualReplicaCount": 2, + "replicaDeltaCount": 2, + "reason": null, + "keys": 1, + "pipelineID": "a10ffab6-8ed5-414a-aaf5-79890ff3e8a1", + "replicas": [ + { + "containerId": 3, + "datanodeUuid": "15526f1b-76f2-4d8f-876c-c343c94ea476", + "datanodeHost": "ozone_datanode_2.ozone_underreplicated3", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590397315, + "lastBcsId": 2 + }, + { + "containerId": 3, + "datanodeUuid": "f55476ab-4687-464d-a100-1c65de4366e3", + "datanodeHost": "ozone_datanode_3.ozone_underreplicated3", + "firstSeenTime": 1665588176616, + "lastSeenTime": 1665590392293, + "lastBcsId": 2 + }, + { + "containerId": 3, + "datanodeUuid": "7a457bcb-d63e-49cc-b3ff-8b22bf48d130", + "datanodeHost": "ozone_datanode_1.ozone_underreplicated3", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590272289, + "lastBcsId": 0 + } + ] + } + ] + }, + "containerUnhealthyOverReplicated": { + "missingCount": 0, + "underReplicatedCount": 1, + "overReplicatedCount": 0, + "misReplicatedCount": 0, + "containers": [ + { + "containerID": 2, + "containerState": "OVER_REPLICATED", + "unhealthySince": 1665590446222, + "expectedReplicaCount": 3, + "actualReplicaCount": 2, + "replicaDeltaCount": 1, + "reason": null, + "keys": 1, + "pipelineID": "a10ffab6-8ed5-414a-aaf5-79890ff3e8a1", + "replicas": [ + { + "containerId": 2, + "datanodeUuid": "15526f1b-76f2-4d8f-876c-c343c94ea476", + "datanodeHost": "ozone_datanode_2.ozone_overreplicated2", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590397315, + "lastBcsId": 2 + }, + { + "containerId": 2, + "datanodeUuid": "f55476ab-4687-464d-a100-1c65de4366e3", + "datanodeHost": "ozone_datanode_3.ozone_overreplicated22", + "firstSeenTime": 1665588176616, + "lastSeenTime": 1665590392293, + "lastBcsId": 2 + }, + { + "containerId": 2, + "datanodeUuid": "7a457bcb-d63e-49cc-b3ff-8b22bf48d130", + "datanodeHost": "ozone_datanode_1.ozone_overreplicated2", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590272289, + "lastBcsId": 0 + } + ] + }, + { + "containerID": 3, + "containerState": "OVER_REPLICATED", + "unhealthySince": 1665590446222, + "expectedReplicaCount": 4, + "actualReplicaCount": 2, + "replicaDeltaCount": 2, + "reason": null, + "keys": 1, + "pipelineID": "a10ffab6-8ed5-414a-aaf5-79890ff3e8a1", + "replicas": [ + { + "containerId": 3, + "datanodeUuid": "15526f1b-76f2-4d8f-876c-c343c94ea476", + "datanodeHost": "ozone_datanode_2.ozone_overreplicated3", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590397315, + "lastBcsId": 2 + }, + { + "containerId": 3, + "datanodeUuid": "f55476ab-4687-464d-a100-1c65de4366e3", + "datanodeHost": "ozone_datanode_3.ozone_overreplicated3", + "firstSeenTime": 1665588176616, + "lastSeenTime": 1665590392293, + "lastBcsId": 2 + }, + { + "containerId": 3, + "datanodeUuid": "7a457bcb-d63e-49cc-b3ff-8b22bf48d130", + "datanodeHost": "ozone_datanode_1.ozone_overreplicated3", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590272289, + "lastBcsId": 0 + } + ] + } + ] + }, + "containerUnhealthyMisReplicated": { + "missingCount": 0, + "underReplicatedCount": 1, + "overReplicatedCount": 0, + "misReplicatedCount": 0, + "containers": [ + { + "containerID": 2, + "containerState": "MIS_REPLICATED", + "unhealthySince": 1665590446222, + "expectedReplicaCount": 3, + "actualReplicaCount": 2, + "replicaDeltaCount": 1, + "reason": null, + "keys": 1, + "pipelineID": "a10ffab6-8ed5-414a-aaf5-79890ff3e8a1", + "replicas": [ + { + "containerId": 2, + "datanodeUuid": "15526f1b-76f2-4d8f-876c-c343c94ea476", + "datanodeHost": "ozone_datanode_2.ozone_misreplicated2", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590397315, + "lastBcsId": 2 + }, + { + "containerId": 2, + "datanodeUuid": "f55476ab-4687-464d-a100-1c65de4366e3", + "datanodeHost": "ozone_datanode_3.ozone_misreplicated2", + "firstSeenTime": 1665588176616, + "lastSeenTime": 1665590392293, + "lastBcsId": 2 + }, + { + "containerId": 2, + "datanodeUuid": "7a457bcb-d63e-49cc-b3ff-8b22bf48d130", + "datanodeHost": "ozone_datanode_1.ozone_misreplicated2", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590272289, + "lastBcsId": 0 + } + ] + }, + { + "containerID": 3, + "containerState": "MIS_REPLICATED", + "unhealthySince": 1665590446222, + "expectedReplicaCount": 4, + "actualReplicaCount": 2, + "replicaDeltaCount": 2, + "reason": null, + "keys": 1, + "pipelineID": "a10ffab6-8ed5-414a-aaf5-79890ff3e8a1", + "replicas": [ + { + "containerId": 3, + "datanodeUuid": "15526f1b-76f2-4d8f-876c-c343c94ea476", + "datanodeHost": "ozone_datanode_2.ozone_misreplicated3", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590397315, + "lastBcsId": 2 + }, + { + "containerId": 3, + "datanodeUuid": "f55476ab-4687-464d-a100-1c65de4366e3", + "datanodeHost": "ozone_datanode_3.ozone_misreplicated3", + "firstSeenTime": 1665588176616, + "lastSeenTime": 1665590392293, + "lastBcsId": 2 + }, + { + "containerId": 3, + "datanodeUuid": "7a457bcb-d63e-49cc-b3ff-8b22bf48d130", + "datanodeHost": "ozone_datanode_1.ozone_misreplicated3", + "firstSeenTime": 1665588176660, + "lastSeenTime": 1665590272289, + "lastBcsId": 0 + } + ] + } + ] + } } \ No newline at end of file diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/routes.json b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/routes.json index 4aa303fe29..0b0a4bb9d6 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/routes.json +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/routes.json @@ -1,7 +1,6 @@ { "/api/v1/*": "/$1", "/containers/:id/keys": "/keys", - "/containers/missing": "/missingContainers", "/utilization/fileCount": "/fileSizeCounts", "/namespace/du?path=/&files=true": "/root", "/namespace/du?path=/vol:id&files=true": "/volume", @@ -23,5 +22,10 @@ "/namespace/du?path=/clunky&files=true": "/clunky", "/namespace/summary?path=*": "/metadata", "/namespace/quota?path=*": "/quota", - "/task/status": "/taskStatus" + "/task/status": "/taskStatus", + "/containers/missing": "/missingContainers", + "/containers/unhealthy/MISSING": "/containerUnhealthyMissing", + "/containers/unhealthy/UNDER_REPLICATED": "/containerUnhealthyUnderReplicated", + "/containers/unhealthy/OVER_REPLICATED": "/containerUnhealthyOverReplicated", + "/containers/unhealthy/MIS_REPLICATED": "/containerUnhealthyMisReplicated" } \ No newline at end of file diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/navBar/navBar.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/navBar/navBar.tsx index acc0550404..d381e8e4d4 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/navBar/navBar.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/navBar/navBar.tsx @@ -66,6 +66,11 @@ class NavBar extends React.Component<INavBarProps> { <span>Pipelines</span> <Link to='/Pipelines'/> </Menu.Item> + <Menu.Item key='/Containers'> + <Icon type='container'/> + <span>Containers</span> + <Link to='/Containers'/> + </Menu.Item> <Menu.Item key='/Insights'> <Icon type='bar-chart'/> <span>Insights</span> diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/missingContainers/missingContainers.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/missingContainers/missingContainers.tsx index 6a6dca304d..c6a76b5b97 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/missingContainers/missingContainers.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/missingContainers/missingContainers.tsx @@ -18,7 +18,7 @@ import React from 'react'; import axios from 'axios'; -import {Icon, Table, Tooltip} from 'antd'; +import {Icon, Table, Tooltip, Tabs} from 'antd'; import {PaginationConfig} from 'antd/lib/pagination'; import filesize from 'filesize'; import moment from 'moment'; @@ -26,6 +26,7 @@ import {showDataFetchError, timeFormat} from 'utils/common'; import './missingContainers.less'; const size = filesize.partial({standard: 'iec'}); +const {TabPane} = Tabs; interface IMissingContainerResponse { containerID: number; @@ -35,6 +36,19 @@ interface IMissingContainerResponse { pipelineID: string; } +interface IContainerResponse { + containerID: number; + containerState: string; + unhealthySince: string; + expectedReplicaCount: number; + actualReplicaCount: number; + replicaDeltaCount: number; + reason: string; + keys: number; + pipelineID: string; + replicas: IContainerReplicas[]; +} + export interface IContainerReplica { containerId: number; datanodeHost: string; @@ -42,11 +56,28 @@ export interface IContainerReplica { lastReportTimestamp: number; } +export interface IContainerReplicas { + containerId: number; + datanodeUuid: string; + datanodeHost: string; + firstSeenTime: number; + lastSeenTime: number; + lastBcsId: number; +} + export interface IMissingContainersResponse { totalCount: number; containers: IMissingContainerResponse[]; } +interface IUnhealthyContainersResponse { + missingCount: number; + underReplicatedCount: number; + overReplicatedCount: number; + misReplicatedCount: number; + containers: IContainerResponse[]; +} + interface IKeyResponse { Volume: string; Bucket: string; @@ -63,30 +94,79 @@ interface IContainerKeysResponse { keys: IKeyResponse[]; } -const COLUMNS = [ +const KEY_TABLE_COLUMNS = [ + { + 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) => <div>{size(dataSize)}</div> + }, + { + 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 CONTAINER_TAB_COLUMNS = [ { title: 'Container ID', dataIndex: 'containerID', key: 'containerID', - sorter: (a: IMissingContainerResponse, b: IMissingContainerResponse) => a.containerID - b.containerID + sorter: (a: IContainerResponse, b: IContainerResponse) => a.containerID - b.containerID }, { title: 'No. of Keys', dataIndex: 'keys', key: 'keys', - sorter: (a: IMissingContainerResponse, b: IMissingContainerResponse) => a.keys - b.keys + sorter: (a: IContainerResponse, b: IContainerResponse) => a.keys - b.keys + }, + { + title: 'Active/Expected Replica(s)', + dataIndex: 'expectedReplicaCount', + key: 'expectedReplicaCount', + render: (expectedReplicaCount: number, record: IContainerResponse) => { + const actualReplicaCount = record.actualReplicaCount; + return ( + <span> + {actualReplicaCount} / {expectedReplicaCount} + </span> + ); + } }, { title: 'Datanodes', dataIndex: 'replicas', key: 'replicas', - render: (replicas: IContainerReplica[]) => ( + render: (replicas: IContainerReplicas[]) => ( <div> - {replicas.map(replica => { + {replicas && replicas.map(replica => { const tooltip = ( <div> - <div>First Report Time: {timeFormat(replica.firstReportTimestamp)}</div> - <div>Last Report Time: {timeFormat(replica.lastReportTimestamp)}</div> + <div>First Report Time: {timeFormat(replica.firstSeenTime)}</div> + <div>Last Report Time: {timeFormat(replica.lastSeenTime)}</div> </div> ); return ( @@ -111,50 +191,14 @@ const COLUMNS = [ title: 'Pipeline ID', dataIndex: 'pipelineID', key: 'pipelineID', - sorter: (a: IMissingContainerResponse, b: IMissingContainerResponse) => a.pipelineID.localeCompare(b.pipelineID) - }, - { - title: 'Missing Since', - dataIndex: 'missingSince', - key: 'missingSince', - render: (missingSince: number) => timeFormat(missingSince), - sorter: (a: IMissingContainerResponse, b: IMissingContainerResponse) => a.missingSince - b.missingSince - } -]; - -const KEY_TABLE_COLUMNS = [ - { - 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) => <div>{size(dataSize)}</div> + sorter: (a: IContainerResponse, b: IContainerResponse) => a.pipelineID.localeCompare(b.pipelineID) }, { - 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') + title: 'Unhealthy Since', + dataIndex: 'unhealthySince', + key: 'unhealthySince', + render: (unhealthySince: number) => timeFormat(unhealthySince), + sorter: (a: IContainerResponse, b: IContainerResponse) => a.unhealthySince - b.unhealthySince } ]; @@ -171,7 +215,10 @@ interface IExpandedRowState { interface IMissingContainersState { loading: boolean; - dataSource: IMissingContainerResponse[]; + missingDataSource: IContainerResponse[]; + underReplicatedDataSource: IContainerResponse[]; + overReplicatedDataSource: IContainerResponse[]; + misReplicatedDataSource: IContainerResponse[]; totalCount: number; expandedRowData: IExpandedRow; } @@ -181,7 +228,10 @@ export class MissingContainers extends React.Component<Record<string, object>, I super(props); this.state = { loading: false, - dataSource: [], + missingDataSource: [], + underReplicatedDataSource: [], + overReplicatedDataSource: [], + misReplicatedDataSource: [], totalCount: 0, expandedRowData: {} }; @@ -192,16 +242,36 @@ export class MissingContainers extends React.Component<Record<string, object>, I this.setState({ loading: true }); - axios.get('/api/v1/containers/missing').then(response => { - const missingContainersResponse: IMissingContainersResponse = response.data; - const totalCount = missingContainersResponse.totalCount; - const missingContainers: IMissingContainerResponse[] = missingContainersResponse.containers; + + axios.all([ + axios.get('/api/v1/containers/unhealthy/MISSING'), + axios.get('/api/v1/containers/unhealthy/UNDER_REPLICATED'), + axios.get('/api/v1/containers/unhealthy/OVER_REPLICATED'), + axios.get('/api/v1/containers/unhealthy/MIS_REPLICATED') + ]).then(axios.spread((missingContainersResponse, underReplicatedResponse, overReplicatedResponse, misReplicatedResponse, allReplicatedResponse) => { + + const missingContainersResponseData: IUnhealthyContainersResponse = missingContainersResponse.data; + const totalCount = missingContainersResponseData.missingCount; + const missingContainers: IContainerResponse[] = missingContainersResponseData.containers; + + const underReplicatedResponseData: IUnhealthyContainersResponse = underReplicatedResponse.data; + const uContainers: IContainerResponse[] = underReplicatedResponseData.containers; + + const overReplicatedResponseData: IUnhealthyContainersResponse = overReplicatedResponse.data; + const oContainers: IContainerResponse[] = overReplicatedResponseData.containers; + + const misReplicatedResponseData: IUnhealthyContainersResponse = misReplicatedResponse.data; + const mContainers: IContainerResponse[] = misReplicatedResponseData.containers; + this.setState({ loading: false, - dataSource: missingContainers, + missingDataSource: missingContainers, + underReplicatedDataSource: uContainers, + overReplicatedDataSource: oContainers, + misReplicatedDataSource: mContainers, totalCount }); - }).catch(error => { + })).catch(error => { this.setState({ loading: false }); @@ -212,7 +282,7 @@ export class MissingContainers extends React.Component<Record<string, object>, I onShowSizeChange = (current: number, pageSize: number) => { console.log(current, pageSize); }; - + onRowExpandClick = (expanded: boolean, record: IMissingContainerResponse) => { if (expanded) { this.setState(({expandedRowData}) => { @@ -270,7 +340,7 @@ export class MissingContainers extends React.Component<Record<string, object>, I }; render() { - const {dataSource, loading, totalCount} = this.state; + const {missingDataSource, loading, underReplicatedDataSource, overReplicatedDataSource, misReplicatedDataSource} = this.state; const paginationConfig: PaginationConfig = { showTotal: (total: number, range) => `${range[0]}-${range[1]} of ${total} missing containers`, showSizeChanger: true, @@ -279,14 +349,39 @@ export class MissingContainers extends React.Component<Record<string, object>, I return ( <div className='missing-containers-container'> <div className='page-header'> - Missing Containers ({totalCount}) + Containers </div> <div className='content-div'> - <Table - expandRowByClick dataSource={dataSource} columns={COLUMNS} - loading={loading} - pagination={paginationConfig} rowKey='containerID' - expandedRowRender={this.expandedRowRender} onExpand={this.onRowExpandClick}/> + <Tabs defaultActiveKey='1'> + <TabPane key='1' tab="Missing"> + <Table + expandRowByClick dataSource={missingDataSource} columns={CONTAINER_TAB_COLUMNS} + loading={loading} + pagination={paginationConfig} rowKey='containerID' + expandedRowRender={this.expandedRowRender} onExpand={this.onRowExpandClick}/> + </TabPane> + <TabPane key='2' tab='Under-Replicated'> + <Table + expandRowByClick dataSource={underReplicatedDataSource} columns={CONTAINER_TAB_COLUMNS} + loading={loading} + pagination={paginationConfig} rowKey='containerID' + expandedRowRender={this.expandedRowRender} onExpand={this.onRowExpandClick}/> + </TabPane> + <TabPane key='3' tab='Over-Replicated'> + <Table + expandRowByClick dataSource={overReplicatedDataSource} columns={CONTAINER_TAB_COLUMNS} + loading={loading} + pagination={paginationConfig} rowKey='containerID' + expandedRowRender={this.expandedRowRender} onExpand={this.onRowExpandClick}/> + </TabPane> + <TabPane key='4' tab='Mis-Replicated'> + <Table + expandRowByClick dataSource={misReplicatedDataSource} columns={CONTAINER_TAB_COLUMNS} + loading={loading} + pagination={paginationConfig} rowKey='containerID' + expandedRowRender={this.expandedRowRender} onExpand={this.onRowExpandClick}/> + </TabPane> + </Tabs> </div> </div> ); --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@ozone.apache.org For additional commands, e-mail: commits-h...@ozone.apache.org