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