summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorToni Uhlig <matzeton@googlemail.com>2022-01-26 02:34:10 +0100
committerToni Uhlig <matzeton@googlemail.com>2022-01-26 02:34:10 +0100
commit4bae9d03446b814f3690db3e62dc4156972c2e8c (patch)
tree456667864b8115fc45bf7f7ce15bdb58b6931de3 /examples
parent29a1b13e7ac8f20512b7a066c351bad614998f83 (diff)
py-flow-dashboard: added tab layout and event pie chart
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
Diffstat (limited to 'examples')
-rw-r--r--examples/README.md3
-rwxr-xr-xexamples/py-flow-dashboard/flow-dash.py54
-rw-r--r--examples/py-flow-dashboard/plotly_dash.py216
-rw-r--r--examples/py-flow-dashboard/requirements.txt1
4 files changed, 201 insertions, 73 deletions
diff --git a/examples/README.md b/examples/README.md
index 39324fdeb..4c8ec827e 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -29,9 +29,10 @@ A discontinued tty UI nDPId dashboard.
Prints prettyfied information about flow events.
-## py-flow-dash
+## py-flow-dashboard
A realtime web based graph using Plotly/Dash.
+Probably the most informative example.
## py-flow-multiprocess
diff --git a/examples/py-flow-dashboard/flow-dash.py b/examples/py-flow-dashboard/flow-dash.py
index 283a97bb9..e3ac0776d 100755
--- a/examples/py-flow-dashboard/flow-dash.py
+++ b/examples/py-flow-dashboard/flow-dash.py
@@ -31,7 +31,7 @@ def nDPIsrvd_worker_onFlowCleanup(instance, current_flow, global_user_data):
shared_flow_dict['current-guessed-flows'] -= 1
if shared_flow_dict[flow_id]['is_not_detected'] is True:
- shared_flow_dict['current-detected-flows'] -= 1
+ shared_flow_dict['current-not-detected-flows'] -= 1
if shared_flow_dict[flow_id]['is_midstream'] is True:
shared_flow_dict['current-midstream-flows'] -= 1
@@ -49,6 +49,17 @@ def nDPIsrvd_worker_onJsonLineRecvd(json_dict, instance, current_flow, global_us
shared_flow_dict['total-events'] += 1
shared_flow_dict['total-bytes'] = nsock.received_bytes
+ if 'basic_event_name' in json_dict:
+ shared_flow_dict['total-base-events'] += 1
+
+ if 'daemon_event_name' in json_dict:
+ shared_flow_dict['total-daemon-events'] += 1
+
+ if 'packet_event_name' in json_dict and \
+ (json_dict['packet_event_name'] == 'packet' or \
+ json_dict['packet_event_name'] == 'packet-flow'):
+ shared_flow_dict['total-packet-events'] += 1
+
if 'flow_id' not in json_dict:
return True
else:
@@ -56,8 +67,6 @@ def nDPIsrvd_worker_onJsonLineRecvd(json_dict, instance, current_flow, global_us
return False
flow_id = current_flow.flow_id
- # print(json_dict)
-
if flow_id not in shared_flow_dict:
shared_flow_dict[flow_id] = mgr.dict()
shared_flow_dict[flow_id]['is_detected'] = False
@@ -86,10 +95,24 @@ def nDPIsrvd_worker_onJsonLineRecvd(json_dict, instance, current_flow, global_us
if json_dict['flow_event_name'] == 'new':
- pass
+ shared_flow_dict['total-flow-new-events'] += 1
+
+ elif json_dict['flow_event_name'] == 'update':
+
+ shared_flow_dict['total-flow-update-events'] += 1
+
+ elif json_dict['flow_event_name'] == 'end':
+
+ shared_flow_dict['total-flow-end-events'] += 1
+
+ elif json_dict['flow_event_name'] == 'idle':
+
+ shared_flow_dict['total-flow-idle-events'] += 1
elif json_dict['flow_event_name'] == 'guessed':
+ shared_flow_dict['total-flow-guessed-events'] += 1
+
if shared_flow_dict[flow_id]['is_guessed'] is False:
shared_flow_dict['total-guessed-flows'] += 1
shared_flow_dict['current-guessed-flows'] += 1
@@ -97,6 +120,8 @@ def nDPIsrvd_worker_onJsonLineRecvd(json_dict, instance, current_flow, global_us
elif json_dict['flow_event_name'] == 'not-detected':
+ shared_flow_dict['total-flow-not-detected-events'] += 1
+
if shared_flow_dict[flow_id]['is_not_detected'] is False:
shared_flow_dict['total-not-detected-flows'] += 1
shared_flow_dict['current-not-detected-flows'] += 1
@@ -105,6 +130,11 @@ def nDPIsrvd_worker_onJsonLineRecvd(json_dict, instance, current_flow, global_us
elif json_dict['flow_event_name'] == 'detected' or \
json_dict['flow_event_name'] == 'detection-update':
+ if json_dict['flow_event_name'] == 'detection-update':
+ shared_flow_dict['total-flow-detection-update-events'] += 1
+ else:
+ shared_flow_dict['total-flow-detected-events'] += 1
+
if shared_flow_dict[flow_id]['is_detected'] is False:
shared_flow_dict['total-detected-flows'] += 1
shared_flow_dict['current-detected-flows'] += 1
@@ -134,6 +164,8 @@ def nDPIsrvd_worker(address, shared_flow_dict):
if __name__ == '__main__':
argparser = nDPIsrvd.defaultArgumentParser()
+ argparser.add_argument('--listen-address', type=str, default='127.0.0.1', help='Plotly listen address')
+ argparser.add_argument('--listen-port', type=str, default=8050, help='Plotly listen port')
args = argparser.parse_args()
address = nDPIsrvd.validateAddress(args)
@@ -141,6 +173,18 @@ if __name__ == '__main__':
shared_flow_dict = mgr.dict()
shared_flow_dict['total-events'] = 0
+ shared_flow_dict['total-flow-new-events'] = 0
+ shared_flow_dict['total-flow-update-events'] = 0
+ shared_flow_dict['total-flow-end-events'] = 0
+ shared_flow_dict['total-flow-idle-events'] = 0
+ shared_flow_dict['total-flow-detected-events'] = 0
+ shared_flow_dict['total-flow-detection-update-events'] = 0
+ shared_flow_dict['total-flow-guessed-events'] = 0
+ shared_flow_dict['total-flow-not-detected-events'] = 0
+ shared_flow_dict['total-packet-events'] = 0
+ shared_flow_dict['total-base-events'] = 0
+ shared_flow_dict['total-daemon-events'] = 0
+
shared_flow_dict['total-bytes'] = 0
shared_flow_dict['total-flows'] = 0
shared_flow_dict['total-detected-flows'] = 0
@@ -161,7 +205,7 @@ if __name__ == '__main__':
nDPIsrvd_job.start()
web_job = multiprocessing.Process(target=plotly_dash.web_worker,
- args=(shared_flow_dict,))
+ args=(shared_flow_dict, args.listen_address, args.listen_port))
web_job.start()
nDPIsrvd_job.join()
diff --git a/examples/py-flow-dashboard/plotly_dash.py b/examples/py-flow-dashboard/plotly_dash.py
index c4cf59086..4822307c0 100644
--- a/examples/py-flow-dashboard/plotly_dash.py
+++ b/examples/py-flow-dashboard/plotly_dash.py
@@ -1,11 +1,25 @@
import math
import dash
+
+try:
+ from dash import dcc
+except ImportError:
+ import dash_core_components as dcc
+
+try:
+ from dash import html
+except ImportError:
+ import dash_html_components as html
+
+try:
+ from dash import dash_table as dt
+except ImportError:
+ import dash_table as dt
+
from dash.dependencies import Input, Output, State
-import dash_core_components as dcc
-import dash_html_components as html
+
import dash_daq as daq
-import dash_table as dt
import plotly.graph_objects as go
@@ -14,34 +28,67 @@ global shared_flow_dict
app = dash.Dash(__name__)
def generate_box():
- return { \
- 'display': 'flex', 'flex-direction': 'row', \
- 'box-shadow': '0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24)', \
- 'background-color': '#082255' \
+ return {
+ 'display': 'flex', 'flex-direction': 'row',
+ 'background-color': '#082255'
}
def generate_led_display(div_id, label_name):
- return daq.LEDDisplay( \
- id=div_id, \
- label={'label': label_name, 'style': {'color': '#C4CDD5'}}, \
- labelPosition='bottom', \
- value='0', \
- backgroundColor='#082255', \
- color='#C4CDD5', \
+ return daq.LEDDisplay(
+ id=div_id,
+ label={'label': label_name, 'style': {'color': '#C4CDD5'}},
+ labelPosition='bottom',
+ value='0',
+ backgroundColor='#082255',
+ color='#C4CDD5',
)
def generate_gauge(div_id, label_name, max_value=10):
- return daq.Gauge( \
- id=div_id, \
- value=0, \
- label={'label': label_name, 'style': {'color': '#C4CDD5'}}, \
- max=max_value, \
- min=0, \
+ return daq.Gauge(
+ id=div_id,
+ value=0,
+ label={'label': label_name, 'style': {'color': '#C4CDD5'}},
+ max=max_value,
+ min=0,
)
-app.layout = html.Div([
+def build_gauge(key, max_value=100):
+ gauge_max = int(max(max_value,
+ shared_flow_dict[key]))
+ grad_green = [0, int(gauge_max * 1/3)]
+ grad_yellow = [int(gauge_max * 1/3), int(gauge_max * 2/3)]
+ grad_red = [int(gauge_max * 2/3), gauge_max]
+
+ grad_dict = {
+ "gradient":True,
+ "ranges":{
+ "green":grad_green,
+ "yellow":grad_yellow,
+ "red":grad_red
+ }
+ }
+
+ return shared_flow_dict[key], gauge_max, grad_dict
+
+def build_piechart(labels, values):
+ lay = dict(
+ plot_bgcolor = '#082255',
+ paper_bgcolor = '#082255',
+ font={"color": "#fff"},
+ autosize=True,
+ height=250,
+ margin = {'autoexpand': True, 'b': 0, 'l': 0, 'r': 0, 't': 0, 'pad': 0},
+ width = 500,
+ uniformtext_minsize = 12,
+ uniformtext_mode = 'hide',
+ )
+
+ return go.Figure(layout=lay, data=[go.Pie(labels=labels, values=values, textinfo='percent', textposition='inside')])
+
+def generate_tab_flow():
+ return html.Div([
html.Div(children=[
- dcc.Interval(id="default-interval", interval=1 * 2000, n_intervals=0),
+ dcc.Interval(id="tab-flow-default-interval", interval=1 * 2000, n_intervals=0),
html.Div(children=[
@@ -59,6 +106,8 @@ app.layout = html.Div([
config={
'displayModeBar': False,
},
+ figure=build_piechart(['Detected', 'Guessed', 'Not-Detected', 'Unclassified'],
+ [0, 0, 0, 0]),
),
], style={'padding': 10, 'flex': 1}),
@@ -68,6 +117,8 @@ app.layout = html.Div([
config={
'displayModeBar': False,
},
+ figure=build_piechart(['Not Midstream', 'Midstream'],
+ [0, 0]),
),
], style={'padding': 10, 'flex': 1}),
@@ -77,12 +128,14 @@ app.layout = html.Div([
config={
'displayModeBar': False,
},
+ figure=build_piechart(['Not Risky', 'Risky'],
+ [0, 0]),
),
], style={'padding': 10, 'flex': 1}),
], style=generate_box()),
html.Div(children=[
- dcc.Interval(id="graph-interval", interval=4 * 1000, n_intervals=0),
+ dcc.Interval(id="tab-flow-graph-interval", interval=4 * 1000, n_intervals=0),
dcc.Store(id="graph-traces"),
html.Div(children=[
@@ -94,42 +147,53 @@ app.layout = html.Div([
style={'height':'60vh'},
),
], style={'padding': 10, 'flex': 1})
- ], style=generate_box()),
-])
+ ], style=generate_box())
+ ])
-def build_gauge(key, max_value=100):
- gauge_max = int(max(max_value,
- shared_flow_dict[key]))
- grad_green = [0, int(gauge_max * 1/3)]
- grad_yellow = [int(gauge_max * 1/3), int(gauge_max * 2/3)]
- grad_red = [int(gauge_max * 2/3), gauge_max]
-
- grad_dict = \
- { \
- "gradient":True, \
- "ranges":{ \
- "green":grad_green, \
- "yellow":grad_yellow, \
- "red":grad_red \
- } \
- }
-
- return shared_flow_dict[key], gauge_max, grad_dict
+def generate_tab_other():
+ return html.Div([
+ html.Div(children=[
+ dcc.Interval(id="tab-other-default-interval", interval=1 * 2000, n_intervals=0),
-def build_piechart(labels, values):
- lay = dict(
- plot_bgcolor = '#082255',
- paper_bgcolor = '#082255',
- font={"color": "#fff"},
- autosize=True,
- height=250,
- margin = {'autoexpand': False, 'b': 0, 'l': 0, 'r': 0, 't': 0, 'pad': 0},
- width = 500,
- uniformtext_minsize = 12,
- uniformtext_mode = 'hide',
- )
+ html.Div(children=[
+ dcc.Graph(
+ id='piechart-events',
+ config={
+ 'displayModeBar': False,
+ },
+ ),
+ ], style={'padding': 10, 'flex': 1}),
+ ], style=generate_box())
+ ])
+
+TABS_STYLES = {
+ 'height': '34px'
+}
+TAB_STYLE = {
+ 'borderBottom': '1px solid #d6d6d6',
+ 'backgroundColor': '#385285',
+ 'padding': '6px',
+ 'fontWeight': 'bold',
+}
+TAB_SELECTED_STYLE = {
+ 'borderTop': '1px solid #d6d6d6',
+ 'borderBottom': '1px solid #d6d6d6',
+ 'backgroundColor': '#119DFF',
+ 'color': 'white',
+ 'padding': '6px'
+}
- return go.Figure(layout=lay, data=[go.Pie(labels=labels, values=values, textinfo='percent', textposition='inside')])
+app.layout = html.Div([
+ dcc.Tabs(id="tabs-flow-dash", value="tab-flows", children=[
+ dcc.Tab(label="Flow", value="tab-flows", style=TAB_STYLE,
+ selected_style=TAB_SELECTED_STYLE,
+ children=generate_tab_flow()),
+ dcc.Tab(label="Other", value="tab-other", style=TAB_STYLE,
+ selected_style=TAB_SELECTED_STYLE,
+ children=generate_tab_other()),
+ ], style=TABS_STYLES),
+ html.Div(id="tabs-content")
+])
def prettifyBytes(bytes_received):
size_names = ['B', 'KB', 'MB', 'GB', 'TB']
@@ -146,8 +210,8 @@ def prettifyBytes(bytes_received):
Output('piechart-midstream-flows', 'figure'),
Output('piechart-risky-flows', 'figure')],
- inputs=[Input('default-interval', 'n_intervals')])
-def update_led_gauge(n):
+ inputs=[Input('tab-flow-default-interval', 'n_intervals')])
+def tab_flow_update_components(n):
return [[{'key': 'Total JSON Events', 'value': shared_flow_dict['total-events']},
{'key': 'Total JSON Bytes', 'value': prettifyBytes(shared_flow_dict['total-bytes'])},
{'key': 'Total Flows', 'value': shared_flow_dict['total-flows']},
@@ -155,7 +219,7 @@ def update_led_gauge(n):
{'key': 'Total Midstream Flows', 'value': shared_flow_dict['total-midstream-flows']},
{'key': 'Total Guessed Flows', 'value': shared_flow_dict['total-guessed-flows']},
{'key': 'Total Not Detected Flows', 'value': shared_flow_dict['total-not-detected-flows']}],
- build_piechart(['Detected', 'Guessed', 'Undetected', 'Unclassified'],
+ build_piechart(['Detected', 'Guessed', 'Not-Detected', 'Unclassified'],
[shared_flow_dict['current-detected-flows'],
shared_flow_dict['current-guessed-flows'],
shared_flow_dict['current-not-detected-flows'],
@@ -174,10 +238,10 @@ def update_led_gauge(n):
@app.callback(output=[Output('graph-flows', 'figure'),
Output('graph-traces', 'data')],
- inputs=[Input('graph-interval', 'n_intervals'),
- Input('graph-interval', 'interval')],
+ inputs=[Input('tab-flow-graph-interval', 'n_intervals'),
+ Input('tab-flow-graph-interval', 'interval')],
state=[State('graph-traces', 'data')])
-def update_graph(n, i, traces):
+def tab_flow_update_graph(n, i, traces):
if traces is None:
traces = ([], [], [], [], [], [])
@@ -233,8 +297,8 @@ def update_graph(n, i, traces):
)
fig = go.Figure(layout=lay)
- fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='#007ACE', zeroline=True, zerolinewidth=1)
- fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='#007ACE', zeroline=True, zerolinewidth=1)
+ fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='#004D80', zeroline=True, zerolinewidth=1)
+ fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='#004D80', zeroline=True, zerolinewidth=1)
fig.add_trace(go.Scatter(
x=x,
y=traces[0],
@@ -258,7 +322,7 @@ def update_graph(n, i, traces):
fig.add_trace(go.Scatter(
x=x,
y=traces[4],
- name='Current Not Detected Flows',
+ name='Current Not-Detected Flows',
))
fig.add_trace(go.Scatter(
x=x,
@@ -268,9 +332,27 @@ def update_graph(n, i, traces):
return [fig, traces]
-def web_worker(mp_shared_flow_dict):
+@app.callback(output=[Output('piechart-events', 'figure')],
+ inputs=[Input('tab-other-default-interval', 'n_intervals')])
+def tab_other_update_components(n):
+ return [build_piechart(['Base', 'Daemon', 'Packet',
+ 'Flow New', 'Flow Update', 'Flow End', 'Flow Idle',
+ 'Flow Detection', 'Flow Detection-Updates', 'Flow Guessed', 'Flow Not-Detected'],
+ [shared_flow_dict['total-base-events'],
+ shared_flow_dict['total-daemon-events'],
+ shared_flow_dict['total-packet-events'],
+ shared_flow_dict['total-flow-new-events'],
+ shared_flow_dict['total-flow-update-events'],
+ shared_flow_dict['total-flow-end-events'],
+ shared_flow_dict['total-flow-idle-events'],
+ shared_flow_dict['total-flow-detected-events'],
+ shared_flow_dict['total-flow-detection-update-events'],
+ shared_flow_dict['total-flow-guessed-events'],
+ shared_flow_dict['total-flow-not-detected-events']])]
+
+def web_worker(mp_shared_flow_dict, listen_host, listen_port):
global shared_flow_dict
shared_flow_dict = mp_shared_flow_dict
- app.run_server(debug=False)
+ app.run_server(debug=False, host=listen_host, port=listen_port)
diff --git a/examples/py-flow-dashboard/requirements.txt b/examples/py-flow-dashboard/requirements.txt
index a2544f7ec..ea32550e5 100644
--- a/examples/py-flow-dashboard/requirements.txt
+++ b/examples/py-flow-dashboard/requirements.txt
@@ -1 +1,2 @@
dash
+dash_daq