comparison speedcheck.py @ 28:b474c873357d

add speed check script
author Daniel O'Connor <darius@dons.net.au>
date Sun, 12 Aug 2018 17:39:32 +0930
parents
children e73e4677f873
comparison
equal deleted inserted replaced
27:607111929e2e 28:b474c873357d
1 #!/usr/bin/env python
2
3 import ConfigParser
4 import optparse
5 import os
6 import re
7 import rrdtool
8 import subprocess
9 import time
10
11 def main():
12 conf = ConfigParser.ConfigParser()
13
14 conflist = []
15 if ('HOME' in os.environ):
16 conflist.append(os.path.expanduser('~/.speedcheck.ini'))
17 conf.read(conflist)
18
19 usage = '''%prog [options]'''
20 parser = optparse.OptionParser(usage)
21 parser.add_option('-v', '--verbose', action="store_true", default=False,
22 help="Enable debug output")
23 parser.add_option('-r', '--rrd', action="store", help="Path to RRD")
24 parser.add_option('-g', '--graphdir', action="store", help="Directory for graphs")
25
26 (opts, args) = parser.parse_args()
27 if opts.rrd == None:
28 if conf.has_option('global', 'rrd'):
29 opts.rrd = conf.get('global', 'rrd')
30 else:
31 parser.error('Path to RRD must be specified in either the ini or on the command line')
32
33 if opts.graphdir == None:
34 if conf.has_option('global', 'graphdir'):
35 opts.graphdir = conf.get('global', 'graphdir')
36 else:
37 parser.error('Graph directory must be specified in either the ini or on the command line')
38
39 if opts.verbose:
40 print 'Fetching stats...'
41 stats = fetchstats(conf)
42 if opts.verbose:
43 print stats
44 if opts.verbose:
45 print 'Updating RRD'
46 updaterrd(opts.rrd, stats)
47 if opts.verbose:
48 print 'Updating graph'
49 graphrrd(opts.rrd, opts.graphdir)
50
51 def fetchstats(conf):
52 stats = {}
53 if conf.has_option('global', 'neardl'):
54 stats['neardl'] = testdl(conf.get('global', 'neardl'))
55 if conf.has_option('global', 'nearul'):
56 stats['nearul'] = testul(conf.get('global', 'nearul'))
57 if conf.has_option('global', 'nearping'):
58 stats['nearpl'], stats['nearlat'] = testping(conf.get('global', 'nearping'))
59 if conf.has_option('global', 'fardl'):
60 stats['fardl'] = testdl(conf.get('global', 'fardl'))
61 if conf.has_option('global', 'farul'):
62 stats['farul'] = testul(conf.get('global', 'farul'))
63 if conf.has_option('global', 'farping'):
64 stats['farpl'], stats['farlat'] = testping(conf.get('global', 'farping'))
65
66 return stats
67
68 def testdl(url):
69 p = subprocess.Popen(['curl', '-w', '%{speed_download}', '-so', '/dev/null', url], stdout = subprocess.PIPE)
70 speed, xxx = p.communicate()
71 if p.returncode != 0:
72 print 'Error %d fetching \'%s\'' % (p.returncode, url)
73 return None
74 return float(speed) * 8.0 / 1024.0 # convert to kbit/sec
75
76 def testping(host):
77 p = subprocess.Popen(['ping', '-c', '5', '-t', '8', '-q', host], stdout = subprocess.PIPE)
78 stdout, stderr = p.communicate()
79 l = stdout.split('\n')
80 if len(l) != 6:
81 print 'Unable to parse ping line:', l
82 xx, xx, xx, plossline, latline, xx = l
83 ploss = float(re.match('.* received, ([0-9.]+)% packet loss', plossline).groups()[0])
84 latency = float(re.match('.*stddev = [0-9.]+/([0-9.]+)/.* ms', latline).groups()[0])
85 return ploss, latency
86
87 def createrrd(rrdname):
88 # Create RRD for upstream/downstream speed, packet loss and
89 # latency for near and far site
90 # Do a test every half and hour
91 # Average 2 for hourly stats (keep 168 - a weeks worth)
92 # Average 48 for hourly stats (keep 1825 - 5 years worth)
93 # Detemine minimum & maximum for an hour and keep a weeks worth.
94 rrdtool.create(rrdname,
95 '--step', '300',
96 'DS:neardl:GAUGE:3600:0:U',
97 'DS:nearul:GAUGE:3600:0:U',
98 'DS:nearpl:GAUGE:3600:0:100',
99 'DS:nearlat:GAUGE:3600:0:U',
100 'DS:fardl:GAUGE:3600:0:U',
101 'DS:farul:GAUGE:3600:0:U',
102 'DS:farpl:GAUGE:3600:0:100',
103 'DS:farlat:GAUGE:3600:0:U',
104 'RRA:AVERAGE:0.1:2:168',
105 'RRA:AVERAGE:0.1:48:1825',
106 'RRA:MIN:0.1:2:168',
107 'RRA:MAX:0.1:2:168',
108 )
109
110 def updaterrd(rrdname, stats):
111 try:
112 os.stat(rrdname)
113 except OSError, e:
114 if e.errno == 2:
115 print 'Creating RRD...'
116 createrrd(rrdname)
117 s = '%d:' % (int(time.time()))
118 for a in ['neardl', 'nearul', 'nearpl', 'nearlat', 'fardl', 'farul', 'farpl', 'farlat']:
119 if a in stats:
120 s += '%f:' % (stats[a])
121 else:
122 s += 'U:'
123 s = s[0:-1]
124 rrdtool.update(rrdname, s)
125
126 def graphrrd(rrdname, graphdir):
127 latencyargs = (
128 '-a', 'SVG',
129 '--vertical-label', 'milliseconds',
130
131 'DEF:nearlat=%s:nearlat:AVERAGE' % rrdname,
132 'DEF:nearlatmin=%s:nearlat:MIN' % rrdname,
133 'DEF:nearlatmax=%s:nearlat:MAX' % rrdname,
134 'CDEF:nearlatdif=nearlatmax,nearlatmin,-',
135
136 'LINE0.001:nearlatmin#000000:',
137 'AREA:nearlatdif#00dc76::STACK',
138 'LINE1:nearlatmax#00ff00:Near latency',
139
140 'DEF:farlat=%s:farlat:AVERAGE' % rrdname,
141 'DEF:farlatmin=%s:farlat:MIN' % rrdname,
142 'DEF:farlatmax=%s:farlat:MAX' % rrdname,
143 'CDEF:farlatdif=farlatmax,farlatmin,-',
144
145 'LINE0.001:farlatmin#000000:',
146 'AREA:farlatdif#dc0076::STACK',
147 'LINE1:farlatmax#ff0000:Far packetloss',
148
149 'DEF:nearpl=%s:nearpl:AVERAGE' % rrdname,
150 'LINE1:nearpl#0000ff:Near packet loss (%)',
151
152 'DEF:farpl=%s:farpl:AVERAGE' % rrdname,
153 'LINE1:nearpl#ffff00:Far packet loss (%)',
154 )
155 rrdtool.graph('%s/latency-hour-link.svg' % (graphdir),
156 '--width', '768',
157 '--height', '256',
158 '--start', 'end - 7d',
159 '--end', 'now',
160 *latencyargs)
161 rrdtool.graph('%s/latency-daily-link.svg' % (graphdir),
162 '--width', '768',
163 '--height', '256',
164 '--start', 'end - 365d',
165 '--end', 'now',
166 *latencyargs)
167
168 bwargs = (
169 '-a', 'SVG',
170 '-X', '0',
171 '--vertical-label', 'kbit/sec',
172
173 'DEF:neardl=%s:neardl:AVERAGE' % rrdname,
174 'DEF:neardlmin=%s:neardl:MIN' % rrdname,
175 'DEF:neardlmax=%s:neardl:MAX' % rrdname,
176 'CDEF:neardldif=neardlmax,neardlmin,-',
177
178 'LINE0.001:neardlmin#000000:',
179 'AREA:neardldif#00dc76::STACK',
180 'LINE1:neardlmax#00ff00:Near download',
181
182 'DEF:fardl=%s:fardl:AVERAGE' % rrdname,
183 'DEF:fardlmin=%s:fardl:MIN' % rrdname,
184 'DEF:fardlmax=%s:fardl:MAX' % rrdname,
185 'CDEF:fardldif=fardlmax,fardlmin,-',
186
187 'LINE0.001:fardlmin#000000:',
188 'AREA:fardldif#dc0076::STACK',
189 'LINE1:fardlmax#ff0000:Far download',
190 )
191 rrdtool.graph('%s/bw-hour-link.svg' % (graphdir),
192 '--width', '768',
193 '--height', '256',
194 '--start', 'end - 1d',
195 '--end', 'now',
196 *bwargs)
197 rrdtool.graph('%s/bw-daily-link.svg' % (graphdir),
198 '--width', '768',
199 '--height', '256',
200 '--start', 'end - 7d',
201 '--end', 'now',
202 *bwargs)
203
204 if __name__ == '__main__':
205 main()