Mercurial > ~darius > hgwebdir.cgi > adslstats
comparison adslstats.py @ 32:1af6865189ce
Update to work with Python 3.
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Sat, 14 Nov 2020 14:54:05 +1030 |
parents | 39bf6dec0753 |
children | 7d8bee5e3c80 |
comparison
equal
deleted
inserted
replaced
31:39bf6dec0753 | 32:1af6865189ce |
---|---|
31 ############################################################################ | 31 ############################################################################ |
32 | 32 |
33 import base64 | 33 import base64 |
34 import binascii | 34 import binascii |
35 import bs4 | 35 import bs4 |
36 import ConfigParser | 36 import configparser |
37 import json | 37 import json |
38 import mechanize | 38 import mechanize |
39 import mysrp as srp | 39 import mysrp as srp |
40 import argparse | 40 import argparse |
41 import os | 41 import os |
43 import re | 43 import re |
44 import requests | 44 import requests |
45 import rrdtool | 45 import rrdtool |
46 import sys | 46 import sys |
47 import time | 47 import time |
48 import urllib | 48 import urllib.request, urllib.parse, urllib.error |
49 | 49 from functools import reduce |
50 conf = ConfigParser.ConfigParser() | 50 |
51 conf = configparser.ConfigParser() | |
51 conf.add_section('global') | 52 conf.add_section('global') |
52 | 53 |
53 conflist = [] | 54 conflist = [] |
54 if ('HOME' in os.environ): | 55 if ('HOME' in os.environ): |
55 conflist.append(os.path.expanduser('~/.adslstats.ini')) | 56 conflist.append(os.path.expanduser('~/.adslstats.ini')) |
115 self.uptime) | 116 self.uptime) |
116 return s | 117 return s |
117 | 118 |
118 def getstats(): | 119 def getstats(): |
119 stats = DSLStats() | 120 stats = DSLStats() |
120 parser = ConfigParser.ConfigParser() | 121 parser = configparser.ConfigParser() |
121 base = 'http://%s' % (args.name) | 122 base = 'http://%s' % (args.name) |
122 br = mechanize.Browser() | 123 br = mechanize.Browser() |
123 #br.set_debug_http(True) | 124 #br.set_debug_http(True) |
124 #br.set_debug_responses(True) | 125 #br.set_debug_responses(True) |
125 #br.set_debug_redirects(True) | 126 #br.set_debug_redirects(True) |
145 #print('Got CSRF token ' + token) | 146 #print('Got CSRF token ' + token) |
146 | 147 |
147 usr = srp.User(username, password, hash_alg = srp.SHA256, ng_type = srp.NG_2048) | 148 usr = srp.User(username, password, hash_alg = srp.SHA256, ng_type = srp.NG_2048) |
148 uname, A = usr.start_authentication() | 149 uname, A = usr.start_authentication() |
149 | 150 |
150 req = mechanize.Request(base + '/authenticate', data = urllib.urlencode({'CSRFtoken' : token, 'I' : uname, 'A' : binascii.hexlify(A)})) | 151 req = mechanize.Request(base + '/authenticate', data = urllib.parse.urlencode({'CSRFtoken' : token, 'I' : uname, 'A' : binascii.hexlify(A)})) |
151 r = br.open(req) | 152 r = br.open(req) |
152 j = json.decoder.JSONDecoder().decode(r.read()) | 153 j = json.decoder.JSONDecoder().decode(r.read()) |
153 #print('Sent challenge, got ' + str(j)) | 154 #print('Sent challenge, got ' + str(j)) |
154 | 155 |
155 M = usr.process_challenge(binascii.unhexlify(j['s']), binascii.unhexlify(j['B'])) | 156 M = usr.process_challenge(binascii.unhexlify(j['s']), binascii.unhexlify(j['B'])) |
156 req = mechanize.Request(base + '/authenticate', data = urllib.urlencode({'CSRFtoken' : token, 'M' : binascii.hexlify(M)})) | 157 req = mechanize.Request(base + '/authenticate', data = urllib.parse.urlencode({'CSRFtoken' : token, 'M' : binascii.hexlify(M)})) |
157 r = br.open(req) | 158 r = br.open(req) |
158 j = json.decoder.JSONDecoder().decode(r.read()) | 159 j = json.decoder.JSONDecoder().decode(r.read()) |
159 #print('Got response ' + str(j)) | 160 #print('Got response ' + str(j)) |
160 | 161 |
161 usr.verify_session(binascii.unhexlify(j['M'])) | 162 usr.verify_session(binascii.unhexlify(j['M'])) |
172 return False | 173 return False |
173 | 174 |
174 # Helper function to extract data | 175 # Helper function to extract data |
175 def getvals(bs, text, mult = 1): | 176 def getvals(bs, text, mult = 1): |
176 subs = bs.findAll('label', text = text)[0].fetchNextSiblings()[0].strings | 177 subs = bs.findAll('label', text = text)[0].fetchNextSiblings()[0].strings |
177 tmp = map(lambda s: float(s.split()[0]), subs) | 178 tmp = [float(s.split()[0]) for s in subs] |
178 return map(lambda s: s * mult, tmp) | 179 return [s * mult for s in tmp] |
179 | 180 |
180 if map(None, bs.findAll('label', text = 'DSL Status')[0].fetchNextSiblings()[0].strings)[0] == 'Up': | 181 if list(bs.findAll('label', text = 'DSL Status')[0].fetchNextSiblings()[0].strings)[0] == 'Up': |
181 stats.linkup = True | 182 stats.linkup = True |
182 else: | 183 else: |
183 stats.linkup = False | 184 stats.linkup = False |
184 | 185 |
185 stats.upstreammax, stats.downstreammax = getvals(bs, 'Maximum Line rate', 1e3) | 186 stats.upstreammax, stats.downstreammax = getvals(bs, 'Maximum Line rate', 1e3) |
186 stats.upstream, stats.downstream = getvals(bs, 'Line Rate', 1e3) | 187 stats.upstream, stats.downstream = getvals(bs, 'Line Rate', 1e3) |
187 stats.uppower, stats.downpower = getvals(bs, 'Output Power') | 188 stats.uppower, stats.downpower = getvals(bs, 'Output Power') |
188 stats.nmup, stats.nmdown = getvals(bs, 'Noise Margin') | 189 stats.nmup, stats.nmdown = getvals(bs, 'Noise Margin') |
189 | 190 |
190 # Line attenuation returns several values for each direction, parse specially and just take the first one | 191 # Line attenuation returns several values for each direction, parse specially and just take the first one |
191 upattens, downattens = map(None, bs.findAll('label', text = 'Line Attenuation')[0].fetchNextSiblings()[0].strings) | 192 upattens, downattens = list(bs.findAll('label', text = 'Line Attenuation')[0].fetchNextSiblings()[0].strings) |
192 stats.attenup = float(re.findall('([0-9.N/A]+)', upattens)[0]) | 193 stats.attenup = float(re.findall('([0-9.N/A]+)', upattens)[0]) |
193 stats.attendown = float(re.findall('([0-9.N/A]+)', downattens)[0]) | 194 stats.attendown = float(re.findall('([0-9.N/A]+)', downattens)[0]) |
194 | 195 |
195 # Convert something like '2days 17hours 28min 19sec' into seconds | 196 # Convert something like '2days 17hours 28min 19sec' into seconds |
196 uptime = re.findall('([0-9]+)', map(None, bs.findAll('label', text = 'DSL Uptime')[0].fetchNextSiblings()[0].strings)[0]) | 197 uptime = re.findall('([0-9]+)', list(bs.findAll('label', text = 'DSL Uptime')[0].fetchNextSiblings()[0].strings)[0]) |
197 uptime.reverse() # End up with an array of seconds, minutes, hours, etc | 198 uptime.reverse() # End up with an array of seconds, minutes, hours, etc |
198 mults = [1, 60, 60 * 60, 24 * 60 * 60] | 199 mults = [1, 60, 60 * 60, 24 * 60 * 60] |
199 if len(uptime) > mults: | 200 if len(uptime) != len(mults): |
200 print('Too many uptime elements to work out') | 201 print('Unexpected number of uptime elements (%s)' % str(uptime)) |
201 stats.uptime = None | 202 stats.uptime = None |
202 else: | 203 else: |
203 stats.uptime = reduce(lambda a, b: a + b, map(lambda a: int(a[0]) * a[1], zip(uptime, mults))) | 204 stats.uptime = reduce(lambda a, b: a + b, [int(a[0]) * a[1] for a in zip(uptime, mults)]) |
204 | 205 |
205 return True | 206 return True |
206 | 207 |
207 # Setup RRD | 208 # Setup RRD |
208 # We expect data to be logged every 5 minutes | 209 # We expect data to be logged every 5 minutes |
405 names = ['Noise Margin (up)', 'Noise Margin (down)', 'Attenuation (up)', 'Attenuation (down)'] | 406 names = ['Noise Margin (up)', 'Noise Margin (down)', 'Attenuation (up)', 'Attenuation (down)'] |
406 if args.munin != None: | 407 if args.munin != None: |
407 # Handle the wrapper passing us its $0 as our $1 | 408 # Handle the wrapper passing us its $0 as our $1 |
408 args.munin = args.munin.split('_')[-1] | 409 args.munin = args.munin.split('_')[-1] |
409 if args.munin not in ['signal', 'sync']: | 410 if args.munin not in ['signal', 'sync']: |
410 print "Unknown data type ", args.munin | 411 print("Unknown data type ", args.munin) |
411 sys.exit(1) | 412 sys.exit(1) |
412 if len(args.rest) > 0: | 413 if len(args.rest) > 0: |
413 if args.rest[0] == 'config': | 414 if args.rest[0] == 'config': |
414 if args.munin == 'signal': | 415 if args.munin == 'signal': |
415 print '''graph_category adsl | 416 print('''graph_category adsl |
416 graph_title DSL Signal Quality | 417 graph_title DSL Signal Quality |
417 graph_args --base 1000 -l 0 | 418 graph_args --base 1000 -l 0 |
418 graph_vlabel dB''' | 419 graph_vlabel dB''') |
419 for n in names: | 420 for n in names: |
420 name = n.translate(None, ' ()').lower() | 421 name = n.translate(None, ' ()').lower() |
421 print '''%s.label %s | 422 print('''%s.label %s |
422 %s.type GAUGE | 423 %s.type GAUGE |
423 %s.max 100 | 424 %s.max 100 |
424 %s.min 0''' % (name, n, name, name, name) | 425 %s.min 0''' % (name, n, name, name, name)) |
425 elif args.munin == 'sync': | 426 elif args.munin == 'sync': |
426 print '''graph_category adsl | 427 print('''graph_category adsl |
427 graph_title DSL Sync Speed | 428 graph_title DSL Sync Speed |
428 graph_args --base 1024 -l 0 | 429 graph_args --base 1024 -l 0 |
429 graph_vlabel kbit/sec | 430 graph_vlabel kbit/sec |
430 up.label Up | 431 up.label Up |
431 up.type GAUGE | 432 up.type GAUGE |
440 upmax.max 150000 | 441 upmax.max 150000 |
441 upmax.min 0 | 442 upmax.min 0 |
442 downmax.label Down (max) | 443 downmax.label Down (max) |
443 downmax.type GAUGE | 444 downmax.type GAUGE |
444 downmax.max 150000 | 445 downmax.max 150000 |
445 downmax.min 0''' | 446 downmax.min 0''') |
446 sys.exit(0) | 447 sys.exit(0) |
447 if args.update or args.munin: | 448 if args.update or args.munin: |
448 stats = getdata() | 449 stats = getdata() |
449 if args.verbose: | 450 if args.verbose: |
450 if stats == None: | 451 if stats == None: |
451 print "Modem is offline" | 452 print("Modem is offline") |
452 else: | 453 else: |
453 print stats | 454 print(stats) |
454 if (args.update or args.munin != None) and stats != None: | 455 if (args.update or args.munin != None) and stats != None: |
455 if args.update: | 456 if args.update: |
456 try: | 457 try: |
457 os.stat(rrdname) | 458 os.stat(rrdname) |
458 except OSError, e: | 459 except OSError as e: |
459 if e.errno == 2: | 460 if e.errno == 2: |
460 print "rrd not found, creating.." | 461 print("rrd not found, creating..") |
461 makerrd(rrdname) | 462 makerrd(rrdname) |
462 updaterrd(rrdname, int(time.time()), stats) | 463 updaterrd(rrdname, int(time.time()), stats) |
463 if args.munin != None: | 464 if args.munin != None: |
464 if args.munin == 'signal': | 465 if args.munin == 'signal': |
465 print '''noisemarginup.value %.1f | 466 print('''noisemarginup.value %.1f |
466 noisemargindown.value %.1f | 467 noisemargindown.value %.1f |
467 attenuationup.value %.1f | 468 attenuationup.value %.1f |
468 attenuationdown.value %.1f''' % (stats.nmup, stats.nmdown, stats.attenup, stats.attendown) | 469 attenuationdown.value %.1f''' % (stats.nmup, stats.nmdown, stats.attenup, stats.attendown)) |
469 elif args.munin == 'sync': | 470 elif args.munin == 'sync': |
470 s = '''up.value %.1f | 471 s = '''up.value %.1f |
471 down.value %.1f\n''' % (stats.upstream, stats.downstream) | 472 down.value %.1f\n''' % (stats.upstream, stats.downstream) |
472 if hasattr(stats, 'upstreammax'): | 473 if hasattr(stats, 'upstreammax'): |
473 s += '''upmax.value %.1f | 474 s += '''upmax.value %.1f |
474 downmax.value %.1f''' % (stats.upstreammax, stats.downstreammax) | 475 downmax.value %.1f''' % (stats.upstreammax, stats.downstreammax) |
475 print s | 476 print(s) |
476 if args.graph: | 477 if args.graph: |
477 gengraph() | 478 gengraph() |
478 | 479 |