mirror of https://github.com/MISP/misp-dashboard
Support of unknown number of feeders + display log messages
parent
3d30846662
commit
0a172be26c
102
server.py
102
server.py
|
@ -1,36 +1,52 @@
|
||||||
#!/usr/bin/env python3.5
|
#!/usr/bin/env python3.5
|
||||||
from flask import Flask, render_template, Response
|
from flask import Flask, render_template, Response
|
||||||
import json
|
import json
|
||||||
|
import redis
|
||||||
|
import configparser
|
||||||
from time import gmtime as now
|
from time import gmtime as now
|
||||||
from time import sleep, strftime
|
from time import sleep, strftime
|
||||||
|
import os
|
||||||
|
|
||||||
|
configfile = os.path.join(os.environ['VIRTUAL_ENV'], '../config.cfg')
|
||||||
|
cfg = configparser.ConfigParser()
|
||||||
|
cfg.read(configfile)
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
FIELDNAME = {
|
redis_server = redis.StrictRedis(
|
||||||
'time': 'Time',
|
host=cfg.get('Redis', 'host'),
|
||||||
'level': 'Level',
|
port=cfg.getint('Redis', 'port'),
|
||||||
'source': 'Source',
|
db=cfg.getint('Redis', 'db'))
|
||||||
'name': 'Name',
|
|
||||||
'message': 'Message'
|
|
||||||
}
|
|
||||||
|
|
||||||
FIELDNAME_ORDER = [ 'time', 'level', 'source', 'name', 'message' ]
|
subscriber = redis_server.pubsub(ignore_subscribe_messages=True)
|
||||||
|
subscriber.psubscribe(cfg.get('Redis', 'channel'))
|
||||||
|
eventNumber = 0
|
||||||
|
|
||||||
class LogRow():
|
class LogItem():
|
||||||
def __init__(self, feed='', time='', level='level', src='source', name='name', message='wonderful meesage'):
|
|
||||||
|
FIELDNAME_ORDER = [ 'time', 'level', 'source', 'name', 'message' ]
|
||||||
|
|
||||||
|
#def __init__(self, feed='', time='', level='level', src='source', name='name', message='wonderful meesage'):
|
||||||
|
def __init__(self, feed):
|
||||||
# Parse feed message
|
# Parse feed message
|
||||||
|
|
||||||
# Assign potential supplied values
|
## Assign potential supplied values
|
||||||
self.time = time if time != '' else strftime("%H:%M:%S", now())
|
#self.time = time if time != '' else strftime("%H:%M:%S", now())
|
||||||
self.level = level
|
#self.level = level
|
||||||
self.source = src
|
#self.source = src
|
||||||
self.name = name
|
#self.name = name
|
||||||
self.message = message
|
#self.message = message
|
||||||
|
|
||||||
|
self.time = strftime("%H:%M:%S", now())
|
||||||
|
self.level = 'level'
|
||||||
|
self.source = 'src'
|
||||||
|
self.name = 'name'
|
||||||
|
self.message = feed
|
||||||
|
|
||||||
def get_head_row(self):
|
def get_head_row(self):
|
||||||
to_ret = []
|
to_ret = []
|
||||||
for fn in FIELDNAME_ORDER:
|
for fn in LogItem.FIELDNAME_ORDER:
|
||||||
to_ret.append(FIELDNAME[fn])
|
to_ret.append(fn[0].upper()+fn[1:])
|
||||||
return to_ret
|
return to_ret
|
||||||
|
|
||||||
def get_row(self):
|
def get_row(self):
|
||||||
|
@ -45,26 +61,32 @@ class LogRow():
|
||||||
|
|
||||||
|
|
||||||
class EventMessage():
|
class EventMessage():
|
||||||
|
# Suppose the event message is a json with the format {name: 'feedName', log:'logData'}
|
||||||
def __init__(self, msg):
|
def __init__(self, msg):
|
||||||
self.feed = None
|
msg = msg.decode('utf8')
|
||||||
self.isLog = EventMessage.is_log(msg)
|
try:
|
||||||
|
jsonMsg = json.loads(msg)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
jsonMsg = { 'name': "undefined" ,'log': msg }
|
||||||
|
|
||||||
|
self.feedName = jsonMsg['name']
|
||||||
|
self.feed = jsonMsg['log']
|
||||||
|
self.feed = LogItem(jsonMsg['log']).get_row()
|
||||||
|
|
||||||
#get type of message: log or feed, then create
|
#get type of message: log or feed, then create
|
||||||
if self.isLog:
|
#if self.isLog:
|
||||||
self.feed = msg
|
# self.feed = msg
|
||||||
#FIXME do parser
|
# #FIXME do parser
|
||||||
self.feed = LogRow(feed=msg).get_row()
|
# self.feed = LogItem(feed=msg).get_row()
|
||||||
else:
|
#else:
|
||||||
self.feed = {feed.name: feed.data}
|
# #FIXME do parser
|
||||||
|
# temp = []
|
||||||
def is_log(msg):
|
# for feed in msg:
|
||||||
return True
|
# temp.append(feed.name, feed.data)
|
||||||
|
# self.feed = temp
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
if self.isLog:
|
to_ret = { 'log': self.feed, 'feedName': self.feedName }
|
||||||
to_ret = { 'log': self.feed, 'chart': "" }
|
|
||||||
else:
|
|
||||||
to_ret = { 'log': "", 'chart': self.feed }
|
|
||||||
return 'data: {}\n\n'.format(json.dumps(to_ret))
|
return 'data: {}\n\n'.format(json.dumps(to_ret))
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
|
@ -77,16 +99,12 @@ def logs():
|
||||||
|
|
||||||
@app.route("/_get_log_head")
|
@app.route("/_get_log_head")
|
||||||
def getLogHead():
|
def getLogHead():
|
||||||
return json.dumps(LogRow().get_head_row())
|
return json.dumps(LogItem('').get_head_row())
|
||||||
|
|
||||||
def event_stream():
|
def event_stream():
|
||||||
#for msg in pubsub:
|
for msg in subscriber.listen():
|
||||||
for i in range(3):
|
content = msg['data']
|
||||||
msg = now()
|
yield EventMessage(content).to_json()
|
||||||
sleep(0.3)
|
|
||||||
print('sending', EventMessage(msg).to_json())
|
|
||||||
yield EventMessage(msg).to_json()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(host='localhost', port=8000, debug=True)
|
app.run(host='localhost', port=8000)
|
||||||
|
|
|
@ -1,19 +1,104 @@
|
||||||
|
//color: #5f6062
|
||||||
|
var updateInterval = 1000; // 1s
|
||||||
|
var numPoint = 60;
|
||||||
|
|
||||||
var updateInterval = 30; // 30ms
|
|
||||||
var numPoint = 10;
|
|
||||||
var emptyArray = [];
|
var emptyArray = [];
|
||||||
|
var dataNumLog = [];
|
||||||
for(i=0; i<numPoint; i++) {
|
for(i=0; i<numPoint; i++) {
|
||||||
emptyArray.push(0);
|
dataNumLog.push([i, 0]);
|
||||||
|
emptyArray.push([i, 0]);
|
||||||
}
|
}
|
||||||
var data = { 'undefined': { label: 'undefined', data: [] } };
|
|
||||||
var curMax = { 'undefined': [] };
|
class Sources {
|
||||||
|
constructor() {
|
||||||
|
this._sourcesArray = {};
|
||||||
|
this._sourcesCount = {};
|
||||||
|
this._sourcesCountMax = {};
|
||||||
|
this._globalMax = 0;
|
||||||
|
this._sourceNames = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
addSource(sourceName) {
|
||||||
|
this._sourcesArray[sourceName] = emptyArray;
|
||||||
|
this._sourcesCount[sourceName] = 0;
|
||||||
|
this._sourcesCountMax[sourceName] = 0;
|
||||||
|
this._sourceNames.push(sourceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
addIfNotPresent(sourceName) {
|
||||||
|
if (this._sourceNames.indexOf(sourceName) == -1) {
|
||||||
|
this.addSource(sourceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
incCountOnSource(sourceName) {
|
||||||
|
this._sourcesCount[sourceName] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
resetCountOnSource() {
|
||||||
|
for (var src of this._sourceNames) {
|
||||||
|
this._sourcesCount[src] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slideSource() {
|
||||||
|
var globMax = 0;
|
||||||
|
for (var src of this._sourceNames) {
|
||||||
|
// res[0] = max, res[1] = slidedArray
|
||||||
|
var res = slide(this._sourcesArray[src], this._sourcesCount[src]);
|
||||||
|
// max
|
||||||
|
this._sourcesCountMax[src] = res[0];
|
||||||
|
globMax = globMax > res[0] ? globMax : res[0];
|
||||||
|
// data
|
||||||
|
this._sourcesArray[src] = res[1];
|
||||||
|
}
|
||||||
|
this._globalMax = globMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
toArray() {
|
||||||
|
var to_return = [];
|
||||||
|
for (var src of this._sourceNames) {
|
||||||
|
to_return.push({
|
||||||
|
label: src,
|
||||||
|
data: this._sourcesArray[src]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return to_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getGlobalMax() {
|
||||||
|
return this._globalMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSingleSource(sourceName) {
|
||||||
|
return this._sourcesArray[sourceName];
|
||||||
|
}
|
||||||
|
|
||||||
|
getEmptyData() {
|
||||||
|
return [{label: 'no data', data: emptyArray}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sources = new Sources();
|
||||||
|
sources.addSource('global');
|
||||||
|
|
||||||
|
var curNumLog = 0;
|
||||||
|
var curMaxDataNumLog = 0;
|
||||||
|
|
||||||
var optionsGraph = {
|
var optionsGraph = {
|
||||||
series: {
|
series: {
|
||||||
shadowSize: 0 ,
|
shadowSize: 0 ,
|
||||||
lines: { fill: true, fillColor: { colors: [ { opacity: 1 }, { opacity: 0.1 } ] }}
|
lines: {
|
||||||
|
fill: true,
|
||||||
|
fillColor: {
|
||||||
|
colors: [ { opacity: 1 }, { opacity: 0.1 } ]
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
//colors: ["#2fa1db"],
|
||||||
yaxis: { min: 0, max: 20 },
|
yaxis: { min: 0, max: 20 },
|
||||||
xaxis: { ticks: [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9], [10, 10]] },
|
xaxis: { min: 0, max: numPoint-1 },
|
||||||
|
ticks: numPoint,
|
||||||
grid: {
|
grid: {
|
||||||
tickColor: "#dddddd",
|
tickColor: "#dddddd",
|
||||||
borderWidth: 0
|
borderWidth: 0
|
||||||
|
@ -39,8 +124,7 @@ $(document).ready(function () {
|
||||||
|
|
||||||
source.onmessage = function(event) {
|
source.onmessage = function(event) {
|
||||||
var json = jQuery.parseJSON( event.data );
|
var json = jQuery.parseJSON( event.data );
|
||||||
updateLogTable(json.log);
|
updateLogTable(json.feedName, json.log);
|
||||||
//updateChartData(json.chart);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -50,37 +134,39 @@ $(document).ready(function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
//var plot = $.plot("#placeholder", [ [] ], optionsGraph);
|
//var plot1 = $.plot("#feedDiv1", [ { label: "Number of log messages", data: dataNumLog } ], optionsGraph);
|
||||||
//updateChart()
|
var plot1 = $.plot("#feedDiv1", sources.getEmptyData(), optionsGraph);
|
||||||
|
updateChart()
|
||||||
|
|
||||||
function updateChart() {
|
function updateChart() {
|
||||||
plot.setData(data);
|
updateChart1();
|
||||||
plot.getOptions().yaxes[0].max = curMax[dataset];
|
updateChart2();
|
||||||
plot.setupGrid();
|
setTimeout(updateChart, updateInterval);
|
||||||
plot.draw();
|
|
||||||
setTimeout(update, updateInterval);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateChartData(feed) {
|
function updateChart1() {
|
||||||
if (feed.length == 0)
|
sources.slideSource();
|
||||||
return;
|
sources.resetCountOnSource();
|
||||||
|
plot1.setData(sources.toArray());
|
||||||
|
plot1.getOptions().yaxes[0].max = sources.getGlobalMax();
|
||||||
|
plot1.setupGrid();
|
||||||
|
plot1.draw();
|
||||||
|
}
|
||||||
|
|
||||||
for (feedName in feed) {
|
function updateChart2() {
|
||||||
console.log(feedName.name);
|
|
||||||
if (data[feedName.name] === undefined) {
|
|
||||||
data[feedName.name] = {};
|
|
||||||
}
|
|
||||||
data[feedName.name].data = slide(data[feedName.name].data, feedName.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateLogTable(log) {
|
function updateLogTable(feedName, log) {
|
||||||
if (log.length == 0)
|
if (log.length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Create new row
|
// Create new row
|
||||||
tableBody = document.getElementById('table_log_body');
|
tableBody = document.getElementById('table_log_body');
|
||||||
|
//curNumLog++;
|
||||||
|
sources.addIfNotPresent(feedName);
|
||||||
|
sources.incCountOnSource(feedName);
|
||||||
|
sources.incCountOnSource('global');
|
||||||
createRow(tableBody, log);
|
createRow(tableBody, log);
|
||||||
|
|
||||||
// Remove old row
|
// Remove old row
|
||||||
|
@ -94,10 +180,16 @@ function updateLogTable(log) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function slide(orig, newData) {
|
function slide(orig, newData) {
|
||||||
var slided = orig;
|
var slided = [];
|
||||||
slided.slice(newData.length);
|
var max = newData;
|
||||||
slided.concat(newData);
|
for (i=1; i<orig.length; i++) {
|
||||||
return slided
|
y = orig[i][1];
|
||||||
|
slided.push([i-1, y]);
|
||||||
|
max = y > max ? y : max;
|
||||||
|
}
|
||||||
|
slided.push([orig.length-1, newData]);
|
||||||
|
curMaxDataNumLog = max;
|
||||||
|
return [curMaxDataNumLog, slided];
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRow(tableBody, log) {
|
function createRow(tableBody, log) {
|
||||||
|
|
|
@ -36,14 +36,14 @@
|
||||||
<div id="page-wrapper">
|
<div id="page-wrapper">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-6">
|
||||||
|
|
||||||
<div class="panel panel-default" style="margin-top: 15px;">
|
<div class="panel panel-default" style="margin-top: 15px;">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<i class="fa fa-bar-chart-o fa-fw"></i> Feed
|
<i class="fa fa-bar-chart-o fa-fw"></i> Log feed
|
||||||
</div>
|
</div>
|
||||||
<div id="panelbody" class="panel-body">
|
<div id="panelbody" class="panel-body">
|
||||||
<div id="feedDiv" style="height: 100%; position: relative;"></div>
|
<div id="feedDiv1" style="width:100%; height:300px; position: relative;"></div>
|
||||||
</div>
|
</div>
|
||||||
<!-- /.panel-body -->
|
<!-- /.panel-body -->
|
||||||
|
|
||||||
|
@ -51,7 +51,24 @@
|
||||||
<!-- /.panel -->
|
<!-- /.panel -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- /.col-lg-12 -->
|
<!-- /.col-lg-6 -->
|
||||||
|
<!-- /.col-lg-6 -->
|
||||||
|
<div class="col-lg-6">
|
||||||
|
|
||||||
|
<div class="panel panel-default" style="margin-top: 15px;">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-bar-chart-o fa-fw"></i> Feed
|
||||||
|
</div>
|
||||||
|
<div id="panelbody" class="panel-body">
|
||||||
|
<div id="feedDiv2" style="width:100%; height:300px; position: relative;"></div>
|
||||||
|
</div>
|
||||||
|
<!-- /.panel-body -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- /.panel -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- /.col-lg-6 -->
|
||||||
</div>
|
</div>
|
||||||
<!-- /.row CHART -->
|
<!-- /.row CHART -->
|
||||||
|
|
||||||
|
|
|
@ -4,25 +4,46 @@ import time
|
||||||
import zmq
|
import zmq
|
||||||
import redis
|
import redis
|
||||||
import random
|
import random
|
||||||
|
import configparser
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
|
configfile = os.path.join(os.environ['VIRTUAL_ENV'], '../config.cfg')
|
||||||
|
cfg = configparser.ConfigParser()
|
||||||
|
cfg.read(configfile)
|
||||||
|
|
||||||
|
zmq_url = cfg.get('Redis', 'zmq_url')
|
||||||
zmq_url = "tcp://crf.circl.lu:5556"
|
zmq_url = "tcp://crf.circl.lu:5556"
|
||||||
channel = "102"
|
channel = cfg.get('Redis', 'channel')
|
||||||
context = zmq.Context()
|
context = zmq.Context()
|
||||||
socket = context.socket(zmq.SUB)
|
socket = context.socket(zmq.SUB)
|
||||||
socket.connect(zmq_url)
|
socket.connect(zmq_url)
|
||||||
socket.setsockopt_string(zmq.SUBSCRIBE, channel)
|
socket.setsockopt_string(zmq.SUBSCRIBE, channel)
|
||||||
|
|
||||||
redis_server = redis.StrictRedis(
|
redis_server = redis.StrictRedis(
|
||||||
host='localhost',
|
host=cfg.get('Redis', 'host'),
|
||||||
port=6250,
|
port=cfg.getint('Redis', 'port'),
|
||||||
db=0)
|
db=cfg.getint('Redis', 'db'))
|
||||||
|
|
||||||
# server side
|
# server side
|
||||||
pubsub = redis_server.pubsub(ignore_subscribe_messages=True)
|
pubsub = redis_server.pubsub(ignore_subscribe_messages=True)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
rdm = random.randint(1,3)
|
||||||
|
time.sleep(float(rdm / 10))
|
||||||
|
content = "rdm "+str(rdm)
|
||||||
|
to_send = { 'name': 'feeder'+str(rdm), 'log': content }
|
||||||
|
redis_server.publish(channel, json.dumps(to_send))
|
||||||
|
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
#FIXME check if sock.recv is blocking
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
content = socket.recv()
|
content = socket.recv()
|
||||||
|
console.log('sending')
|
||||||
print(content)
|
print(content)
|
||||||
redis_server.publish(channel, content)
|
redis_server.publish(channel, content)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue