BrokersList.tsx 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import React from 'react';
  2. import { ClusterName } from 'redux/interfaces';
  3. import { useNavigate } from 'react-router-dom';
  4. import PageHeading from 'components/common/PageHeading/PageHeading';
  5. import * as Metrics from 'components/common/Metrics';
  6. import useAppParams from 'lib/hooks/useAppParams';
  7. import { useBrokers } from 'lib/hooks/api/brokers';
  8. import { useClusterStats } from 'lib/hooks/api/clusters';
  9. import Table, { LinkCell, SizeCell } from 'components/common/NewTable';
  10. import CheckMarkRoundIcon from 'components/common/Icons/CheckMarkRoundIcon';
  11. import { ColumnDef } from '@tanstack/react-table';
  12. import { clusterBrokerPath } from 'lib/paths';
  13. import Tooltip from 'components/common/Tooltip/Tooltip';
  14. import ColoredCell from 'components/common/NewTable/ColoredCell';
  15. import SkewHeader from './SkewHeader/SkewHeader';
  16. import * as S from './BrokersList.styled';
  17. const NA = 'N/A';
  18. const BrokersList: React.FC = () => {
  19. const navigate = useNavigate();
  20. const { clusterName } = useAppParams<{ clusterName: ClusterName }>();
  21. const { data: clusterStats = {} } = useClusterStats(clusterName);
  22. const { data: brokers } = useBrokers(clusterName);
  23. const {
  24. brokerCount,
  25. activeControllers,
  26. onlinePartitionCount,
  27. offlinePartitionCount,
  28. inSyncReplicasCount,
  29. outOfSyncReplicasCount,
  30. underReplicatedPartitionCount,
  31. diskUsage,
  32. version,
  33. } = clusterStats;
  34. const rows = React.useMemo(() => {
  35. let brokersResource;
  36. if (!diskUsage || !diskUsage?.length) {
  37. brokersResource =
  38. brokers?.map((broker) => {
  39. return {
  40. brokerId: broker.id,
  41. segmentSize: NA,
  42. segmentCount: NA,
  43. };
  44. }) || [];
  45. } else {
  46. brokersResource = diskUsage;
  47. }
  48. return brokersResource.map(({ brokerId, segmentSize, segmentCount }) => {
  49. const broker = brokers?.find(({ id }) => id === brokerId);
  50. return {
  51. brokerId,
  52. size: segmentSize || NA,
  53. count: segmentCount || NA,
  54. port: broker?.port,
  55. host: broker?.host,
  56. partitionsLeader: broker?.partitionsLeader,
  57. partitionsSkew: broker?.partitionsSkew,
  58. leadersSkew: broker?.leadersSkew,
  59. inSyncPartitions: broker?.inSyncPartitions,
  60. };
  61. });
  62. }, [diskUsage, brokers]);
  63. const columns = React.useMemo<ColumnDef<(typeof rows)[number]>[]>(
  64. () => [
  65. {
  66. header: 'Broker ID',
  67. accessorKey: 'brokerId',
  68. // eslint-disable-next-line react/no-unstable-nested-components
  69. cell: ({ row: { id }, getValue }) => (
  70. <S.RowCell>
  71. <LinkCell
  72. value={`${getValue<string | number>()}`}
  73. to={encodeURIComponent(`${getValue<string | number>()}`)}
  74. />
  75. {id === String(activeControllers) && (
  76. <Tooltip
  77. value={<CheckMarkRoundIcon />}
  78. content="Active Controller"
  79. placement="right"
  80. />
  81. )}
  82. </S.RowCell>
  83. ),
  84. },
  85. {
  86. header: 'Disk usage',
  87. accessorKey: 'size',
  88. // eslint-disable-next-line react/no-unstable-nested-components
  89. cell: ({ getValue, table, cell, column, renderValue, row }) =>
  90. getValue() === NA ? (
  91. NA
  92. ) : (
  93. <SizeCell
  94. table={table}
  95. column={column}
  96. row={row}
  97. cell={cell}
  98. getValue={getValue}
  99. renderValue={renderValue}
  100. renderSegments
  101. precision={2}
  102. />
  103. ),
  104. },
  105. {
  106. // eslint-disable-next-line react/no-unstable-nested-components
  107. header: () => <SkewHeader />,
  108. accessorKey: 'partitionsSkew',
  109. // eslint-disable-next-line react/no-unstable-nested-components
  110. cell: ({ getValue }) => {
  111. const value = getValue<number>();
  112. return (
  113. <ColoredCell
  114. value={value ? `${value.toFixed(2)}%` : '-'}
  115. warn={value >= 10 && value < 20}
  116. attention={value >= 20}
  117. />
  118. );
  119. },
  120. },
  121. { header: 'Leaders', accessorKey: 'partitionsLeader' },
  122. {
  123. header: 'Leader skew',
  124. accessorKey: 'leadersSkew',
  125. // eslint-disable-next-line react/no-unstable-nested-components
  126. cell: ({ getValue }) => {
  127. const value = getValue<number>();
  128. return (
  129. <ColoredCell
  130. value={value ? `${value.toFixed(2)}%` : '-'}
  131. warn={value >= 10 && value < 20}
  132. attention={value >= 20}
  133. />
  134. );
  135. },
  136. },
  137. {
  138. header: 'Online partitions',
  139. accessorKey: 'inSyncPartitions',
  140. // eslint-disable-next-line react/no-unstable-nested-components
  141. cell: ({ getValue, row }) => {
  142. const value = getValue<number>();
  143. return (
  144. <ColoredCell
  145. value={value}
  146. attention={value !== row.original.count}
  147. />
  148. );
  149. },
  150. },
  151. { header: 'Port', accessorKey: 'port' },
  152. {
  153. header: 'Host',
  154. accessorKey: 'host',
  155. },
  156. ],
  157. []
  158. );
  159. const replicas = (inSyncReplicasCount ?? 0) + (outOfSyncReplicasCount ?? 0);
  160. const areAllInSync = inSyncReplicasCount && replicas === inSyncReplicasCount;
  161. const partitionIsOffline = offlinePartitionCount && offlinePartitionCount > 0;
  162. const isActiveControllerUnKnown = typeof activeControllers === 'undefined';
  163. return (
  164. <>
  165. <PageHeading text="Brokers" />
  166. <Metrics.Wrapper>
  167. <Metrics.Section title="Uptime">
  168. <Metrics.Indicator label="Broker Count">
  169. {brokerCount}
  170. </Metrics.Indicator>
  171. <Metrics.Indicator
  172. label="Active Controller"
  173. isAlert={isActiveControllerUnKnown}
  174. >
  175. {isActiveControllerUnKnown ? (
  176. <S.DangerText>No Active Controller</S.DangerText>
  177. ) : (
  178. activeControllers
  179. )}
  180. </Metrics.Indicator>
  181. <Metrics.Indicator label="Version">{version}</Metrics.Indicator>
  182. </Metrics.Section>
  183. <Metrics.Section title="Partitions">
  184. <Metrics.Indicator
  185. label="Online"
  186. isAlert
  187. alertType={partitionIsOffline ? 'error' : 'success'}
  188. >
  189. {partitionIsOffline ? (
  190. <Metrics.RedText>{onlinePartitionCount}</Metrics.RedText>
  191. ) : (
  192. onlinePartitionCount
  193. )}
  194. <Metrics.LightText>
  195. {` of ${
  196. (onlinePartitionCount || 0) + (offlinePartitionCount || 0)
  197. }
  198. `}
  199. </Metrics.LightText>
  200. </Metrics.Indicator>
  201. <Metrics.Indicator
  202. label="URP"
  203. title="Under replicated partitions"
  204. isAlert
  205. alertType={!underReplicatedPartitionCount ? 'success' : 'error'}
  206. >
  207. {!underReplicatedPartitionCount ? (
  208. <Metrics.LightText>
  209. {underReplicatedPartitionCount}
  210. </Metrics.LightText>
  211. ) : (
  212. <Metrics.RedText>{underReplicatedPartitionCount}</Metrics.RedText>
  213. )}
  214. </Metrics.Indicator>
  215. <Metrics.Indicator
  216. label="In Sync Replicas"
  217. isAlert
  218. alertType={areAllInSync ? 'success' : 'error'}
  219. >
  220. {areAllInSync ? (
  221. replicas
  222. ) : (
  223. <Metrics.RedText>{inSyncReplicasCount}</Metrics.RedText>
  224. )}
  225. <Metrics.LightText> of {replicas}</Metrics.LightText>
  226. </Metrics.Indicator>
  227. <Metrics.Indicator label="Out Of Sync Replicas">
  228. {outOfSyncReplicasCount}
  229. </Metrics.Indicator>
  230. </Metrics.Section>
  231. </Metrics.Wrapper>
  232. <Table
  233. columns={columns}
  234. data={rows}
  235. enableSorting
  236. onRowClick={({ original: { brokerId } }) =>
  237. navigate(clusterBrokerPath(clusterName, brokerId))
  238. }
  239. emptyMessage="No clusters are online"
  240. />
  241. </>
  242. );
  243. };
  244. export default BrokersList;