comparison adslstats.py @ 37:4f9a79f733ff

Don't explode when the link is down.
author Daniel O'Connor <darius@dons.net.au>
date Sat, 21 Nov 2020 10:31:42 +1030
parents 5dbc310f1eca
children 6f85bedf9966
comparison
equal deleted inserted replaced
36:5dbc310f1eca 37:4f9a79f733ff
104 rrdname = "%s.rrd" % (args.base) 104 rrdname = "%s.rrd" % (args.base)
105 graphbasename = args.base 105 graphbasename = args.base
106 106
107 class DSLStats(object): 107 class DSLStats(object):
108 def __str__(self): 108 def __str__(self):
109 if self.upstream == None:
110 return 'Disconnected'
111
109 s = '''Line Rate - Up: %d kbits, Down %d kbits 112 s = '''Line Rate - Up: %d kbits, Down %d kbits
110 Maximum Rate - Up: %d kbit, Down %s kbit 113 Maximum Rate - Up: %d kbit, Down %s kbit
111 Noise Margin - Up: %.1f dB, Down %.1f dB 114 Noise Margin - Up: %.1f dB, Down %.1f dB
112 Attenuation - Up: %.1f dB, Down %.1f dB 115 Attenuation - Up: %.1f dB, Down %.1f dB
113 Power - Up: %.1f dBm, Down %.1f dBm 116 Power - Up: %.1f dBm, Down %.1f dBm
175 if bs.find('div', 'login') != None: 178 if bs.find('div', 'login') != None:
176 return False 179 return False
177 180
178 # Helper function to extract data 181 # Helper function to extract data
179 def getvals(bs, text, mult = 1): 182 def getvals(bs, text, mult = 1):
180 subs = bs.findAll('label', text = text)[0].fetchNextSiblings()[0].strings 183 subs = bs.findAll('label', text = text)
184 if len(subs) == 0:
185 return None, None
186 subs = subs[0].fetchNextSiblings()[0].strings
181 tmp = [float(s.split()[0]) for s in subs] 187 tmp = [float(s.split()[0]) for s in subs]
182 return [s * mult for s in tmp] 188 return [s * mult for s in tmp]
183 189
184 if list(bs.findAll('label', text = 'DSL Status')[0].fetchNextSiblings()[0].strings)[0] == 'Up': 190 tmp = bs.findAll('label', text = 'DSL Status')
191 if len(tmp) > 0 and list(tmp[0].fetchNextSiblings()[0].strings)[0] == 'Up':
185 stats.linkup = True 192 stats.linkup = True
186 else: 193 else:
187 stats.linkup = False 194 stats.linkup = False
188 195
189 stats.upstreammax, stats.downstreammax = getvals(bs, 'Maximum Line rate', 1e3) 196 stats.upstreammax, stats.downstreammax = getvals(bs, 'Maximum Line rate', 1e3)
190 stats.upstream, stats.downstream = getvals(bs, 'Line Rate', 1e3) 197 stats.upstream, stats.downstream = getvals(bs, 'Line Rate', 1e3)
191 stats.uppower, stats.downpower = getvals(bs, 'Output Power') 198 stats.uppower, stats.downpower = getvals(bs, 'Output Power')
192 stats.nmup, stats.nmdown = getvals(bs, 'Noise Margin') 199 stats.nmup, stats.nmdown = getvals(bs, 'Noise Margin')
193 200
194 # Line attenuation returns several values for each direction, parse specially and just take the first one 201 # Line attenuation returns several values for each direction, parse specially and just take the first one
195 upattens, downattens = list(bs.findAll('label', text = 'Line Attenuation')[0].fetchNextSiblings()[0].strings) 202 tmp = bs.findAll('label', text = 'Line Attenuation')
196 stats.attenup = float(re.findall('([0-9.N/A]+)', upattens)[0]) 203 if len(tmp) == 0:
197 stats.attendown = float(re.findall('([0-9.N/A]+)', downattens)[0]) 204 stats.attenup, stats.attendown = None, None
205 else:
206 upattens, downattens = list(tmp[0].fetchNextSiblings()[0].strings)
207 stats.attenup = float(re.findall('([0-9.N/A]+)', upattens)[0])
208 stats.attendown = float(re.findall('([0-9.N/A]+)', downattens)[0])
198 209
199 # Convert something like '2days 17hours 28min 19sec' into seconds 210 # Convert something like '2days 17hours 28min 19sec' into seconds
200 uptime = re.findall('([0-9]+)', list(bs.findAll('label', text = 'DSL Uptime')[0].fetchNextSiblings()[0].strings)[0]) 211 tmp = bs.findAll('label', text = 'DSL Uptime')
201 uptime.reverse() # End up with an array of seconds, minutes, hours, etc 212 if len(tmp) == 0:
202 mults = [1, 60, 60 * 60, 24 * 60 * 60]
203 # Basic sanity check of the number of elements, should be at least 1
204 if len(uptime) == 0 or len(uptime) > len(mults):
205 print('Unexpected number of uptime elements (%s)' % str(uptime))
206 stats.uptime = None 213 stats.uptime = None
207 else: 214 else:
208 stats.uptime = reduce(lambda a, b: a + b, [int(a[0]) * a[1] for a in zip(uptime, mults)]) 215 uptime = re.findall('([0-9]+)', list(tmp[0].fetchNextSiblings()[0].strings)[0])
216 uptime.reverse() # End up with an array of seconds, minutes, hours, etc
217 mults = [1, 60, 60 * 60, 24 * 60 * 60]
218 # Basic sanity check of the number of elements, should be at least 1
219 if len(uptime) == 0 or len(uptime) > len(mults):
220 print('Unexpected number of uptime elements (%s)' % str(uptime))
221 stats.uptime = None
222 else:
223 stats.uptime = reduce(lambda a, b: a + b, [int(a[0]) * a[1] for a in zip(uptime, mults)])
209 224
210 return True 225 return True
211 226
212 # Setup RRD 227 # Setup RRD
213 # We expect data to be logged every 5 minutes 228 # We expect data to be logged every 5 minutes
235 'RRA:MIN:0.1:12:168', 250 'RRA:MIN:0.1:12:168',
236 'RRA:MAX:0.1:12:168') 251 'RRA:MAX:0.1:12:168')
237 252
238 # Update the RRD (format stats as expected) 253 # Update the RRD (format stats as expected)
239 def updaterrd(filename, tstamp, stats): 254 def updaterrd(filename, tstamp, stats):
255 if stats.uptime == None:
256 return
240 rrdtool.update(filename, 257 rrdtool.update(filename,
241 '%d:%d:%d:%d:%d:%f:%f:%f:%f:U:U:%f:%f:%d' % ( 258 '%d:%d:%d:%d:%d:%f:%f:%f:%f:U:U:%f:%f:%d' % (
242 tstamp, 259 tstamp,
243 stats.upstream, 260 stats.upstream,
244 stats.downstream, 261 stats.downstream,