mirror of
https://github.com/wassname/ray.git
synced 2026-07-02 15:40:00 +08:00
Node Info Functional Components (#9073)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
createStyles,
|
||||
makeStyles,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
@@ -7,15 +8,12 @@ import {
|
||||
TableRow,
|
||||
Theme,
|
||||
Typography,
|
||||
withStyles,
|
||||
WithStyles,
|
||||
} from "@material-ui/core";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import React, { useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RayletInfoResponse } from "../../../api";
|
||||
import { sum } from "../../../common/util";
|
||||
import { StoreState } from "../../../store";
|
||||
import {} from "../../../common/tableUtils";
|
||||
import Errors from "./dialogs/errors/Errors";
|
||||
import Logs from "./dialogs/logs/Logs";
|
||||
import NodeRowGroup from "./NodeRowGroup";
|
||||
@@ -39,7 +37,7 @@ const clusterWorkerPids = (
|
||||
return nodeMap;
|
||||
};
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
const useNodeInfoStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
table: {
|
||||
marginTop: theme.spacing(1),
|
||||
@@ -51,187 +49,165 @@ const styles = (theme: Theme) =>
|
||||
paddingRight: theme.spacing(1),
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
const mapStateToProps = (state: StoreState) => ({
|
||||
const nodeInfoSelector = (state: StoreState) => ({
|
||||
nodeInfo: state.dashboard.nodeInfo,
|
||||
rayletInfo: state.dashboard.rayletInfo,
|
||||
});
|
||||
|
||||
type State = {
|
||||
logDialog: { hostname: string; pid: number | null } | null;
|
||||
errorDialog: { hostname: string; pid: number | null } | null;
|
||||
type dialogState = {
|
||||
hostname: string;
|
||||
pid: number | null;
|
||||
} | null;
|
||||
|
||||
const NodeInfo: React.FC<{}> = () => {
|
||||
const [logDialog, setLogDialog] = useState<dialogState>(null);
|
||||
const [errorDialog, setErrorDialog] = useState<dialogState>(null);
|
||||
const classes = useNodeInfoStyles();
|
||||
const { nodeInfo, rayletInfo } = useSelector(nodeInfoSelector);
|
||||
|
||||
if (nodeInfo === null || rayletInfo === null) {
|
||||
return <Typography color="textSecondary">Loading...</Typography>;
|
||||
}
|
||||
|
||||
const logCounts: {
|
||||
[ip: string]: {
|
||||
perWorker: {
|
||||
[pid: string]: number;
|
||||
};
|
||||
total: number;
|
||||
};
|
||||
} = {};
|
||||
|
||||
const errorCounts: {
|
||||
[ip: string]: {
|
||||
perWorker: {
|
||||
[pid: string]: number;
|
||||
};
|
||||
total: number;
|
||||
};
|
||||
} = {};
|
||||
|
||||
// We fetch data about which process IDs are registered with
|
||||
// the cluster's raylet for each node. We use this to filter
|
||||
// the worker data contained in the node info data because
|
||||
// the node info can contain data from more than one cluster
|
||||
// if more than one cluster is running on a machine.
|
||||
const clusterWorkerPidsByIp = clusterWorkerPids(rayletInfo);
|
||||
const clusterTotalWorkers = sum(
|
||||
Array.from(clusterWorkerPidsByIp.values()).map(
|
||||
(workerSet) => workerSet.size,
|
||||
),
|
||||
);
|
||||
// Initialize inner structure of the count objects
|
||||
for (const client of nodeInfo.clients) {
|
||||
const clusterWorkerPids = clusterWorkerPidsByIp.get(client.ip);
|
||||
if (!clusterWorkerPids) {
|
||||
continue;
|
||||
}
|
||||
const filteredLogEntries = Object.entries(
|
||||
nodeInfo.log_counts[client.ip] || {},
|
||||
).filter(([pid, _]) => clusterWorkerPids.has(pid));
|
||||
const totalLogEntries = sum(filteredLogEntries.map(([_, count]) => count));
|
||||
logCounts[client.ip] = {
|
||||
perWorker: Object.fromEntries(filteredLogEntries),
|
||||
total: totalLogEntries,
|
||||
};
|
||||
|
||||
const filteredErrEntries = Object.entries(
|
||||
nodeInfo.error_counts[client.ip] || {},
|
||||
).filter(([pid, _]) => clusterWorkerPids.has(pid));
|
||||
const totalErrEntries = sum(filteredErrEntries.map(([_, count]) => count));
|
||||
errorCounts[client.ip] = {
|
||||
perWorker: Object.fromEntries(filteredErrEntries),
|
||||
total: totalErrEntries,
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Table className={classes.table}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell className={classes.cell} />
|
||||
<TableCell className={classes.cell}>Host</TableCell>
|
||||
<TableCell className={classes.cell}>Workers</TableCell>
|
||||
<TableCell className={classes.cell}>Uptime</TableCell>
|
||||
<TableCell className={classes.cell}>CPU</TableCell>
|
||||
<TableCell className={classes.cell}>RAM</TableCell>
|
||||
<TableCell className={classes.cell}>GPU</TableCell>
|
||||
<TableCell className={classes.cell}>GRAM</TableCell>
|
||||
<TableCell className={classes.cell}>Disk</TableCell>
|
||||
<TableCell className={classes.cell}>Sent</TableCell>
|
||||
<TableCell className={classes.cell}>Received</TableCell>
|
||||
<TableCell className={classes.cell}>Logs</TableCell>
|
||||
<TableCell className={classes.cell}>Errors</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{nodeInfo.clients.map((client) => {
|
||||
const clusterWorkerPids =
|
||||
clusterWorkerPidsByIp.get(client.ip) || new Set();
|
||||
return (
|
||||
<NodeRowGroup
|
||||
key={client.ip}
|
||||
clusterWorkers={client.workers
|
||||
.filter((worker) =>
|
||||
clusterWorkerPids.has(worker.pid.toString()),
|
||||
)
|
||||
.sort((w1, w2) => {
|
||||
if (w2.cmdline[0] === "ray::IDLE") {
|
||||
return -1;
|
||||
}
|
||||
if (w1.cmdline[0] === "ray::IDLE") {
|
||||
return 1;
|
||||
}
|
||||
return w1.pid < w2.pid ? -1 : 1;
|
||||
})}
|
||||
node={client}
|
||||
raylet={
|
||||
client.ip in rayletInfo.nodes
|
||||
? rayletInfo.nodes[client.ip]
|
||||
: null
|
||||
}
|
||||
logCounts={logCounts[client.ip]}
|
||||
errorCounts={errorCounts[client.ip]}
|
||||
setLogDialog={(hostname, pid) =>
|
||||
setLogDialog({ hostname, pid })
|
||||
}
|
||||
setErrorDialog={(hostname, pid) =>
|
||||
setErrorDialog({ hostname, pid })
|
||||
}
|
||||
initialExpanded={nodeInfo.clients.length <= 1}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<TotalRow
|
||||
clusterTotalWorkers={clusterTotalWorkers}
|
||||
nodes={nodeInfo.clients}
|
||||
logCounts={logCounts}
|
||||
errorCounts={errorCounts}
|
||||
/>
|
||||
</TableBody>
|
||||
</Table>
|
||||
{logDialog !== null && (
|
||||
<Logs
|
||||
clearLogDialog={() => setLogDialog(null)}
|
||||
hostname={logDialog.hostname}
|
||||
pid={logDialog.pid}
|
||||
/>
|
||||
)}
|
||||
{errorDialog !== null && (
|
||||
<Errors
|
||||
clearErrorDialog={() => setErrorDialog(null)}
|
||||
hostname={errorDialog.hostname}
|
||||
pid={errorDialog.pid}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
class NodeInfo extends React.Component<
|
||||
WithStyles<typeof styles> & ReturnType<typeof mapStateToProps>
|
||||
> {
|
||||
state: State = {
|
||||
logDialog: null,
|
||||
errorDialog: null,
|
||||
};
|
||||
|
||||
setLogDialog = (hostname: string, pid: number | null) => {
|
||||
this.setState({ logDialog: { hostname, pid } });
|
||||
};
|
||||
|
||||
clearLogDialog = () => {
|
||||
this.setState({ logDialog: null });
|
||||
};
|
||||
|
||||
setErrorDialog = (hostname: string, pid: number | null) => {
|
||||
this.setState({ errorDialog: { hostname, pid } });
|
||||
};
|
||||
|
||||
clearErrorDialog = () => {
|
||||
this.setState({ errorDialog: null });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { classes, nodeInfo, rayletInfo } = this.props;
|
||||
const { logDialog, errorDialog } = this.state;
|
||||
|
||||
if (nodeInfo === null || rayletInfo === null) {
|
||||
return <Typography color="textSecondary">Loading...</Typography>;
|
||||
}
|
||||
|
||||
const logCounts: {
|
||||
[ip: string]: {
|
||||
perWorker: {
|
||||
[pid: string]: number;
|
||||
};
|
||||
total: number;
|
||||
};
|
||||
} = {};
|
||||
|
||||
const errorCounts: {
|
||||
[ip: string]: {
|
||||
perWorker: {
|
||||
[pid: string]: number;
|
||||
};
|
||||
total: number;
|
||||
};
|
||||
} = {};
|
||||
|
||||
// We fetch data about which process IDs are registered with
|
||||
// the cluster's raylet for each node. We use this to filter
|
||||
// the worker data contained in the node info data because
|
||||
// the node info can contain data from more than one cluster
|
||||
// if more than one cluster is running on a machine.
|
||||
const clusterWorkerPidsByIp = clusterWorkerPids(rayletInfo);
|
||||
const clusterTotalWorkers = sum(
|
||||
Array.from(clusterWorkerPidsByIp.values()).map(
|
||||
(workerSet) => workerSet.size,
|
||||
),
|
||||
);
|
||||
// Initialize inner structure of the count objects
|
||||
for (const client of nodeInfo.clients) {
|
||||
const clusterWorkerPids = clusterWorkerPidsByIp.get(client.ip);
|
||||
if (!clusterWorkerPids) {
|
||||
continue;
|
||||
}
|
||||
const filteredLogEntries = Object.entries(
|
||||
nodeInfo.log_counts[client.ip] || {},
|
||||
).filter(([pid, _]) => clusterWorkerPids.has(pid));
|
||||
const totalLogEntries = sum(
|
||||
filteredLogEntries.map(([_, count]) => count),
|
||||
);
|
||||
logCounts[client.ip] = {
|
||||
perWorker: Object.fromEntries(filteredLogEntries),
|
||||
total: totalLogEntries,
|
||||
};
|
||||
|
||||
const filteredErrEntries = Object.entries(
|
||||
nodeInfo.error_counts[client.ip] || {},
|
||||
).filter(([pid, _]) => clusterWorkerPids.has(pid));
|
||||
const totalErrEntries = sum(
|
||||
filteredErrEntries.map(([_, count]) => count),
|
||||
);
|
||||
errorCounts[client.ip] = {
|
||||
perWorker: Object.fromEntries(filteredErrEntries),
|
||||
total: totalErrEntries,
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Table className={classes.table}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell className={classes.cell} />
|
||||
<TableCell className={classes.cell}>Host</TableCell>
|
||||
<TableCell className={classes.cell}>Workers</TableCell>
|
||||
<TableCell className={classes.cell}>Uptime</TableCell>
|
||||
<TableCell className={classes.cell}>CPU</TableCell>
|
||||
<TableCell className={classes.cell}>RAM</TableCell>
|
||||
<TableCell className={classes.cell}>GPU</TableCell>
|
||||
<TableCell className={classes.cell}>GRAM</TableCell>
|
||||
<TableCell className={classes.cell}>Disk</TableCell>
|
||||
<TableCell className={classes.cell}>Sent</TableCell>
|
||||
<TableCell className={classes.cell}>Received</TableCell>
|
||||
<TableCell className={classes.cell}>Logs</TableCell>
|
||||
<TableCell className={classes.cell}>Errors</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{nodeInfo.clients.map((client) => {
|
||||
const clusterWorkerPids =
|
||||
clusterWorkerPidsByIp.get(client.ip) || new Set();
|
||||
return (
|
||||
<NodeRowGroup
|
||||
key={client.ip}
|
||||
clusterWorkers={client.workers
|
||||
.filter((worker) =>
|
||||
clusterWorkerPids.has(worker.pid.toString()),
|
||||
)
|
||||
.sort((w1, w2) => {
|
||||
if (w2.cmdline[0] === "ray::IDLE") {
|
||||
return -1;
|
||||
}
|
||||
if (w1.cmdline[0] === "ray::IDLE") {
|
||||
return 1;
|
||||
}
|
||||
return w1.pid < w2.pid ? -1 : 1;
|
||||
})}
|
||||
node={client}
|
||||
raylet={
|
||||
client.ip in rayletInfo.nodes
|
||||
? rayletInfo.nodes[client.ip]
|
||||
: null
|
||||
}
|
||||
logCounts={logCounts[client.ip]}
|
||||
errorCounts={errorCounts[client.ip]}
|
||||
setLogDialog={this.setLogDialog}
|
||||
setErrorDialog={this.setErrorDialog}
|
||||
initialExpanded={nodeInfo.clients.length <= 1}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<TotalRow
|
||||
clusterTotalWorkers={clusterTotalWorkers}
|
||||
nodes={nodeInfo.clients}
|
||||
logCounts={logCounts}
|
||||
errorCounts={errorCounts}
|
||||
/>
|
||||
</TableBody>
|
||||
</Table>
|
||||
{logDialog !== null && (
|
||||
<Logs
|
||||
clearLogDialog={this.clearLogDialog}
|
||||
hostname={logDialog.hostname}
|
||||
pid={logDialog.pid}
|
||||
/>
|
||||
)}
|
||||
{errorDialog !== null && (
|
||||
<Errors
|
||||
clearErrorDialog={this.clearErrorDialog}
|
||||
hostname={errorDialog.hostname}
|
||||
pid={errorDialog.pid}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(withStyles(styles)(NodeInfo));
|
||||
export default NodeInfo;
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import {
|
||||
createStyles,
|
||||
makeStyles,
|
||||
TableCell,
|
||||
TableRow,
|
||||
Theme,
|
||||
withStyles,
|
||||
WithStyles,
|
||||
} from "@material-ui/core";
|
||||
import AddIcon from "@material-ui/icons/Add";
|
||||
import RemoveIcon from "@material-ui/icons/Remove";
|
||||
import classNames from "classnames";
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
NodeInfoResponse,
|
||||
NodeInfoResponseWorker,
|
||||
@@ -28,7 +27,7 @@ import { NodeSent, WorkerSent } from "./features/Sent";
|
||||
import { NodeUptime, WorkerUptime } from "./features/Uptime";
|
||||
import { NodeWorkers, WorkerWorkers } from "./features/Workers";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
const useNodeRowGroupStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
cell: {
|
||||
padding: theme.spacing(1),
|
||||
@@ -49,12 +48,13 @@ const styles = (theme: Theme) =>
|
||||
fontFamily: "SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace",
|
||||
whiteSpace: "pre",
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
type ArrayType<T> = T extends Array<infer U> ? U : never;
|
||||
type Node = ArrayType<NodeInfoResponse["clients"]>;
|
||||
|
||||
type Props = {
|
||||
type NodeRowGroupProps = {
|
||||
node: Node;
|
||||
clusterWorkers: Array<NodeInfoResponseWorker>;
|
||||
raylet: RayletInfoResponse["nodes"][keyof RayletInfoResponse["nodes"]] | null;
|
||||
@@ -71,118 +71,100 @@ type Props = {
|
||||
initialExpanded: boolean;
|
||||
};
|
||||
|
||||
type State = {
|
||||
expanded: boolean;
|
||||
const NodeRowGroup: React.FC<NodeRowGroupProps> = ({
|
||||
node,
|
||||
raylet,
|
||||
clusterWorkers,
|
||||
logCounts,
|
||||
errorCounts,
|
||||
setLogDialog,
|
||||
setErrorDialog,
|
||||
initialExpanded,
|
||||
}) => {
|
||||
const [expanded, setExpanded] = useState<boolean>(initialExpanded);
|
||||
const toggleExpand = () => setExpanded(!expanded);
|
||||
const classes = useNodeRowGroupStyles();
|
||||
const features = [
|
||||
{ NodeFeature: NodeHost, WorkerFeature: WorkerHost },
|
||||
{
|
||||
NodeFeature: NodeWorkers(clusterWorkers.length),
|
||||
WorkerFeature: WorkerWorkers,
|
||||
},
|
||||
{ NodeFeature: NodeUptime, WorkerFeature: WorkerUptime },
|
||||
{ NodeFeature: NodeCPU, WorkerFeature: WorkerCPU },
|
||||
{ NodeFeature: NodeRAM, WorkerFeature: WorkerRAM },
|
||||
{ NodeFeature: NodeGPU, WorkerFeature: WorkerGPU },
|
||||
{ NodeFeature: NodeGRAM, WorkerFeature: WorkerGRAM },
|
||||
{ NodeFeature: NodeDisk, WorkerFeature: WorkerDisk },
|
||||
{ NodeFeature: NodeSent, WorkerFeature: WorkerSent },
|
||||
{ NodeFeature: NodeReceived, WorkerFeature: WorkerReceived },
|
||||
{
|
||||
NodeFeature: makeNodeLogs(logCounts, setLogDialog),
|
||||
WorkerFeature: makeWorkerLogs(logCounts, setLogDialog),
|
||||
},
|
||||
{
|
||||
NodeFeature: makeNodeErrors(errorCounts, setErrorDialog),
|
||||
WorkerFeature: makeWorkerErrors(errorCounts, setErrorDialog),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TableRow hover>
|
||||
<TableCell
|
||||
className={classNames(classes.cell, classes.expandCollapseCell)}
|
||||
onClick={toggleExpand}
|
||||
>
|
||||
{!expanded ? (
|
||||
<AddIcon className={classes.expandCollapseIcon} />
|
||||
) : (
|
||||
<RemoveIcon className={classes.expandCollapseIcon} />
|
||||
)}
|
||||
</TableCell>
|
||||
{features.map(({ NodeFeature }, index) => (
|
||||
<TableCell className={classes.cell} key={index}>
|
||||
<NodeFeature node={node} />
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
{expanded && (
|
||||
<React.Fragment>
|
||||
{raylet !== null && raylet.extraInfo !== undefined && (
|
||||
<TableRow hover>
|
||||
<TableCell className={classes.cell} />
|
||||
<TableCell
|
||||
className={classNames(classes.cell, classes.extraInfo)}
|
||||
colSpan={features.length}
|
||||
>
|
||||
{raylet.extraInfo}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{clusterWorkers.map((worker, index: number) => {
|
||||
const rayletWorker =
|
||||
raylet?.workersStats.find(
|
||||
(rayletWorker) => worker.pid === rayletWorker.pid,
|
||||
) || null;
|
||||
|
||||
return (
|
||||
<TableRow hover key={index}>
|
||||
<TableCell className={classes.cell} />
|
||||
{features.map(({ WorkerFeature }, index) => (
|
||||
<TableCell className={classes.cell} key={index}>
|
||||
<WorkerFeature
|
||||
node={node}
|
||||
worker={worker}
|
||||
rayletWorker={rayletWorker}
|
||||
/>
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
class NodeRowGroup extends React.Component<
|
||||
Props & WithStyles<typeof styles>,
|
||||
State
|
||||
> {
|
||||
state: State = {
|
||||
expanded: this.props.initialExpanded,
|
||||
};
|
||||
|
||||
toggleExpand = () => {
|
||||
this.setState((state) => ({
|
||||
expanded: !state.expanded,
|
||||
}));
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
classes,
|
||||
node,
|
||||
raylet,
|
||||
clusterWorkers,
|
||||
logCounts,
|
||||
errorCounts,
|
||||
setLogDialog,
|
||||
setErrorDialog,
|
||||
} = this.props;
|
||||
const { expanded } = this.state;
|
||||
const features = [
|
||||
{ NodeFeature: NodeHost, WorkerFeature: WorkerHost },
|
||||
{
|
||||
NodeFeature: NodeWorkers(clusterWorkers.length),
|
||||
WorkerFeature: WorkerWorkers,
|
||||
},
|
||||
{ NodeFeature: NodeUptime, WorkerFeature: WorkerUptime },
|
||||
{ NodeFeature: NodeCPU, WorkerFeature: WorkerCPU },
|
||||
{ NodeFeature: NodeRAM, WorkerFeature: WorkerRAM },
|
||||
{ NodeFeature: NodeGPU, WorkerFeature: WorkerGPU },
|
||||
{ NodeFeature: NodeGRAM, WorkerFeature: WorkerGRAM },
|
||||
{ NodeFeature: NodeDisk, WorkerFeature: WorkerDisk },
|
||||
{ NodeFeature: NodeSent, WorkerFeature: WorkerSent },
|
||||
{ NodeFeature: NodeReceived, WorkerFeature: WorkerReceived },
|
||||
{
|
||||
NodeFeature: makeNodeLogs(logCounts, setLogDialog),
|
||||
WorkerFeature: makeWorkerLogs(logCounts, setLogDialog),
|
||||
},
|
||||
{
|
||||
NodeFeature: makeNodeErrors(errorCounts, setErrorDialog),
|
||||
WorkerFeature: makeWorkerErrors(errorCounts, setErrorDialog),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TableRow hover>
|
||||
<TableCell
|
||||
className={classNames(classes.cell, classes.expandCollapseCell)}
|
||||
onClick={this.toggleExpand}
|
||||
>
|
||||
{!expanded ? (
|
||||
<AddIcon className={classes.expandCollapseIcon} />
|
||||
) : (
|
||||
<RemoveIcon className={classes.expandCollapseIcon} />
|
||||
)}
|
||||
</TableCell>
|
||||
{features.map(({ NodeFeature }, index) => (
|
||||
<TableCell className={classes.cell} key={index}>
|
||||
<NodeFeature node={node} />
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
{expanded && (
|
||||
<React.Fragment>
|
||||
{raylet !== null && raylet.extraInfo !== undefined && (
|
||||
<TableRow hover>
|
||||
<TableCell className={classes.cell} />
|
||||
<TableCell
|
||||
className={classNames(classes.cell, classes.extraInfo)}
|
||||
colSpan={features.length}
|
||||
>
|
||||
{raylet.extraInfo}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{clusterWorkers.map((worker, index: number) => {
|
||||
const rayletWorker =
|
||||
raylet?.workersStats.find(
|
||||
(rayletWorker) => worker.pid === rayletWorker.pid,
|
||||
) || null;
|
||||
|
||||
return (
|
||||
<TableRow hover key={index}>
|
||||
<TableCell className={classes.cell} />
|
||||
{features.map(({ WorkerFeature }, index) => (
|
||||
<TableCell className={classes.cell} key={index}>
|
||||
<WorkerFeature
|
||||
node={node}
|
||||
worker={worker}
|
||||
rayletWorker={rayletWorker}
|
||||
/>
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(NodeRowGroup);
|
||||
export default NodeRowGroup;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import {
|
||||
createStyles,
|
||||
makeStyles,
|
||||
TableCell,
|
||||
TableRow,
|
||||
Theme,
|
||||
WithStyles,
|
||||
withStyles,
|
||||
} from "@material-ui/core";
|
||||
import LayersIcon from "@material-ui/icons/Layers";
|
||||
import React from "react";
|
||||
@@ -22,7 +21,7 @@ import { ClusterSent } from "./features/Sent";
|
||||
import { ClusterUptime } from "./features/Uptime";
|
||||
import { ClusterWorkers } from "./features/Workers";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
const useTotalRowStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
cell: {
|
||||
borderTopColor: theme.palette.divider,
|
||||
@@ -39,9 +38,10 @@ const styles = (theme: Theme) =>
|
||||
fontSize: "1.5em",
|
||||
verticalAlign: "middle",
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
type Props = {
|
||||
type TotalRowProps = {
|
||||
nodes: NodeInfoResponse["clients"];
|
||||
clusterTotalWorkers: number;
|
||||
logCounts: {
|
||||
@@ -58,44 +58,40 @@ type Props = {
|
||||
};
|
||||
};
|
||||
|
||||
class TotalRow extends React.Component<Props & WithStyles<typeof styles>> {
|
||||
render() {
|
||||
const {
|
||||
classes,
|
||||
nodes,
|
||||
clusterTotalWorkers,
|
||||
logCounts,
|
||||
errorCounts,
|
||||
} = this.props;
|
||||
const TotalRow: React.FC<TotalRowProps> = ({
|
||||
nodes,
|
||||
clusterTotalWorkers,
|
||||
logCounts,
|
||||
errorCounts,
|
||||
}) => {
|
||||
const classes = useTotalRowStyles();
|
||||
const features = [
|
||||
{ ClusterFeature: ClusterHost },
|
||||
{ ClusterFeature: ClusterWorkers(clusterTotalWorkers) },
|
||||
{ ClusterFeature: ClusterUptime },
|
||||
{ ClusterFeature: ClusterCPU },
|
||||
{ ClusterFeature: ClusterRAM },
|
||||
{ ClusterFeature: ClusterGPU },
|
||||
{ ClusterFeature: ClusterGRAM },
|
||||
{ ClusterFeature: ClusterDisk },
|
||||
{ ClusterFeature: ClusterSent },
|
||||
{ ClusterFeature: ClusterReceived },
|
||||
{ ClusterFeature: makeClusterLogs(logCounts) },
|
||||
{ ClusterFeature: makeClusterErrors(errorCounts) },
|
||||
];
|
||||
|
||||
const features = [
|
||||
{ ClusterFeature: ClusterHost },
|
||||
{ ClusterFeature: ClusterWorkers(clusterTotalWorkers) },
|
||||
{ ClusterFeature: ClusterUptime },
|
||||
{ ClusterFeature: ClusterCPU },
|
||||
{ ClusterFeature: ClusterRAM },
|
||||
{ ClusterFeature: ClusterGPU },
|
||||
{ ClusterFeature: ClusterGRAM },
|
||||
{ ClusterFeature: ClusterDisk },
|
||||
{ ClusterFeature: ClusterSent },
|
||||
{ ClusterFeature: ClusterReceived },
|
||||
{ ClusterFeature: makeClusterLogs(logCounts) },
|
||||
{ ClusterFeature: makeClusterErrors(errorCounts) },
|
||||
];
|
||||
|
||||
return (
|
||||
<TableRow hover>
|
||||
<TableCell className={classes.cell}>
|
||||
<LayersIcon className={classes.totalIcon} />
|
||||
return (
|
||||
<TableRow hover>
|
||||
<TableCell className={classes.cell}>
|
||||
<LayersIcon className={classes.totalIcon} />
|
||||
</TableCell>
|
||||
{features.map(({ ClusterFeature }, index) => (
|
||||
<TableCell className={classes.cell} key={index}>
|
||||
<ClusterFeature nodes={nodes} />
|
||||
</TableCell>
|
||||
{features.map(({ ClusterFeature }, index) => (
|
||||
<TableCell className={classes.cell} key={index}>
|
||||
<ClusterFeature nodes={nodes} />
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
}
|
||||
))}
|
||||
</TableRow>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(TotalRow);
|
||||
export default TotalRow;
|
||||
|
||||
Reference in New Issue
Block a user