blob: 7e84a0d564ee67135d5b18eede6e6304f82149f0 [file] [log] [blame]
/*
* 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 {Row, Col, Icon, Tooltip} from 'antd';
import OverviewCard from 'components/overviewCard/overviewCard';
import axios from 'axios';
import prettyBytes from 'pretty-bytes';
import './overview.less';
import {IStorageReport} from 'types/datanode.types';
import {IMissingContainersResponse} from '../missingContainers/missingContainers';
import moment from 'moment';
import AutoReloadPanel from 'components/autoReloadPanel/autoReloadPanel';
import {showDataFetchError} from 'utils/common';
import {AutoReloadHelper} from 'utils/autoReloadHelper';
interface IClusterStateResponse {
totalDatanodes: number;
healthyDatanodes: number;
pipelines: number;
storageReport: IStorageReport;
containers: number;
volumes: number;
buckets: number;
keys: number;
}
interface IOverviewState {
loading: boolean;
datanodes: string;
pipelines: number;
storageReport: IStorageReport;
containers: number;
volumes: number;
buckets: number;
keys: number;
missingContainersCount: number;
lastUpdated: number;
}
export class Overview extends React.Component<Record<string, object>, IOverviewState> {
interval = 0;
autoReload: AutoReloadHelper;
constructor(props = {}) {
super(props);
this.state = {
loading: false,
datanodes: '',
pipelines: 0,
storageReport: {
capacity: 0,
used: 0,
remaining: 0
},
containers: 0,
volumes: 0,
buckets: 0,
keys: 0,
missingContainersCount: 0,
lastUpdated: 0
};
this.autoReload = new AutoReloadHelper(this._loadData);
}
_loadData = () => {
this.setState({
loading: true
});
axios.all([
axios.get('/api/v1/clusterState'),
axios.get('/api/v1/containers/missing')
]).then(axios.spread((clusterStateResponse, missingContainersResponse) => {
const clusterState: IClusterStateResponse = clusterStateResponse.data;
const missingContainers: IMissingContainersResponse = missingContainersResponse.data;
const missingContainersCount = missingContainers.totalCount;
this.setState({
loading: false,
datanodes: `${clusterState.healthyDatanodes}/${clusterState.totalDatanodes}`,
storageReport: clusterState.storageReport,
pipelines: clusterState.pipelines,
containers: clusterState.containers,
volumes: clusterState.volumes,
buckets: clusterState.buckets,
keys: clusterState.keys,
missingContainersCount,
lastUpdated: Number(moment())
});
})).catch(error => {
this.setState({
loading: false
});
showDataFetchError(error.toString());
});
};
componentDidMount(): void {
this._loadData();
this.autoReload.startPolling();
}
componentWillUnmount(): void {
this.autoReload.stopPolling();
}
render() {
const {loading, datanodes, pipelines, storageReport, containers, volumes, buckets,
keys, missingContainersCount, lastUpdated} = this.state;
const datanodesElement = (
<span>
<Icon type='check-circle' theme='filled' className='icon-success icon-small'/> {datanodes} <span className='ant-card-meta-description meta'>HEALTHY</span>
</span>
);
const containersTooltip = missingContainersCount === 1 ? 'container is missing' : 'containers are missing';
const containersLink = missingContainersCount > 0 ? '/MissingContainers' : '';
const containersElement = missingContainersCount > 0 ? (
<span>
<Tooltip placement='bottom' title={`${missingContainersCount} ${containersTooltip}`}>
<Icon type='exclamation-circle' theme='filled' className='icon-failure icon-small'/>
</Tooltip>
<span className='padded-text'>{containers - missingContainersCount}/{containers}</span>
</span>
) :
containers.toString();
const clusterCapacity = `${prettyBytes(storageReport.capacity - storageReport.remaining)}/${prettyBytes(storageReport.capacity)}`;
return (
<div className='overview-content'>
<div className='page-header'>
Overview
<AutoReloadPanel isLoading={loading} lastUpdated={lastUpdated} togglePolling={this.autoReload.handleAutoReloadToggle} onReload={this._loadData}/>
</div>
<Row gutter={[25, 25]}>
<Col xs={24} sm={18} md={12} lg={12} xl={6}>
<OverviewCard
hoverable loading={loading} title='Datanodes'
data={datanodesElement} icon='cluster'
linkToUrl='/Datanodes'/>
</Col>
<Col xs={24} sm={18} md={12} lg={12} xl={6}>
<OverviewCard
hoverable loading={loading} title='Pipelines'
data={pipelines.toString()} icon='deployment-unit'
linkToUrl='/Pipelines'/>
</Col>
<Col xs={24} sm={18} md={12} lg={12} xl={6}>
<OverviewCard
loading={loading} title='Cluster Capacity' data={clusterCapacity}
icon='database'
storageReport={storageReport}/>
</Col>
<Col xs={24} sm={18} md={12} lg={12} xl={6}>
<OverviewCard
loading={loading} title='Containers' data={containersElement}
icon='container'
error={missingContainersCount > 0} linkToUrl={containersLink}/>
</Col>
</Row>
<Row gutter={[25, 25]}>
<Col xs={24} sm={18} md={12} lg={12} xl={6}>
<OverviewCard loading={loading} title='Volumes' data={volumes.toString()} icon='inbox'/>
</Col>
<Col xs={24} sm={18} md={12} lg={12} xl={6}>
<OverviewCard loading={loading} title='Buckets' data={buckets.toString()} icon='folder-open'/>
</Col>
<Col xs={24} sm={18} md={12} lg={12} xl={6}>
<OverviewCard loading={loading} title='Keys' data={keys.toString()} icon='file-text'/>
</Col>
</Row>
</div>
);
}
}