summaryrefslogtreecommitdiff
path: root/if_rrd_fast.py
diff options
context:
space:
mode:
Diffstat (limited to 'if_rrd_fast.py')
-rwxr-xr-xif_rrd_fast.py132
1 files changed, 132 insertions, 0 deletions
diff --git a/if_rrd_fast.py b/if_rrd_fast.py
new file mode 100755
index 0000000..8d7fb24
--- /dev/null
+++ b/if_rrd_fast.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+#
+# Record network device counters every 1 second and write in RRD or plain text.
+#
+# Copyright 2016 Ludovic Pouzenc <ludovic@pouzenc.fr>
+#
+# if_rrd_fast is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# if_rrd_fast is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with CHD OpenWRT. If not, see <http://www.gnu.org/licenses/>.
+#
+import sys,os,signal,argparse
+from time import time,sleep;
+from math import ceil;
+from pyrrd.rrd import DataSource, RRA, RRD
+
+
+def signal_handler(signal, frame):
+ global exit_flag
+ exit_flag=True
+
+def output_text(f_outpout_text, values):
+ f_outpout_text.writelines('%i:%s\n'%(ts,v) for (ts,v) in values)
+
+if __name__ == "__main__":
+ # Workaround for rrd bug about float parsing and locale on HWPREDICT
+ os.environ['LC_NUMERIC']='C'
+
+ # Argument parsing
+ parser = argparse.ArgumentParser(description='Record network device counters every 1 second and write in RRD or plain text.')
+ parser.add_argument('-v', '--verbose', action='store_true', help='verbose on stdout, showing what the program is doing')
+ parser.add_argument('-o', '--output', metavar='RRDFILE', default='./out.rrd', help='put acquired values in RRDFILE')
+ parser.add_argument('-O', '--text-output', metavar='TEXTFILE', default='./out.txt', help='put acquired values in TEXTFILE')
+ parser.add_argument('-I', '--import-first', metavar='TEXTFILE', help='import values from TEXTFILE before acquiring new data')
+ parser.add_argument('iface', help='read counters from this network interface')
+ args = parser.parse_args()
+
+ # Signal setup
+ exit_flag=False
+ signal.signal(signal.SIGINT, signal_handler)
+
+ # Init input files
+ sysfs_dir='/sys/class/net/' + args.iface + '/statistics/'
+ if args.verbose: print "Using %s..." % sysfs_dir
+ f_rx_bytes = open(sysfs_dir + 'rx_bytes')
+ f_tx_bytes = open(sysfs_dir + 'tx_bytes')
+
+ # Guess the start timestamp of RRD from import file
+ start=None
+ if args.import_first:
+ f_import = open(args.import_first)
+ try:
+ (first_ts,first_data) = f_import.next().rstrip().split(':',1)
+ start = int(first_ts)-1
+ except:
+ print "Skipping import : can't find the first timestamp at begining of file"
+ f_import.seek(0)
+
+ # Open / create output files
+ if args.verbose: print "Initializing %s..." % args.output
+ dataSources = []
+ roundRobinArchives = []
+ dataSources.append(DataSource(dsName='rx', dsType='COUNTER', heartbeat=2, minval=0, maxval=1e9))
+ dataSources.append(DataSource(dsName='tx', dsType='COUNTER', heartbeat=2, minval=0, maxval=1e9))
+ roundRobinArchives.append(RRA(cf='AVERAGE', xff=0.5, steps=1, rows=1152))
+ roundRobinArchives.append(RRA(cf='AVERAGE', xff=0.5, steps=60, rows=1152))
+ roundRobinArchives.append(RRA(cf='AVERAGE', xff=0.5, steps=300, rows=1152))
+ roundRobinArchives.append(RRA(cf='AVERAGE', xff=0.5, steps=3600, rows=1152))
+ roundRobinArchives.append(RRA(cf='MAX', xff=0.5, steps=1, rows=1152))
+ roundRobinArchives.append(RRA(cf='MAX', xff=0.5, steps=60, rows=1152))
+ roundRobinArchives.append(RRA(cf='MAX', xff=0.5, steps=300, rows=1152))
+ roundRobinArchives.append(RRA(cf='MAX', xff=0.5, steps=3600, rows=1152))
+ roundRobinArchives.append(RRA(cf='MIN', xff=0.5, steps=1, rows=1152))
+ roundRobinArchives.append(RRA(cf='MIN', xff=0.5, steps=60, rows=1152))
+ roundRobinArchives.append(RRA(cf='MIN', xff=0.5, steps=300, rows=1152))
+ roundRobinArchives.append(RRA(cf='MIN', xff=0.5, steps=3600, rows=1152))
+ myRRD = RRD(filename=args.output, start=start, step=1, ds=dataSources, rra=roundRobinArchives)
+ myRRD.create()
+
+ # Import data
+ if args.import_first:
+ if start:
+ print "Importing %s..." % args.import_first
+ lineno=0
+ for line in f_import:
+ lineno = lineno + 1;
+ myRRD.bufferValue(line.rstrip())
+ if (lineno%300==0): myRRD.update()
+ myRRD.update()
+ f_import.close()
+ print "Imported %i PDP" % lineno
+
+ # Open text output file (after import because could be the same file)
+ if args.verbose: print "Opening %s..." % args.text_output
+ f_outpout_text = open(args.text_output, 'a')
+
+ # Main data acquire/write loop
+ while(exit_flag==False):
+ # Try to align out self on next second start
+ ts=time();
+ ts2=ceil(ts);
+ sleep(ts2-ts); # FIXME traped signals could make things messy
+ # Acquire values
+ f_rx_bytes.seek(0)
+ f_tx_bytes.seek(0)
+ rx=f_rx_bytes.read().rstrip()
+ tx=f_tx_bytes.read().rstrip()
+ if args.verbose: print "at %i : %s/%s" % (ts,rx,tx)
+ # Buffer and periodically write down values in RRD and text files
+ myRRD.bufferValue(ts,rx,tx)
+ if (ts2%300==0):
+ output_text(f_outpout_text, myRRD.values)
+ myRRD.update()
+
+ # Cleanup for exiting
+ if len(myRRD.values):
+ output_text(f_outpout_text, myRRD.values)
+ myRRD.update()
+
+ f_rx_bytes.close()
+ f_tx_bytes.close()
+ f_outpout_text.close()
+
+ print "Last timestamp : %d" % ts