[UI] Brokers Dashboard

This commit is contained in:
Oleg Shuralev 2020-01-08 03:11:52 +03:00
parent 88335f94a8
commit 74b0c000da
No known key found for this signature in database
GPG key ID: 0459DF80E1A2FD1B
8 changed files with 209 additions and 50 deletions

View file

@ -1,11 +1,15 @@
import React from 'react'; import React from 'react';
import PageLoader from 'components/common/PageLoader/PageLoader'; import PageLoader from 'components/common/PageLoader/PageLoader';
import { ClusterId } from 'types'; import { ClusterId, BrokerMetrics, ZooKeeperStatus } from 'types';
import useInterval from 'lib/hooks/useInterval'; import useInterval from 'lib/hooks/useInterval';
import formatBytes from 'lib/utils/formatBytes';
import cx from 'classnames';
interface Props { interface Props extends BrokerMetrics {
clusterId: string; clusterId: string;
isFetched: boolean; isFetched: boolean;
minDiskUsage: number;
maxDiskUsage: number;
fetchBrokers: (clusterId: ClusterId) => void; fetchBrokers: (clusterId: ClusterId) => void;
fetchBrokerMetrics: (clusterId: ClusterId) => void; fetchBrokerMetrics: (clusterId: ClusterId) => void;
} }
@ -13,6 +17,17 @@ interface Props {
const Topics: React.FC<Props> = ({ const Topics: React.FC<Props> = ({
clusterId, clusterId,
isFetched, isFetched,
brokerCount,
activeControllers,
zooKeeperStatus,
onlinePartitionCount,
offlinePartitionCount,
underReplicatedPartitionCount,
diskUsageDistribution,
minDiskUsage,
maxDiskUsage,
networkPoolUsage,
requestPoolUsage,
fetchBrokers, fetchBrokers,
fetchBrokerMetrics, fetchBrokerMetrics,
}) => { }) => {
@ -27,8 +42,129 @@ const Topics: React.FC<Props> = ({
useInterval(() => { fetchBrokerMetrics(clusterId); }, 5000); useInterval(() => { fetchBrokerMetrics(clusterId); }, 5000);
if (isFetched) { if (isFetched) {
const [minDiskUsageValue, minDiskUsageSize] = formatBytes(minDiskUsage);
const [maxDiskUsageValue, maxDiskUsageSize] = formatBytes(maxDiskUsage);
return ( return (
<div>Brokers of {clusterId}</div> <div className="section">
<div className="box">
<h5 className="title is-5">Uptime</h5>
<div className="level">
<div className="level-item level-left">
<div>
<p className="heading">Total Brokers</p>
<p className="title">{brokerCount}</p>
</div>
</div>
<div className="level-item level-left">
<div>
<p className="heading">Active Controllers</p>
<p className="title">{activeControllers}</p>
</div>
</div>
<div className="level-item level-left">
<div>
<p className="heading">Zookeeper Status</p>
<p className="title">
{zooKeeperStatus === ZooKeeperStatus.online ? (
<span className="tag is-primary">Online</span>
) : (
<span className="tag is-danger">Offline</span>
)}
</p>
</div>
</div>
</div>
</div>
<div className="box">
<h5 className="title is-5">Partitions</h5>
<div className="level">
<div className="level-item level-left">
<div>
<p className="heading">Online</p>
<p>
<span className={cx('title', {'has-text-danger': offlinePartitionCount !== 0})}>
{onlinePartitionCount}
</span>
<span className="subtitle"> of {onlinePartitionCount + offlinePartitionCount}</span>
</p>
</div>
</div>
<div className="level-item level-left">
<div>
<p className="heading">Under Replicated</p>
<p className="title">{underReplicatedPartitionCount}</p>
</div>
</div>
<div className="level-item level-left">
<div>
<p className="heading">In Sync Replicas</p>
<p className="title has-text-grey-lighter">Soon</p>
</div>
</div>
<div className="level-item level-left">
<div>
<p className="heading">Out of Sync Replicas</p>
<p className="title has-text-grey-lighter">Soon</p>
</div>
</div>
</div>
</div>
<div className="box">
<h5 className="title is-5">Disk</h5>
<div className="level">
<div className="level-item level-left">
<div>
<p className="heading">Max usage</p>
<p>
<span className="title">{maxDiskUsageValue}</span>
<span className="subtitle"> {maxDiskUsageSize}</span>
</p>
</div>
</div>
<div className="level-item level-left">
<div>
<p className="heading">Min Usage</p>
<p>
<span className="title">{minDiskUsageValue}</span>
<span className="subtitle"> {minDiskUsageSize}</span>
</p>
</div>
</div>
<div className="level-item level-left">
<div>
<p className="heading">Distribution</p>
<p className="title">{diskUsageDistribution}</p>
</div>
</div>
</div>
</div>
<div className="box">
<h5 className="title is-5">System</h5>
<div className="level">
<div className="level-item level-left">
<div>
<p className="heading">Network pool usage</p>
<p className="title">
{Math.round(networkPoolUsage * 10000) / 100}
<span className="subtitle">%</span>
</p>
</div>
</div>
<div className="level-item level-left">
<div>
<p className="heading">Request pool usage</p>
<p className="title">
{Math.round(requestPoolUsage * 10000) / 100}
<span className="subtitle">%</span>
</p>
</div>
</div>
</div>
</div>
</div>
); );
} }

View file

@ -4,7 +4,7 @@ import {
fetchBrokerMetrics, fetchBrokerMetrics,
} from 'redux/reducers/brokers/thunks'; } from 'redux/reducers/brokers/thunks';
import Brokers from './Brokers'; import Brokers from './Brokers';
import { getIsBrokerListFetched } from 'redux/reducers/brokers/selectors'; import * as brokerSelectors from 'redux/reducers/brokers/selectors';
import { RootState, ClusterId } from 'types'; import { RootState, ClusterId } from 'types';
import { RouteComponentProps } from 'react-router-dom'; import { RouteComponentProps } from 'react-router-dom';
@ -15,8 +15,19 @@ interface RouteProps {
interface OwnProps extends RouteComponentProps<RouteProps> { } interface OwnProps extends RouteComponentProps<RouteProps> { }
const mapStateToProps = (state: RootState, { match: { params: { clusterId } }}: OwnProps) => ({ const mapStateToProps = (state: RootState, { match: { params: { clusterId } }}: OwnProps) => ({
isFetched: getIsBrokerListFetched(state), isFetched: brokerSelectors.getIsBrokerListFetched(state),
clusterId, clusterId,
brokerCount: brokerSelectors.getBrokerCount(state),
zooKeeperStatus: brokerSelectors.getZooKeeperStatus(state),
activeControllers: brokerSelectors.getActiveControllers(state),
networkPoolUsage: brokerSelectors.getNetworkPoolUsage(state),
requestPoolUsage: brokerSelectors.getRequestPoolUsage(state),
onlinePartitionCount: brokerSelectors.getOnlinePartitionCount(state),
offlinePartitionCount: brokerSelectors.getOfflinePartitionCount(state),
underReplicatedPartitionCount: brokerSelectors.getUnderReplicatedPartitionCount(state),
diskUsageDistribution: brokerSelectors.getDiskUsageDistribution(state),
minDiskUsage: brokerSelectors.getMinDiskUsage(state),
maxDiskUsage: brokerSelectors.getMaxDiskUsage(state),
}); });
const mapDispatchToProps = { const mapDispatchToProps = {

View file

@ -18,7 +18,6 @@ const List: React.FC<Props> = ({
const items = showInternal ? topics : externalTopics; const items = showInternal ? topics : externalTopics;
return ( return (
<>
<div className="section"> <div className="section">
<div className="box"> <div className="box">
<div className="field"> <div className="field">
@ -55,7 +54,6 @@ const List: React.FC<Props> = ({
</table> </table>
</div> </div>
</div> </div>
</>
); );
} }

View file

@ -8,7 +8,7 @@ import {
BASE_PARAMS, BASE_PARAMS,
} from 'lib/constants'; } from 'lib/constants';
export const getBrokers = (clusterId: ClusterId): Promise<{ brokers: Broker[] }> => export const getBrokers = (clusterId: ClusterId): Promise<Broker[]> =>
fetch(`${BASE_URL}/clusters/${clusterId}/brokers`, { ...BASE_PARAMS }) fetch(`${BASE_URL}/clusters/${clusterId}/brokers`, { ...BASE_PARAMS })
.then(res => res.json()); .then(res => res.json());

View file

@ -9,8 +9,8 @@ export const initialState: BrokersState = {
networkPoolUsage: 0, networkPoolUsage: 0,
requestPoolUsage: 0, requestPoolUsage: 0,
onlinePartitionCount: 0, onlinePartitionCount: 0,
underReplicatedPartitionCount: 0,
offlinePartitionCount: 0, offlinePartitionCount: 0,
underReplicatedPartitionCount: 0,
diskUsageDistribution: undefined, diskUsageDistribution: undefined,
}; };

View file

@ -13,8 +13,22 @@ export const getIsBrokerListFetched = createSelector(
const getBrokerList = createSelector(brokersState, ({ items }) => items); const getBrokerList = createSelector(brokersState, ({ items }) => items);
export const getTotalBrokers = createSelector( export const getBrokerCount = createSelector(brokersState, ({ brokerCount }) => brokerCount);
getIsBrokerListFetched, export const getZooKeeperStatus = createSelector(brokersState, ({ zooKeeperStatus }) => zooKeeperStatus);
export const getActiveControllers = createSelector(brokersState, ({ activeControllers }) => activeControllers);
export const getNetworkPoolUsage = createSelector(brokersState, ({ networkPoolUsage }) => networkPoolUsage);
export const getRequestPoolUsage = createSelector(brokersState, ({ requestPoolUsage }) => requestPoolUsage);
export const getOnlinePartitionCount = createSelector(brokersState, ({ onlinePartitionCount }) => onlinePartitionCount);
export const getOfflinePartitionCount = createSelector(brokersState, ({ offlinePartitionCount }) => offlinePartitionCount);
export const getDiskUsageDistribution = createSelector(brokersState, ({ diskUsageDistribution }) => diskUsageDistribution);
export const getUnderReplicatedPartitionCount = createSelector(brokersState, ({ underReplicatedPartitionCount }) => underReplicatedPartitionCount);
export const getMinDiskUsage = createSelector(
getBrokerList, getBrokerList,
(isFetched, brokers) => (isFetched && brokers !== undefined ? brokers.length : undefined), (brokers) => Math.min(...brokers.map(({ segmentSize }) => segmentSize)),
);
export const getMaxDiskUsage = createSelector(
getBrokerList,
(brokers) => Math.max(...brokers.map(({ segmentSize }) => segmentSize)),
); );

View file

@ -9,8 +9,8 @@ import { PromiseThunk, ClusterId } from 'types';
export const fetchBrokers = (clusterId: ClusterId): PromiseThunk<void> => async (dispatch) => { export const fetchBrokers = (clusterId: ClusterId): PromiseThunk<void> => async (dispatch) => {
dispatch(fetchBrokersAction.request()); dispatch(fetchBrokersAction.request());
try { try {
const { brokers } = await getBrokers(clusterId); const payload = await getBrokers(clusterId);
dispatch(fetchBrokersAction.success(brokers)); dispatch(fetchBrokersAction.success(payload));
} catch (e) { } catch (e) {
dispatch(fetchBrokersAction.failure()); dispatch(fetchBrokersAction.failure());
} }

View file

@ -8,7 +8,7 @@ export interface Broker {
bytesOutPerSec: number; bytesOutPerSec: number;
}; };
export enum ZooKeeperStatus { online, offline }; export enum ZooKeeperStatus { offline, online };
export interface BrokerDiskUsage { export interface BrokerDiskUsage {
brokerId: BrokerId; brokerId: BrokerId;
@ -22,8 +22,8 @@ export interface BrokerMetrics {
networkPoolUsage: number; networkPoolUsage: number;
requestPoolUsage: number; requestPoolUsage: number;
onlinePartitionCount: number; onlinePartitionCount: number;
underReplicatedPartitionCount: number;
offlinePartitionCount: number; offlinePartitionCount: number;
underReplicatedPartitionCount: number;
diskUsageDistribution?: string; diskUsageDistribution?: string;
} }