diff --git a/lib/GateRRD.py b/lib/GateRRD.py new file mode 100644 index 0000000..fb8d5d4 --- /dev/null +++ b/lib/GateRRD.py @@ -0,0 +1,74 @@ +""" +RRD for gateways +""" + +import os +import subprocess + +from lib.RRD import DS, RRA, RRD + + +class GateRRD(RRD): + ds_list = [ + DS('upstate', 'GAUGE', 120, 0, 1), + DS('clients', 'GAUGE', 120, 0, float('NaN')), + DS('loadavg', 'GAUGE', 120, 0, float('NaN')), + DS('leases', 'GAUGE', 120, 0, float('NaN')), + ] + rra_list = [ + RRA('AVERAGE', 0.5, 1, 120), # 2 hours of 1 minute samples + RRA('AVERAGE', 0.5, 5, 1440), # 5 days of 5 minute samples + RRA('AVERAGE', 0.5, 15, 672), # 7 days of 15 minute samples + RRA('AVERAGE', 0.5, 60, 720), # 30 days of 1 hour samples + RRA('AVERAGE', 0.5, 720, 730), # 1 year of 12 hour samples + ] + + def __init__(self, filename, node=None): + """ + Create a new RRD for a given node. + + If the RRD isn't supposed to be updated, the node can be omitted. + """ + self.node = node + super().__init__(filename) + self.ensure_sanity(self.ds_list, self.rra_list, step=60) + + @property + def imagename(self): + return "{basename}.png".format( + basename=os.path.basename(self.filename).rsplit('.', 2)[0]) + + # TODO: fix this, python does not support function overloading + def update(self): + values = { + 'upstate': int(self.node['flags']['online']), + 'clients': float(self.node['statistics']['clients']), + } + if 'loadavg' in self.node['statistics']: + values['loadavg'] = float(self.node['statistics'].get('loadavg', 0)) + # Gateways can send the peer count. We use the clients field to store data + if 'peers' in self.node['statistics']: + values['clients'] = self.node['statistics']['peers'] + if 'leases' in self.node['statistics']: + values['leases'] = self.node['statistics']['leases'] + super().update(values) + + def graph(self, directory, timeframe): + """ + Create a graph in the given directory. The file will be named + basename.png if the RRD file is named basename.rrd + """ + args = ['rrdtool', 'graph', os.path.join(directory, self.imagename), + '-s', '-' + timeframe, + '-w', '800', + '-h', '400', + '-l', '0', + '-y', '1:1', + 'DEF:clients=' + self.filename + ':clients:AVERAGE', + 'VDEF:maxc=clients,MAXIMUM', + 'CDEF:c=0,clients,ADDNAN', + 'CDEF:d=clients,UN,maxc,UN,1,maxc,IF,*', + 'AREA:c#0F0:up\\l', + 'AREA:d#F00:down\\l', + 'LINE1:c#00F:clients connected\\l'] + subprocess.check_output(args) diff --git a/lib/NodeRRD.py b/lib/NodeRRD.py index 714b0f2..877cc46 100644 --- a/lib/NodeRRD.py +++ b/lib/NodeRRD.py @@ -1,3 +1,7 @@ +""" +RRD for nodes +""" + import os import subprocess diff --git a/lib/rrddb.py b/lib/rrddb.py index a53e456..390c4c6 100644 --- a/lib/rrddb.py +++ b/lib/rrddb.py @@ -4,7 +4,7 @@ import os from lib.GlobalRRD import GlobalRRD from lib.NodeRRD import NodeRRD - +from lib.GateRRD import GateRRD class RRD(object): def __init__(self, @@ -28,9 +28,15 @@ class RRD(object): client_count = sum(map( lambda d: d['statistics']['clients'], online_nodes.values())) + # Refresh global database self.globalDb.update(len(online_nodes), client_count) + + # Refresh databases for all single nodes for node_id, node in online_nodes.items(): - rrd = NodeRRD(os.path.join(self.dbPath, node_id + '.rrd'), node) + if node['flags']['gateway']: + rrd = GateRRD(os.path.join(self.dbPath, node_id + '.rrd'), node) + else: + rrd = NodeRRD(os.path.join(self.dbPath, node_id + '.rrd'), node) rrd.update() def update_images(self):