# -*- coding: utf-8 -*-
import os.path
from subprocess import Popen, PIPE
from rrdtool import *
from settings import *
# py-rrdtool lacks this class
class Cdef:
def __init__(self, name, expression):
self._name = name
self._expression = expression
def __str__(self):
return "CDEF:%s=%s" % (self._name, self._expression)
class TrafficRRD(object):
"""rrd for storing bandwidth information"""
fname_pattern = os.path.join(LIB_DIR, "%s.rrd")
graph_pattern = os.path.join(OUTPUT_DIR, "%s-%s.png")
__in_command = 'ifconfig %s |grep bytes|cut -d":" -f2|cut -d" " -f1'
__out_command = 'ifconfig %s |grep bytes|cut -d":" -f3|cut -d" " -f1'
def __init__(self, interface, label=''):
self.interface = interface
self.label = label
self.fname = self.fname_pattern % (self.interface)
self.rrd = RoundRobinDatabase(self.fname)
if not os.path.exists(os.path.join(LIB_DIR, self.fname)):
self.rrd.create(
DataSource("in", type=DeriveDST, heartbeat=600, min=0, max=12500000),
DataSource("out", type=DeriveDST, heartbeat=600, min=0, max=12500000),
RoundRobinArchive(cf=AverageCF, xff=0.5, steps=1, rows=576), # last 48 hours, every 5 mins
RoundRobinArchive(cf=AverageCF, xff=0.5, steps=6, rows=672), # last 2 weeks, 30 mins average
RoundRobinArchive(cf=AverageCF, xff=0.5, steps=24, rows=732), # last 2 months, 2 hours average
RoundRobinArchive(cf=AverageCF, xff=0.5, steps=144, rows=1460), # last 2 years, 12 hours average
step=300
)
def get_data(self):
in_bytes = Popen(self.__in_command % (self.interface,), stdout=PIPE, shell=True).communicate()[0].strip()
out_bytes = Popen(self.__out_command % (self.interface,), stdout=PIPE, shell=True).communicate()[0].strip()
if not (in_bytes and out_bytes):
raise ValueError("Error getting interface stats for %s", self.interface)
print "Values %s for %s" % ((in_bytes, out_bytes), self.interface)
return (in_bytes, out_bytes)
def update(self, values=None, timestamp=None):
"""update data in rrd with given values or current, if None"""
try:
if not values:
values = self.get_data()
if timestamp:
kwargs = {'timestamp': timestamp}
else:
kwargs = {}
template = ("in", "out")
self.rrd.update(Val(*values, **kwargs), template=template)
return self
except ValueError, e:
print "updating %s failed (%s)" % (self.interface, e.message,)
def graph(self):
for period in ['day', 'week', 'month', 'year']:
self._graph(period)
return self
def _graph(self, period):
"""create a graph for the given period"""
title = "Traffic on %s" % (self.interface,)
if self.label:
title += " :: " + self.label
graph = RoundRobinGraph(self.graph_pattern % (self.interface, period))
graph.graph(
Def("in", self.fname, data_source="in", cf=AverageCF),
Def("out", self.fname, data_source="out", cf=AverageCF),
Cdef("out_neg", "out,-1,*"),
AREA("in", rrggbb="32CD32", legend="Incoming"),
LINE1("in", rrggbb="336600"),
GPRINT("in", cf=MaxCF, format=r" Max\: %5.1lf %S"),
GPRINT("in", cf=AverageCF, format=r" Avg\: %5.1lf %S"),
GPRINT("in", cf=LastCF, format=r" Current\: %5.1lf %Sbytes/sec\n"),
AREA("out_neg", rrggbb="4169E1", legend="Outgoing"),
LINE1("out_neg", rrggbb="0033CC"),
GPRINT("out", cf=MaxCF, format=r" Max\: %5.1lf %S"),
GPRINT("out", cf=AverageCF, format=r" Avg\: %5.1lf %S"),
GPRINT("out", cf=LastCF, format=r" Current\: %5.1lf %Sbytes/sec\n"),
HRULE("0", rrggbb="000000"),
vertical_label="bytes/sec",
lower_limit=0,
start="-1%s" % (period,),
title=title,
width=600,
height=80,
lazy=None,
)
class GaugeRRD(object):
"""rrd for storing any gauge-measured values"""
fname_pattern = os.path.join(LIB_DIR, "%s.rrd")
graph_pattern = os.path.join(OUTPUT_DIR, "%s-%s.png")
_command = ''
_min = _max = 0
_units = ''
def __init__(self, name, label=''):
self.name = name
self.label = label
self.fname = self.fname_pattern % (self.name)
self.rrd = RoundRobinDatabase(self.fname)
if not self._command:
raise NotImplementedError("GaugeRRD must be subclassed")
if not os.path.exists(os.path.join(LIB_DIR, self.fname)):
self.rrd.create(
DataSource("value", type=GaugeDST, heartbeat=600, min=self._min, max=self._max),
RoundRobinArchive(cf=AverageCF, xff=0.5, steps=1, rows=576), # last 48 hours, every 5 mins
RoundRobinArchive(cf=AverageCF, xff=0.5, steps=6, rows=672), # last 2 weeks, 30 mins average
RoundRobinArchive(cf=AverageCF, xff=0.5, steps=24, rows=732), # last 2 months, 2 hours average
RoundRobinArchive(cf=AverageCF, xff=0.5, steps=144, rows=1460), # last 2 years, 12 hours average
step=300
)
def get_data(self):
value = Popen(self._command % (self.name,), stdout=PIPE, shell=True).communicate()[0].strip()
if not value:
raise ValueError("Error getting value for %s", self.name)
print "Values %s for %s" % (value, self.name)
return (value,)
def update(self, values=None, timestamp=None):
"""update data in rrd with given values or current, if None"""
try:
if not values:
values = self.get_data()
if timestamp:
kwargs = {'timestamp': timestamp}
else:
kwargs = {}
template = ("value",)
self.rrd.update(Val(*values, **kwargs), template=template)
return self
except ValueError, e:
print "updating failed (%s)" % (e.message,)
def graph(self):
for period in ['day', 'week', 'month', 'year']:
self._graph(period)
return self
def _graph(self, period):
"""create a graph for the given period"""
title = "Information on %s" % (self.name,)
if self.label:
title += " :: " + self.label
graph = RoundRobinGraph(self.graph_pattern % (self.name, period))
graph.graph(
Def("value", self.fname, data_source="value", cf=AverageCF),
LINE1("value", rrggbb="4116EC"),
GPRINT("value", cf=MinCF, format=r" Min\: %2.lf %S"),
GPRINT("value", cf=MaxCF, format=r" Max\: %2.lf %S"),
GPRINT("value", cf=AverageCF, format=r" Avg\: %4.lf %S"),
GPRINT("value", cf=LastCF, format=r" Current\: %2.lf %S" + self._units +"\n"),
vertical_label=self._units,
start="-1%s" % (period,),
title=title,
width=600,
height=80,
lazy=None,
)
class HddTempRRD(GaugeRRD):
"""rrd for storing hdd temp information (requires hddtemp utility)"""
_command = 'hddtemp -n /dev/%s'
_max = 1000
_units = "C"
class GoogleReaderSubscribersRRD(GaugeRRD):
"""rrd for storing info on the number of google reader subscribers"""
_command = "tail %s -n 1000 | grep Feedfetcher | tail -n1 | sed 's/.* ([0-9]+) subscribers.*/\\1/' -r"
_max = 1000
_units = 'subscribers'
def _graph(self, *args, **kwargs):
oldname = self.name
self.name = self.name.split('/')[-1]
result = super(GoogleReaderSubscribersRRD, self)._graph(*args, **kwargs)
self.name = oldname
return result
# property here for not having /var/lib/rrd/ + /var/log/apache/access_log
def set_fname(self, value):
self._fname = value
def get_fname(self):
return os.path.join(LIB_DIR, self._fname.split('/')[-1])
fname = property(get_fname, set_fname)
class MadwifiAssociatedRRD(GaugeRRD):
"""rrd for storing the number of wifi clients associated with madwifi AP"""
_command = 'cat /proc/net/madwifi/%s/associated_sta | grep macaddr | wc -l'
_max = 100
_units = 'clients'
def _graph(self, *args, **kwargs):
oldname = self.name
self.name = 'madwifi_assoc_' + self.name.split('/')[-1]
result = super(MadwifiAssociatedRRD, self)._graph(*args, **kwargs)
self.name = oldname
return result
def set_fname(self, value):
self._fname = value
def get_fname(self):
return os.path.join(LIB_DIR, 'madwifi_assoc_' + self._fname.split('/')[-1])
fname = property(get_fname, set_fname)