From b8fc5e00f19f230db4e55cd5bcb6afcff47fef01 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Tue, 14 May 2019 21:37:29 +0900 Subject: [PATCH 1/2] Show NetworkOutNodeExtensionGraph --- server/src/common_rpc_types.rs | 8 + server/src/db/queries/network_usage_graph.rs | 35 +++- server/src/db/service.rs | 38 +++- server/src/frontend/api.rs | 22 ++- server/src/frontend/types.rs | 10 +- ui/src/actions/graph.ts | 73 +++++++- ui/src/components/Graph/Graph.tsx | 23 ++- .../components/Graph/GraphNode/GraphNode.tsx | 23 +++ .../NetworkOutNodeExtensionGraph.scss | 27 +++ .../NetworkOutNodeExtensionGraph.tsx | 165 ++++++++++++++++++ .../NetworkOutAllGraph/NetworkOutAllGraph.tsx | 15 +- ui/src/reducers/graph.ts | 35 +++- ui/src/requests/types.ts | 6 + 13 files changed, 468 insertions(+), 12 deletions(-) create mode 100644 ui/src/components/Graph/GraphNode/GraphNode.tsx create mode 100644 ui/src/components/Graph/GraphNode/NetworkOutNodeExtensionGraph/NetworkOutNodeExtensionGraph.scss create mode 100644 ui/src/components/Graph/GraphNode/NetworkOutNodeExtensionGraph/NetworkOutNodeExtensionGraph.tsx diff --git a/server/src/common_rpc_types.rs b/server/src/common_rpc_types.rs index 5bbd6b8..1c1d89f 100644 --- a/server/src/common_rpc_types.rs +++ b/server/src/common_rpc_types.rs @@ -133,6 +133,14 @@ pub struct GraphNetworkOutAllRow { pub type GraphNetworkOutAllAVGRow = GraphNetworkOutAllRow; +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GraphNetworkOutNodeExtensionRow { + pub extension: String, + pub time: DateTime, + pub value: f32, +} + #[cfg(test)] mod tests { use super::*; diff --git a/server/src/db/queries/network_usage_graph.rs b/server/src/db/queries/network_usage_graph.rs index 6c9eb01..1155ebe 100644 --- a/server/src/db/queries/network_usage_graph.rs +++ b/server/src/db/queries/network_usage_graph.rs @@ -1,4 +1,6 @@ -use common_rpc_types::{GraphCommonArgs, GraphNetworkOutAllRow, GraphPeriod}; +use common_rpc_types::{ + GraphCommonArgs, GraphNetworkOutAllRow, GraphNetworkOutNodeExtensionRow, GraphPeriod, NodeName, +}; use postgres; pub fn query_network_out_all( @@ -69,3 +71,34 @@ pub fn query_network_out_all_avg( }) .collect()) } + +pub fn query_network_out_node_extension( + conn: &postgres::Connection, + node_name: NodeName, + graph_args: GraphCommonArgs, +) -> postgres::Result> { + let query_stmt = format!( + "\ + SELECT \ + extension, \ + {}, \ + CAST (AVG(bytes) AS REAL) as value \ + FROM \"network_usage\" \ + WHERE \"network_usage\".\"time\"<$1 AND \"network_usage\".\"time\">$2 \ + AND \"network_usage\".\"name\"=$3 + GROUP BY \"network_usage\".\"extension\", \"rounded_time\" \ + ORDER BY \"network_usage\".\"extension\", \"rounded_time\" ASC", + get_sql_round_period_expression(graph_args.period) + ); + + let rows = conn.query(&query_stmt, &[&graph_args.to, &graph_args.from, &node_name])?; + + Ok(rows + .into_iter() + .map(|row| GraphNetworkOutNodeExtensionRow { + extension: row.get("extension"), + time: row.get("rounded_time"), + value: row.get("value"), + }) + .collect()) +} diff --git a/server/src/db/service.rs b/server/src/db/service.rs index 1f3cd1e..4ef5bed 100644 --- a/server/src/db/service.rs +++ b/server/src/db/service.rs @@ -13,7 +13,9 @@ use super::super::common_rpc_types::{NodeName, NodeStatus, StructuredLog}; use super::event::{Event, EventSubscriber}; use super::queries; use super::types::{AgentExtra, AgentQueryResult, Connection, Connections, Error as DBError, Log, LogQueryParams}; -use common_rpc_types::{GraphCommonArgs, GraphNetworkOutAllAVGRow, GraphNetworkOutAllRow, NetworkUsage}; +use common_rpc_types::{ + GraphCommonArgs, GraphNetworkOutAllAVGRow, GraphNetworkOutAllRow, GraphNetworkOutNodeExtensionRow, NetworkUsage, +}; use util; #[derive(Debug, Clone)] @@ -32,6 +34,11 @@ pub enum Message { WritePeerCount(NodeName, i32, chrono::DateTime), GetGraphNetworkOutAll(GraphCommonArgs, Sender, DBError>>), GetGraphNetworkOutAllAVG(GraphCommonArgs, Sender, DBError>>), + GetGraphNetworkOutNodeExtension( + NodeName, + GraphCommonArgs, + Sender, DBError>>, + ), } #[derive(Clone)] @@ -149,6 +156,14 @@ impl Service { cerror!("Error at {}", callback_err); } } + Message::GetGraphNetworkOutNodeExtension(node_name, args, callback) => { + let result = service + .get_network_out_node_extension_graph(node_name, args) + .map_err(|err| DBError::Internal(err.to_string())); + if let Err(callback_err) = callback.send(result) { + cerror!("Error at {}", callback_err); + } + } } } }) @@ -339,6 +354,15 @@ impl Service { let rows = queries::network_usage_graph::query_network_out_all_avg(&self.db_conn, args)?; Ok(rows) } + + fn get_network_out_node_extension_graph( + &self, + node_name: NodeName, + args: GraphCommonArgs, + ) -> Result, Box> { + let rows = queries::network_usage_graph::query_network_out_node_extension(&self.db_conn, node_name, args)?; + Ok(rows) + } } impl ServiceSender { @@ -440,4 +464,16 @@ impl ServiceSender { self.sender.send(Message::GetGraphNetworkOutAllAVG(args, tx)).expect("Should success send request"); rx.recv()? } + + pub fn get_network_out_node_extension_graph( + &self, + node_name: NodeName, + args: GraphCommonArgs, + ) -> Result, DBError> { + let (tx, rx) = channel(); + self.sender + .send(Message::GetGraphNetworkOutNodeExtension(node_name, args, tx)) + .expect("should success send request"); + rx.recv()? + } } diff --git a/server/src/frontend/api.rs b/server/src/frontend/api.rs index 466159e..17e3c7c 100644 --- a/server/src/frontend/api.rs +++ b/server/src/frontend/api.rs @@ -4,7 +4,8 @@ use super::super::router::Router; use super::super::rpc::{response, RPCError, RPCResponse}; use super::types::{ Context, DashboardGetNetworkResponse, DashboardNode, GraphNetworkOutAllAVGResponse, GraphNetworkOutAllResponse, - LogGetRequest, LogGetResponse, LogGetTargetsResponse, NodeConnection, NodeGetInfoResponse, + GraphNetworkOutNodeExtensionResponse, LogGetRequest, LogGetResponse, LogGetTargetsResponse, NodeConnection, + NodeGetInfoResponse, }; use common_rpc_types::{GraphCommonArgs, UpdateCodeChainRequest}; @@ -42,6 +43,13 @@ pub fn add_routing(router: &mut Router) { as fn(Context, (GraphCommonArgs,)) -> RPCResponse, ), ); + router.add_route( + "graph_network_out_node_extension", + Box::new( + graph_network_out_node_extension + as fn(Context, (NodeName, GraphCommonArgs)) -> RPCResponse, + ), + ) } fn ping(_: Context) -> RPCResponse { @@ -146,3 +154,15 @@ fn graph_network_out_all_node_avg( rows, }) } + +fn graph_network_out_node_extension( + context: Context, + args: (NodeName, GraphCommonArgs), +) -> RPCResponse { + let (node_name, graph_args) = args; + + let rows = context.db_service.get_network_out_node_extension_graph(node_name, graph_args)?; + response(GraphNetworkOutNodeExtensionResponse { + rows, + }) +} diff --git a/server/src/frontend/types.rs b/server/src/frontend/types.rs index 6987a4e..ecbe4d9 100644 --- a/server/src/frontend/types.rs +++ b/server/src/frontend/types.rs @@ -3,8 +3,8 @@ use std::net::SocketAddr; use super::super::agent; use super::super::common_rpc_types; use super::super::common_rpc_types::{ - BlackList, BlockId, GraphNetworkOutAllAVGRow, GraphNetworkOutAllRow, HardwareInfo, HardwareUsage, NodeName, - NodeStatus, NodeVersion, PendingTransaction, WhiteList, + BlackList, BlockId, GraphNetworkOutAllAVGRow, GraphNetworkOutAllRow, GraphNetworkOutNodeExtensionRow, HardwareInfo, + HardwareUsage, NodeName, NodeStatus, NodeVersion, PendingTransaction, WhiteList, }; use super::super::db; @@ -186,3 +186,9 @@ pub struct GraphNetworkOutAllResponse { pub struct GraphNetworkOutAllAVGResponse { pub rows: Vec, } + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct GraphNetworkOutNodeExtensionResponse { + pub rows: Vec, +} diff --git a/ui/src/actions/graph.ts b/ui/src/actions/graph.ts index 4b57e62..724e877 100644 --- a/ui/src/actions/graph.ts +++ b/ui/src/actions/graph.ts @@ -3,14 +3,17 @@ import { ReducerConfigure } from "../reducers"; import RequestAgent from "../RequestAgent"; import { GraphNetworkOutAllAVGRow, - GraphNetworkOutAllRow + GraphNetworkOutAllRow, + GraphNetworkOutNodeExtensionRow } from "../requests/types"; export type GraphAction = | SetNetworkOutAllGraph | ChangeNetworkOutAllFilters | SetNetworkOutAllAVGGraph - | ChangeNetworkOutAllAVGFilters; + | ChangeNetworkOutAllAVGFilters + | SetNetworkOutNodeExtensionGraph + | ChangeNetworkOutNodeExtensionFilters; export interface SetNetworkOutAllGraph { type: "SetNetworkOutAllGraph"; @@ -123,3 +126,69 @@ export const fetchNetworkOutAllAVGGraph = () => { dispatch(setNetworkOutAllAVGGraph(response.rows)); }; }; + +export interface SetNetworkOutNodeExtensionGraph { + type: "SetNetworkOutNodeExtensionGraph"; + data: GraphNetworkOutNodeExtensionRow[]; +} + +const setNetworkOutNodeExtensionGraph = ( + data: GraphNetworkOutNodeExtensionRow[] +) => ({ + type: "SetNetworkOutNodeExtensionGraph", + data +}); + +export interface ChangeNetworkOutNodeExtensionFilters { + type: "ChangeNetworkOutNodeExtensionFilters"; + data: { + nodeId: string; + time: { + fromTime: number; + toTime: number; + }; + }; +} + +export const changeNetworkOutNodeExtensionFilters = (params: { + nodeId: string; + time: { + fromTime: number; + toTime: number; + }; +}) => { + return async (dispatch: any, getState: () => ReducerConfigure) => { + dispatch({ + type: "ChangeNetworkOutNodeExtensionFilters", + data: { + nodeId: params.nodeId, + time: params.time + } + }); + dispatch(fetchNetworkOutNodeExtensionGraph()); + }; +}; + +export const fetchNetworkOutNodeExtensionGraph = () => { + return async (dispatch: any, getState: () => ReducerConfigure) => { + const response = await RequestAgent.getInstance().call<{ + rows: GraphNetworkOutNodeExtensionRow[]; + }>("graph_network_out_node_extension", [ + getState().graphReducer.networkOutNodeExtensionGraph.nodeId, + { + from: moment + .unix( + getState().graphReducer.networkOutNodeExtensionGraph.time.fromTime + ) + .toISOString(), + to: moment + .unix( + getState().graphReducer.networkOutNodeExtensionGraph.time.toTime + ) + .toISOString(), + period: "minutes5" + } + ]); + dispatch(setNetworkOutNodeExtensionGraph(response.rows)); + }; +}; diff --git a/ui/src/components/Graph/Graph.tsx b/ui/src/components/Graph/Graph.tsx index c94f21e..2b598b4 100644 --- a/ui/src/components/Graph/Graph.tsx +++ b/ui/src/components/Graph/Graph.tsx @@ -1,15 +1,32 @@ import { Component } from "react"; import * as React from "react"; +import { Route } from "react-router"; +import GraphNode from "./GraphNode/GraphNode"; import NetworkOutAllAVGGraph from "./NetworkOutAllAVGGraph/NetworkOutAllAVGGraph"; import NetworkOutAllGraph from "./NetworkOutAllGraph/NetworkOutAllGraph"; -export default class Graph extends Component { +interface Props { + match: any; + history: any; +} + +export default class Graph extends Component { public render() { + const { match } = this.props; + return ( +
+ + +
+ ); + } + + private renderAllNodeGraph = () => { return (
- +
); - } + }; } diff --git a/ui/src/components/Graph/GraphNode/GraphNode.tsx b/ui/src/components/Graph/GraphNode/GraphNode.tsx new file mode 100644 index 0000000..e58a925 --- /dev/null +++ b/ui/src/components/Graph/GraphNode/GraphNode.tsx @@ -0,0 +1,23 @@ +import * as React from "react"; +import NetworkOutNodeExtensionGraph from "./NetworkOutNodeExtensionGraph/NetworkOutNodeExtensionGraph"; + +interface OwnProps { + match: { + params: { + nodeId: string; + }; + }; +} + +export default class GraphNode extends React.Component { + public render() { + const { match } = this.props; + return ( +
+

Node {match.params.nodeId}

+ + +
+ ); + } +} diff --git a/ui/src/components/Graph/GraphNode/NetworkOutNodeExtensionGraph/NetworkOutNodeExtensionGraph.scss b/ui/src/components/Graph/GraphNode/NetworkOutNodeExtensionGraph/NetworkOutNodeExtensionGraph.scss new file mode 100644 index 0000000..2aa6635 --- /dev/null +++ b/ui/src/components/Graph/GraphNode/NetworkOutNodeExtensionGraph/NetworkOutNodeExtensionGraph.scss @@ -0,0 +1,27 @@ +.network-out-node-extension-graph { + background-color: white; + width: 1100px; + padding: 10px; + margin: 10px; + + .plot { + display: block; + } +} + +.to-time { + display: inline-block; + margin: 5px; + + label { + margin-right: 5px; + } +} +.from-time { + display: inline-block; + margin: 5px; + + label { + margin-right: 5px; + } +} diff --git a/ui/src/components/Graph/GraphNode/NetworkOutNodeExtensionGraph/NetworkOutNodeExtensionGraph.tsx b/ui/src/components/Graph/GraphNode/NetworkOutNodeExtensionGraph/NetworkOutNodeExtensionGraph.tsx new file mode 100644 index 0000000..c6f5c58 --- /dev/null +++ b/ui/src/components/Graph/GraphNode/NetworkOutNodeExtensionGraph/NetworkOutNodeExtensionGraph.tsx @@ -0,0 +1,165 @@ +import * as _ from "lodash"; +import moment from "moment"; +import { PlotData } from "plotly.js"; +import { Component } from "react"; +import * as React from "react"; +import DatePicker from "react-datepicker"; +import "react-datepicker/dist/react-datepicker.css"; +import Plot from "react-plotly.js"; +import { connect } from "react-redux"; +import { Label } from "reactstrap"; +import { + changeNetworkOutNodeExtensionFilters, + fetchNetworkOutNodeExtensionGraph +} from "../../../../actions/graph"; +import { ReducerConfigure } from "../../../../reducers"; +import { GraphNetworkOutNodeExtensionRow } from "../../../../requests/types"; +import "./NetworkOutNodeExtensionGraph.css"; + +interface OwnProps { + nodeId: string; +} + +interface StateProps { + fromTime: number; + toTime: number; + data: GraphNetworkOutNodeExtensionRow[]; +} + +interface DispatchProps { + dispatch: any; +} + +type Props = OwnProps & StateProps & DispatchProps; +class NetworkOutNodeExtensionGraph extends Component { + public constructor(props: any) { + super(props); + } + + public componentDidMount(): void { + this.props.dispatch(fetchNetworkOutNodeExtensionGraph()); + } + + public render() { + const { fromTime, toTime } = this.props; + const rowsByExtension = _.groupBy(this.props.data, row => row.extension); + return ( +
+
+ + +
+
+ + +
+
+ >( + rowsByExtension, + (rows, extension) => ({ + x: _.map(rows, row => row.time), + y: _.map(rows, row => row.value), + type: "scatter", + mode: "lines+markers", + name: extension, + showlegend: true + }) + )} + layout={{ + width: 1000, + height: 600, + title: "Network Out by Extension" + }} + /> +
+
+ ); + } + + private handleChangeFromTime = (date: moment.Moment) => { + const nodeId = this.props.nodeId; + this.props.dispatch( + changeNetworkOutNodeExtensionFilters({ + nodeId, + time: { + fromTime: date.unix(), + toTime: this.props.toTime + } + }) + ); + }; + private handleChangeFromTimeRawDate = (event: any) => { + const nodeId = this.props.nodeId; + const newDate = moment(event.target.value); + if (newDate.isValid()) { + this.props.dispatch( + changeNetworkOutNodeExtensionFilters({ + nodeId, + time: { + fromTime: newDate.unix(), + toTime: this.props.toTime + } + }) + ); + } + }; + + private handleChangeToTime = (date: moment.Moment) => { + const nodeId = this.props.nodeId; + this.props.dispatch( + changeNetworkOutNodeExtensionFilters({ + nodeId, + time: { + fromTime: this.props.fromTime, + toTime: date.unix() + } + }) + ); + }; + private handleChangeToTimeRawDate = (event: any) => { + const nodeId = this.props.nodeId; + const newDate = moment(event.target.value); + if (newDate.isValid()) { + this.props.dispatch( + changeNetworkOutNodeExtensionFilters({ + nodeId, + time: { + fromTime: this.props.toTime, + toTime: newDate.unix() + } + }) + ); + } + }; +} + +const mapStateToProps = (state: ReducerConfigure) => { + return { + data: state.graphReducer.networkOutNodeExtensionGraph.data, + fromTime: state.graphReducer.networkOutNodeExtensionGraph.time.fromTime, + toTime: state.graphReducer.networkOutNodeExtensionGraph.time.toTime + }; +}; + +export default connect(mapStateToProps)(NetworkOutNodeExtensionGraph); diff --git a/ui/src/components/Graph/NetworkOutAllGraph/NetworkOutAllGraph.tsx b/ui/src/components/Graph/NetworkOutAllGraph/NetworkOutAllGraph.tsx index 1299b9e..d7532e1 100644 --- a/ui/src/components/Graph/NetworkOutAllGraph/NetworkOutAllGraph.tsx +++ b/ui/src/components/Graph/NetworkOutAllGraph/NetworkOutAllGraph.tsx @@ -16,6 +16,10 @@ import { ReducerConfigure } from "../../../reducers"; import { GraphNetworkOutAllRow } from "../../../requests/types"; import "./NetworkOutAllGraph.css"; +interface OwnProps { + history: any; +} + interface StateProps { fromTime: number; toTime: number; @@ -26,7 +30,7 @@ interface DispatchProps { dispatch: any; } -type Props = StateProps & DispatchProps; +type Props = OwnProps & StateProps & DispatchProps; class NetworkOutAllGraph extends Component { public constructor(props: any) { super(props); @@ -82,6 +86,7 @@ class NetworkOutAllGraph extends Component { showlegend: true }) )} + onLegendClick={this.handleLegendClick} layout={{ width: 1000, height: 600, title: "Network Out All" }} /> @@ -89,6 +94,14 @@ class NetworkOutAllGraph extends Component { ); } + private handleLegendClick = ( + eventData: Readonly + ): boolean => { + const nodeName = eventData.data[eventData.curveNumber].name; + this.props.history.push(`/graph/${nodeName}`); + return false; + }; + private handleChangeFromTime = (date: moment.Moment) => { this.props.dispatch( changeNetworkOutAllFilters({ diff --git a/ui/src/reducers/graph.ts b/ui/src/reducers/graph.ts index 81177b4..505ae3c 100644 --- a/ui/src/reducers/graph.ts +++ b/ui/src/reducers/graph.ts @@ -3,13 +3,15 @@ import { GraphAction } from "../actions/graph"; import NetworkOutAllGraph from "../components/Graph/NetworkOutAllGraph/NetworkOutAllGraph"; import { GraphNetworkOutAllAVGRow, - GraphNetworkOutAllRow + GraphNetworkOutAllRow, + GraphNetworkOutNodeExtensionRow } from "../requests/types"; const merge = require("deepmerge").default; export interface GraphState { networkOutAllGraph: NetworkOutAllGraph; networkOutAllAVGGraph: NetworkOutAllAVGGraph; + networkOutNodeExtensionGraph: NetworkOutNodeExtensionGraph; } export interface NetworkOutAllGraph { @@ -28,6 +30,15 @@ export interface NetworkOutAllAVGGraph { }; } +export interface NetworkOutNodeExtensionGraph { + nodeId: string; + data: GraphNetworkOutNodeExtensionRow[]; + time: { + fromTime: number; + toTime: number; + }; +} + const initialState: GraphState = { networkOutAllGraph: { data: [], @@ -46,6 +57,16 @@ const initialState: GraphState = { .unix(), toTime: moment().unix() } + }, + networkOutNodeExtensionGraph: { + nodeId: "", + data: [], + time: { + fromTime: moment() + .subtract(7, "days") + .unix(), + toTime: moment().unix() + } } }; @@ -71,6 +92,18 @@ export const graphReducer = (state = initialState, action: GraphAction) => { data: action.data } }; + case "ChangeNetworkOutNodeExtensionFilters": + return merge(state, { + networkOutNodeExtensionGraph: action.data + }); + case "SetNetworkOutNodeExtensionGraph": + return { + ...state, + networkOutNodeExtensionGraph: { + ...state.networkOutNodeExtensionGraph, + data: action.data + } + }; } return state; }; diff --git a/ui/src/requests/types.ts b/ui/src/requests/types.ts index cb59093..b8331bc 100644 --- a/ui/src/requests/types.ts +++ b/ui/src/requests/types.ts @@ -106,3 +106,9 @@ export interface GraphNetworkOutAllRow { } export type GraphNetworkOutAllAVGRow = GraphNetworkOutAllRow; + +export interface GraphNetworkOutNodeExtensionRow { + extension: string; + time: string; + value: number; +} From 8a397e0e80e3ea9d48dbcab82dc6ad8fe2cbecd4 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Thu, 16 May 2019 20:53:05 +0900 Subject: [PATCH 2/2] Show NetworkOutNodePeerGraph --- server/src/common_rpc_types.rs | 8 + server/src/db/queries/network_usage_graph.rs | 34 +++- server/src/db/service.rs | 34 +++- server/src/frontend/api.rs | 25 ++- server/src/frontend/types.rs | 11 +- ui/src/actions/graph.ts | 67 ++++++- .../components/Graph/GraphNode/GraphNode.tsx | 2 + .../NetworkOutNodePeerGraph.scss | 27 +++ .../NetworkOutNodePeerGraph.tsx | 167 ++++++++++++++++++ ui/src/reducers/graph.ts | 36 +++- ui/src/requests/types.ts | 6 + 11 files changed, 407 insertions(+), 10 deletions(-) create mode 100644 ui/src/components/Graph/GraphNode/NetworkOutNodePeerGraph/NetworkOutNodePeerGraph.scss create mode 100644 ui/src/components/Graph/GraphNode/NetworkOutNodePeerGraph/NetworkOutNodePeerGraph.tsx diff --git a/server/src/common_rpc_types.rs b/server/src/common_rpc_types.rs index 1c1d89f..a4e52b3 100644 --- a/server/src/common_rpc_types.rs +++ b/server/src/common_rpc_types.rs @@ -141,6 +141,14 @@ pub struct GraphNetworkOutNodeExtensionRow { pub value: f32, } +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GraphNetworkOutNodePeerRow { + pub peer: String, + pub time: DateTime, + pub value: f32, +} + #[cfg(test)] mod tests { use super::*; diff --git a/server/src/db/queries/network_usage_graph.rs b/server/src/db/queries/network_usage_graph.rs index 1155ebe..7a7e73e 100644 --- a/server/src/db/queries/network_usage_graph.rs +++ b/server/src/db/queries/network_usage_graph.rs @@ -1,5 +1,6 @@ use common_rpc_types::{ - GraphCommonArgs, GraphNetworkOutAllRow, GraphNetworkOutNodeExtensionRow, GraphPeriod, NodeName, + GraphCommonArgs, GraphNetworkOutAllRow, GraphNetworkOutNodeExtensionRow, GraphNetworkOutNodePeerRow, GraphPeriod, + NodeName, }; use postgres; @@ -102,3 +103,34 @@ pub fn query_network_out_node_extension( }) .collect()) } + +pub fn query_network_out_node_peer( + conn: &postgres::Connection, + node_name: NodeName, + graph_args: GraphCommonArgs, +) -> postgres::Result> { + let query_stmt = format!( + "\ + SELECT \ + \"target_ip\", \ + {}, \ + CAST (AVG(bytes) AS REAL) as value \ + FROM \"network_usage\" \ + WHERE \"network_usage\".\"time\"<$1 AND \"network_usage\".\"time\">$2 \ + AND \"network_usage\".\"name\"=$3 + GROUP BY \"network_usage\".\"target_ip\", \"rounded_time\" \ + ORDER BY \"network_usage\".\"target_ip\", \"rounded_time\" ASC", + get_sql_round_period_expression(graph_args.period) + ); + + let rows = conn.query(&query_stmt, &[&graph_args.to, &graph_args.from, &node_name])?; + + Ok(rows + .into_iter() + .map(|row| GraphNetworkOutNodePeerRow { + peer: row.get("target_ip"), + time: row.get("rounded_time"), + value: row.get("value"), + }) + .collect()) +} diff --git a/server/src/db/service.rs b/server/src/db/service.rs index 4ef5bed..2278395 100644 --- a/server/src/db/service.rs +++ b/server/src/db/service.rs @@ -14,7 +14,8 @@ use super::event::{Event, EventSubscriber}; use super::queries; use super::types::{AgentExtra, AgentQueryResult, Connection, Connections, Error as DBError, Log, LogQueryParams}; use common_rpc_types::{ - GraphCommonArgs, GraphNetworkOutAllAVGRow, GraphNetworkOutAllRow, GraphNetworkOutNodeExtensionRow, NetworkUsage, + GraphCommonArgs, GraphNetworkOutAllAVGRow, GraphNetworkOutAllRow, GraphNetworkOutNodeExtensionRow, + GraphNetworkOutNodePeerRow, NetworkUsage, }; use util; @@ -39,6 +40,7 @@ pub enum Message { GraphCommonArgs, Sender, DBError>>, ), + GetGraphNetworkOutNodePeer(NodeName, GraphCommonArgs, Sender, DBError>>), } #[derive(Clone)] @@ -84,6 +86,7 @@ impl Service { } } + #[allow(clippy::cognitive_complexity)] pub fn run_thread(arg: ServiceNewArg) -> ServiceSender { let (tx, rx) = channel(); let service_sender = ServiceSender::new(tx); @@ -164,6 +167,14 @@ impl Service { cerror!("Error at {}", callback_err); } } + Message::GetGraphNetworkOutNodePeer(node_name, args, callback) => { + let result = service + .get_network_out_node_peer_graph(node_name, args) + .map_err(|err| DBError::Internal(err.to_string())); + if let Err(callback_err) = callback.send(result) { + cerror!("Error at {}", callback_err); + } + } } } }) @@ -363,6 +374,15 @@ impl Service { let rows = queries::network_usage_graph::query_network_out_node_extension(&self.db_conn, node_name, args)?; Ok(rows) } + + fn get_network_out_node_peer_graph( + &self, + node_name: NodeName, + args: GraphCommonArgs, + ) -> Result, Box> { + let rows = queries::network_usage_graph::query_network_out_node_peer(&self.db_conn, node_name, args)?; + Ok(rows) + } } impl ServiceSender { @@ -476,4 +496,16 @@ impl ServiceSender { .expect("should success send request"); rx.recv()? } + + pub fn get_network_out_node_peer_graph( + &self, + node_name: NodeName, + args: GraphCommonArgs, + ) -> Result, DBError> { + let (tx, rx) = channel(); + self.sender + .send(Message::GetGraphNetworkOutNodePeer(node_name, args, tx)) + .expect("Should success send request"); + rx.recv()? + } } diff --git a/server/src/frontend/api.rs b/server/src/frontend/api.rs index 17e3c7c..47f66f4 100644 --- a/server/src/frontend/api.rs +++ b/server/src/frontend/api.rs @@ -4,8 +4,8 @@ use super::super::router::Router; use super::super::rpc::{response, RPCError, RPCResponse}; use super::types::{ Context, DashboardGetNetworkResponse, DashboardNode, GraphNetworkOutAllAVGResponse, GraphNetworkOutAllResponse, - GraphNetworkOutNodeExtensionResponse, LogGetRequest, LogGetResponse, LogGetTargetsResponse, NodeConnection, - NodeGetInfoResponse, + GraphNetworkOutNodeExtensionResponse, GraphNetworkOutNodePeerResponse, LogGetRequest, LogGetResponse, + LogGetTargetsResponse, NodeConnection, NodeGetInfoResponse, }; use common_rpc_types::{GraphCommonArgs, UpdateCodeChainRequest}; @@ -49,7 +49,14 @@ pub fn add_routing(router: &mut Router) { graph_network_out_node_extension as fn(Context, (NodeName, GraphCommonArgs)) -> RPCResponse, ), - ) + ); + router.add_route( + "graph_network_out_node_peer", + Box::new( + graph_network_out_node_peer + as fn(Context, (NodeName, GraphCommonArgs)) -> RPCResponse, + ), + ); } fn ping(_: Context) -> RPCResponse { @@ -166,3 +173,15 @@ fn graph_network_out_node_extension( rows, }) } + +fn graph_network_out_node_peer( + context: Context, + args: (NodeName, GraphCommonArgs), +) -> RPCResponse { + let (node_name, graph_args) = args; + + let rows = context.db_service.get_network_out_node_peer_graph(node_name, graph_args)?; + response(GraphNetworkOutNodePeerResponse { + rows, + }) +} diff --git a/server/src/frontend/types.rs b/server/src/frontend/types.rs index ecbe4d9..1c3138a 100644 --- a/server/src/frontend/types.rs +++ b/server/src/frontend/types.rs @@ -3,8 +3,9 @@ use std::net::SocketAddr; use super::super::agent; use super::super::common_rpc_types; use super::super::common_rpc_types::{ - BlackList, BlockId, GraphNetworkOutAllAVGRow, GraphNetworkOutAllRow, GraphNetworkOutNodeExtensionRow, HardwareInfo, - HardwareUsage, NodeName, NodeStatus, NodeVersion, PendingTransaction, WhiteList, + BlackList, BlockId, GraphNetworkOutAllAVGRow, GraphNetworkOutAllRow, GraphNetworkOutNodeExtensionRow, + GraphNetworkOutNodePeerRow, HardwareInfo, HardwareUsage, NodeName, NodeStatus, NodeVersion, PendingTransaction, + WhiteList, }; use super::super::db; @@ -192,3 +193,9 @@ pub struct GraphNetworkOutAllAVGResponse { pub struct GraphNetworkOutNodeExtensionResponse { pub rows: Vec, } + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct GraphNetworkOutNodePeerResponse { + pub rows: Vec, +} diff --git a/ui/src/actions/graph.ts b/ui/src/actions/graph.ts index 724e877..608ec1b 100644 --- a/ui/src/actions/graph.ts +++ b/ui/src/actions/graph.ts @@ -4,7 +4,8 @@ import RequestAgent from "../RequestAgent"; import { GraphNetworkOutAllAVGRow, GraphNetworkOutAllRow, - GraphNetworkOutNodeExtensionRow + GraphNetworkOutNodeExtensionRow, + GraphNetworkOutNodePeerRow } from "../requests/types"; export type GraphAction = @@ -13,7 +14,9 @@ export type GraphAction = | SetNetworkOutAllAVGGraph | ChangeNetworkOutAllAVGFilters | SetNetworkOutNodeExtensionGraph - | ChangeNetworkOutNodeExtensionFilters; + | ChangeNetworkOutNodeExtensionFilters + | SetNetworkOutNodePeerGraph + | ChangeNetworkOutNodePeerFilters; export interface SetNetworkOutAllGraph { type: "SetNetworkOutAllGraph"; @@ -192,3 +195,63 @@ export const fetchNetworkOutNodeExtensionGraph = () => { dispatch(setNetworkOutNodeExtensionGraph(response.rows)); }; }; + +export interface SetNetworkOutNodePeerGraph { + type: "SetNetworkOutNodePeerGraph"; + data: GraphNetworkOutNodePeerRow[]; +} + +const setNetworkOutNodePeerGraph = (data: GraphNetworkOutNodePeerRow[]) => ({ + type: "SetNetworkOutNodePeerGraph", + data +}); + +export interface ChangeNetworkOutNodePeerFilters { + type: "ChangeNetworkOutNodePeerFilters"; + data: { + nodeId: string; + time: { + fromTime: number; + toTime: number; + }; + }; +} + +export const changeNetworkOutNodePeerFilters = (params: { + nodeId: string; + time: { + fromTime: number; + toTime: number; + }; +}) => { + return async (dispatch: any, getState: () => ReducerConfigure) => { + dispatch({ + type: "ChangeNetworkOutNodePeerFilters", + data: { + nodeId: params.nodeId, + time: params.time + } + }); + dispatch(fetchNetworkOutNodePeerGraph()); + }; +}; + +export const fetchNetworkOutNodePeerGraph = () => { + return async (dispatch: any, getState: () => ReducerConfigure) => { + const response = await RequestAgent.getInstance().call<{ + rows: GraphNetworkOutNodePeerRow[]; + }>("graph_network_out_node_peer", [ + getState().graphReducer.networkOutNodePeerGraph.nodeId, + { + from: moment + .unix(getState().graphReducer.networkOutNodePeerGraph.time.fromTime) + .toISOString(), + to: moment + .unix(getState().graphReducer.networkOutNodePeerGraph.time.toTime) + .toISOString(), + period: "minutes5" + } + ]); + dispatch(setNetworkOutNodePeerGraph(response.rows)); + }; +}; diff --git a/ui/src/components/Graph/GraphNode/GraphNode.tsx b/ui/src/components/Graph/GraphNode/GraphNode.tsx index e58a925..52acbbc 100644 --- a/ui/src/components/Graph/GraphNode/GraphNode.tsx +++ b/ui/src/components/Graph/GraphNode/GraphNode.tsx @@ -1,5 +1,6 @@ import * as React from "react"; import NetworkOutNodeExtensionGraph from "./NetworkOutNodeExtensionGraph/NetworkOutNodeExtensionGraph"; +import NetworkOutNodePeerGraph from "./NetworkOutNodePeerGraph/NetworkOutNodePeerGraph"; interface OwnProps { match: { @@ -17,6 +18,7 @@ export default class GraphNode extends React.Component {

Node {match.params.nodeId}

+ ); } diff --git a/ui/src/components/Graph/GraphNode/NetworkOutNodePeerGraph/NetworkOutNodePeerGraph.scss b/ui/src/components/Graph/GraphNode/NetworkOutNodePeerGraph/NetworkOutNodePeerGraph.scss new file mode 100644 index 0000000..b8cb60e --- /dev/null +++ b/ui/src/components/Graph/GraphNode/NetworkOutNodePeerGraph/NetworkOutNodePeerGraph.scss @@ -0,0 +1,27 @@ +.network-out-node-peer-graph { + background-color: white; + width: 1100px; + padding: 10px; + margin: 10px; + + .plot { + display: block; + } +} + +.to-time { + display: inline-block; + margin: 5px; + + label { + margin-right: 5px; + } +} +.from-time { + display: inline-block; + margin: 5px; + + label { + margin-right: 5px; + } +} diff --git a/ui/src/components/Graph/GraphNode/NetworkOutNodePeerGraph/NetworkOutNodePeerGraph.tsx b/ui/src/components/Graph/GraphNode/NetworkOutNodePeerGraph/NetworkOutNodePeerGraph.tsx new file mode 100644 index 0000000..d29d72b --- /dev/null +++ b/ui/src/components/Graph/GraphNode/NetworkOutNodePeerGraph/NetworkOutNodePeerGraph.tsx @@ -0,0 +1,167 @@ +import * as _ from "lodash"; +import moment from "moment"; +import { PlotData } from "plotly.js"; +import { Component } from "react"; +import * as React from "react"; +import DatePicker from "react-datepicker"; +import "react-datepicker/dist/react-datepicker.css"; +import Plot from "react-plotly.js"; +import { connect } from "react-redux"; +import { Label } from "reactstrap"; +import { changeNetworkOutNodePeerFilters } from "../../../../actions/graph"; +import { ReducerConfigure } from "../../../../reducers"; +import { GraphNetworkOutNodePeerRow } from "../../../../requests/types"; +import "./NetworkOutNodePeerGraph.css"; + +interface OwnProps { + nodeId: string; +} + +interface StateProps { + fromTime: number; + toTime: number; + data: GraphNetworkOutNodePeerRow[]; +} + +interface DispatchProps { + dispatch: any; +} + +type Props = OwnProps & StateProps & DispatchProps; +class NetworkOutNodePeerGraph extends Component { + public constructor(props: any) { + super(props); + } + + public componentDidMount(): void { + changeNetworkOutNodePeerFilters({ + nodeId: this.props.nodeId, + time: { + fromTime: moment() + .subtract(7, "days") + .unix(), + toTime: moment().unix() + } + }); + } + + public render() { + const { fromTime, toTime } = this.props; + const rowsByPeer = _.groupBy(this.props.data, row => row.peer); + return ( +
+
+ + +
+
+ + +
+
+ >(rowsByPeer, (rows, peer) => ({ + x: _.map(rows, row => row.time), + y: _.map(rows, row => row.value), + type: "scatter", + mode: "lines+markers", + name: peer, + showlegend: true + }))} + layout={{ + width: 1000, + height: 600, + title: "Network Out by Peer" + }} + /> +
+
+ ); + } + + private handleChangeFromTime = (date: moment.Moment) => { + const nodeId = this.props.nodeId; + this.props.dispatch( + changeNetworkOutNodePeerFilters({ + nodeId, + time: { + fromTime: date.unix(), + toTime: this.props.toTime + } + }) + ); + }; + private handleChangeFromTimeRawDate = (event: any) => { + const nodeId = this.props.nodeId; + const newDate = moment(event.target.value); + if (newDate.isValid()) { + this.props.dispatch( + changeNetworkOutNodePeerFilters({ + nodeId, + time: { + fromTime: newDate.unix(), + toTime: this.props.toTime + } + }) + ); + } + }; + + private handleChangeToTime = (date: moment.Moment) => { + const nodeId = this.props.nodeId; + this.props.dispatch( + changeNetworkOutNodePeerFilters({ + nodeId, + time: { + fromTime: this.props.fromTime, + toTime: date.unix() + } + }) + ); + }; + private handleChangeToTimeRawDate = (event: any) => { + const nodeId = this.props.nodeId; + const newDate = moment(event.target.value); + if (newDate.isValid()) { + this.props.dispatch( + changeNetworkOutNodePeerFilters({ + nodeId, + time: { + fromTime: this.props.toTime, + toTime: newDate.unix() + } + }) + ); + } + }; +} + +const mapStateToProps = (state: ReducerConfigure) => { + return { + data: state.graphReducer.networkOutNodePeerGraph.data, + fromTime: state.graphReducer.networkOutNodePeerGraph.time.fromTime, + toTime: state.graphReducer.networkOutNodePeerGraph.time.toTime + }; +}; + +export default connect(mapStateToProps)(NetworkOutNodePeerGraph); diff --git a/ui/src/reducers/graph.ts b/ui/src/reducers/graph.ts index 505ae3c..89e5cb2 100644 --- a/ui/src/reducers/graph.ts +++ b/ui/src/reducers/graph.ts @@ -1,10 +1,12 @@ import moment from "moment"; import { GraphAction } from "../actions/graph"; +import NetworkOutNodePeerGraph from "../components/Graph/GraphNode/NetworkOutNodePeerGraph/NetworkOutNodePeerGraph"; import NetworkOutAllGraph from "../components/Graph/NetworkOutAllGraph/NetworkOutAllGraph"; import { GraphNetworkOutAllAVGRow, GraphNetworkOutAllRow, - GraphNetworkOutNodeExtensionRow + GraphNetworkOutNodeExtensionRow, + GraphNetworkOutNodePeerRow } from "../requests/types"; const merge = require("deepmerge").default; @@ -12,6 +14,7 @@ export interface GraphState { networkOutAllGraph: NetworkOutAllGraph; networkOutAllAVGGraph: NetworkOutAllAVGGraph; networkOutNodeExtensionGraph: NetworkOutNodeExtensionGraph; + networkOutNodePeerGraph: NetworkOutNodePeerGraph; } export interface NetworkOutAllGraph { @@ -39,6 +42,15 @@ export interface NetworkOutNodeExtensionGraph { }; } +export interface NetworkOutNodePeerGraph { + nodeId: string; + data: GraphNetworkOutNodePeerRow[]; + time: { + fromTime: number; + toTime: number; + }; +} + const initialState: GraphState = { networkOutAllGraph: { data: [], @@ -67,6 +79,16 @@ const initialState: GraphState = { .unix(), toTime: moment().unix() } + }, + networkOutNodePeerGraph: { + nodeId: "", + data: [], + time: { + fromTime: moment() + .subtract(7, "days") + .unix(), + toTime: moment().unix() + } } }; @@ -104,6 +126,18 @@ export const graphReducer = (state = initialState, action: GraphAction) => { data: action.data } }; + case "ChangeNetworkOutNodePeerFilters": + return merge(state, { + networkOutNodePeerGraph: action.data + }); + case "SetNetworkOutNodePeerGraph": + return { + ...state, + networkOutNodePeerGraph: { + ...state.networkOutNodePeerGraph, + data: action.data + } + }; } return state; }; diff --git a/ui/src/requests/types.ts b/ui/src/requests/types.ts index b8331bc..e3b1512 100644 --- a/ui/src/requests/types.ts +++ b/ui/src/requests/types.ts @@ -112,3 +112,9 @@ export interface GraphNetworkOutNodeExtensionRow { time: string; value: number; } + +export interface GraphNetworkOutNodePeerRow { + peer: string; + time: string; + value: number; +}