#!/usr/bin/env python3 import subprocess import time import os from RRD import RRD, DS, RRA class rrd: def __init__( self , databaseDirectory , imagePath , displayTimeGlobal = "7d" , displayTimeNode = "1d" ): self.dbPath = databaseDirectory self.globalDb = RRD(databaseDirectory + "/nodes.rrd") self.imagePath = imagePath self.displayTimeGlobal = displayTimeGlobal self.displayTimeNode = displayTimeNode self.currentTimeInt = (int(time.time())/60)*60 self.currentTime = str(self.currentTimeInt) try: os.stat(self.imagePath) except: os.mkdir(self.imagePath) def updateGlobalDatabase(self,nodeCount,clientCount): """ Adds a new (#Nodes,#Clients) entry to the global database. """ self.globalDb.ensureSanity( [ # Number of nodes available DS('nodes', 'GAUGE', 120, 0, float('NaN')), # Number of client available DS('clients', 'GAUGE', 120, 0, float('NaN')), ], [ RRA('LAST', 0, 1, 44640), RRA('LAST', 0, 60, 744), RRA('LAST', 0, 1440, 1780), ], step=60, ) self.globalDb.update({'nodes': nodeCount, 'clients': clientCount}) def createGlobalGraph(self): nodeGraph = self.imagePath + "/" + "globalGraph.png" args = ["rrdtool", 'graph', nodeGraph, '-s', '-' + self.displayTimeGlobal, '-w', '800', '-h' '400' ,'DEF:nodes=' + self.globalDb.filename + ':nodes:AVERAGE', 'LINE1:nodes#F00:nodes\\l' ,'DEF:clients=' + self.globalDb.filename + ':clients:AVERAGE','LINE2:clients#00F:clients' ] subprocess.check_output(args) def nodeMACToRRDFile(self,nodeMAC): return self.dbPath + "/" + str(nodeMAC).replace(":","") + ".rrd" def nodeMACToPNGFile(self,nodeMAC): return self.imagePath + "/" + str(nodeMAC).replace(":","") + ".png" # Call only if node is up def updateNodeDatabase(self,nodePrimaryMAC,clientCount): # TODO check for bad nodeNames nodeFile = self.nodeMACToRRDFile(nodePrimaryMAC) nodeRRD = RRD(nodeFile) nodeRRD.ensureSanity( [ DS('upstate', 'GAUGE', 120, 0, 1), DS('clients', 'GAUGE', 120, 0, float('NaN')), ], [ RRA('LAST', 0, 1, 44640), ], step=60, ) # Update Global RRDatabase args = ["rrdtool",'updatev', nodeFile # #Upstate #Clients , self.currentTime + ":"+str(1)+":"+str(clientCount) ] subprocess.check_output(args) def createNodeGraph(self,nodePrimaryMAC): nodeGraph = self.nodeMACToPNGFile(nodePrimaryMAC) nodeFile = self.nodeMACToRRDFile(nodePrimaryMAC) args = ['rrdtool','graph', nodeGraph, '-s', '-' + self.displayTimeNode , '-w', '800', '-h', '400', '-l', '0', '-y', '1:1', 'DEF:clients=' + nodeFile + ':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) def update_database(self,db): nodes = {} clientCount = 0 for node in db.get_nodes(): if node.flags['online']: if not node.flags['client']: nodes[node.id] = node node.clients = 0; if 'legacy' in node.flags and node.flags['legacy']: clientCount -= 1 else: clientCount += 1 for link in db.get_links(): source = link.source.interface target = link.target.interface if source in nodes and not target in nodes: nodes[source].clients += 1 elif target in nodes and not source in nodes: nodes[target].clients += 1 self.updateGlobalDatabase(len(nodes),clientCount) for mac in nodes: self.updateNodeDatabase(mac,nodes[mac].clients) def update_images(self): """ Creates a image for every rrd file in the database directory. """ self.createGlobalGraph() nodeDbFiles = os.listdir(self.dbPath) for fileName in nodeDbFiles: if not os.path.isfile(os.path.join(self.dbPath, fileName)): continue nodeName = os.path.basename(fileName).split('.') if nodeName[1] == 'rrd' and not nodeName[0] == "nodes": self.createNodeGraph(nodeName[0])