comparison adslstats.py @ 18:ec994073f70a

Gather more stats (not plotted yet). Remove some old modem cruft. Rearrange graphs by item being graphed.
author Daniel O'Connor <darius@dons.net.au>
date Sun, 22 May 2016 15:39:51 +0930
parents 43f54da8baf9
children 5bec78c876db
comparison
equal deleted inserted replaced
17:43f54da8baf9 18:ec994073f70a
72 opts.add_option('-b', '--base', action="store", default="/home/darius/projects/adslstats/adslstats", 72 opts.add_option('-b', '--base', action="store", default="/home/darius/projects/adslstats/adslstats",
73 help="Base directory for RRD & PNGs") 73 help="Base directory for RRD & PNGs")
74 74
75 (options, args) = opts.parse_args() 75 (options, args) = opts.parse_args()
76 76
77 statsurl = "http://%s/statsadsl.html" % (options.name)
78 rrdname = "%s.rrd" % (options.base) 77 rrdname = "%s.rrd" % (options.base)
79 graphbasename = options.base 78 graphbasename = options.base
80 79
81 matchnum = re.compile('([0-9]+(\.[0-9]+)?)')
82 statsdict = {
83 3 : 'Status:',
84 8 : 'SNR Margin (0.1 dB):',
85 9 : 'Attenuation (0.1 dB):',
86 14 : 'Rate (Kbps):',
87 }
88
89 class DSLStats(object): 80 class DSLStats(object):
90 def __str__(self): 81 def __str__(self):
91 s = "Line Rate - Up: %d kbits, Down %d kbits\n" % (self.upstream, self.downstream) 82 s = '''Line Rate - Up: %d kbits, Down %d kbits
92 if hasattr(self, 'upstreammax'): 83 Maximum Rate - Up: %d kbit, Down %s kbit
93 s += "Maximum Rate - Up: %d kbit, Down %s kbit\n" % (self.upstreammax, self.downstreammax) 84 Noise Margin - Up: %.1f dB, Down %.1f dB
94 s += """Noise Margin - Up: %.1f dB, Down %.1f dB 85 Attenuation - Up: %.1f dB, Down %.1f dB
95 Attenuation - Up: %.1f dB, Down %.1f dB""" % (self.nmup, self.nmdown, 86 Errors - Up: %d, Down %d
96 self.attenup, self.attendown) 87 Power - Up: %.1f dBm, Down %.1f dBm
88 Uptime - %d sec''' % (self.upstream, self.downstream,
89 self.upstreammax, self.downstreammax,
90 self.nmup, self.nmdown,
91 self.attenup, self.attendown,
92 self.fecATUC, self.fecATUR,
93 self.uppower, self.downpower,
94 self.uptime)
97 return s 95 return s
98 96
99 def getstats(): 97 def getstats():
100 stats = DSLStats() 98 stats = DSLStats()
101 parser = ConfigParser.ConfigParser() 99 parser = ConfigParser.ConfigParser()
102 base = 'http://%s' % (conf.get('global', 'name')) 100 base = 'http://%s' % (conf.get('global', 'name'))
103 # Gunk extracted from Chrome (what the page is requesting). Note it's sensitive to line ending type... 101 # Gunk extracted from Chrome (what the page is requesting). Note it's sensitive to line ending type...
104 # We could get more data, eg error rates.. 102 # Plus information from http://forum.kitz.co.uk/index.php?topic=15738.0
105 data = '[WAN_DSL_INTF_CFG#1,0,0,0,0,0#0,0,0,0,0,0]0,12\r\nstatus\r\nmodulationType\r\nX_TP_AdslModulationCfg\r\nupstreamCurrRate\r\ndownstreamCurrRate\r\nX_TP_AnnexType\r\nupstreamMaxRate\r\ndownstreamMaxRate\r\nupstreamNoiseMargin\r\ndownstreamNoiseMargin\r\nupstreamAttenuation\r\ndownstreamAttenuation\r\n[WAN_DSL_INTF_STATS_TOTAL#1,0,0,0,0,0#0,0,0,0,0,0]1,8\r\nATUCCRCErrors\r\nCRCErrors\r\nATUCFECErrors\r\nFECErrors\r\nSeverelyErroredSecs\r\nX_TP_US_SeverelyErroredSecs\r\nerroredSecs\r\nX_TP_US_ErroredSecs\r\n' 103 # ATUR = ADSL Termination Unit Remote
104 # ATUC = ADSL Termination Unit Central office
105 query = '[WAN_DSL_INTF_CFG#0,0,0,0,0,0#0,0,0,0,0,0]0,0\r\n[WAN_DSL_INTF_STATS_TOTAL#0,0,0,0,0,0#0,0,0,0,0,0]1,0\r\n'
106 cookies = {'Authorization' : 'Basic ' + base64.standard_b64encode(conf.get('global', 'username') + ':' + conf.get('global', 'password'))} 106 cookies = {'Authorization' : 'Basic ' + base64.standard_b64encode(conf.get('global', 'username') + ':' + conf.get('global', 'password'))}
107 headers = {'Referer' : base} 107 headers = {'Referer' : base}
108 r = requests.post(base + '/cgi?1&5' , data = data, headers = headers, cookies = cookies, stream = True) 108 r = requests.post(base + '/cgi?5&5' , data = query, headers = headers, cookies = cookies, stream = True)
109 parser.readfp(r.raw) 109 parser.readfp(r.raw)
110 res = {} 110 res = {}
111 tmp = '1,0,0,0,0,0' 111 tmp = '1,0,0,0,0,0'
112 if parser.get(tmp, 'status') == 'Up': 112 if parser.get(tmp, 'status') == 'Up':
113 stats.linkup = True 113 stats.linkup = True
119 stats.downstreammax = float(parser.get(tmp, 'downstreamMaxRate')) 119 stats.downstreammax = float(parser.get(tmp, 'downstreamMaxRate'))
120 stats.nmup = float(parser.get(tmp, 'upstreamNoiseMargin')) / 10.0 120 stats.nmup = float(parser.get(tmp, 'upstreamNoiseMargin')) / 10.0
121 stats.nmdown = float(parser.get(tmp, 'downstreamNoiseMargin')) / 10.0 121 stats.nmdown = float(parser.get(tmp, 'downstreamNoiseMargin')) / 10.0
122 stats.attenup = float(parser.get(tmp, 'upstreamAttenuation')) / 10.0 122 stats.attenup = float(parser.get(tmp, 'upstreamAttenuation')) / 10.0
123 stats.attendown = float(parser.get(tmp, 'downstreamAttenuation')) / 10.0 123 stats.attendown = float(parser.get(tmp, 'downstreamAttenuation')) / 10.0
124 stats.fecATUR = int(parser.get(tmp, 'FECErrors'))
125 stats.fecATUC = int(parser.get(tmp, 'ATUCFECErrors'))
126 stats.uppower = float(parser.get(tmp, 'upstreamPower')) / 10.0 # I think it's tenths of a dBm but who knows
127 stats.downpower = float(parser.get(tmp, 'downstreamPower')) / 10.0
128 stats.uptime = int(parser.get(tmp, 'showtimeStart'))
124 129
125 return stats 130 return stats
126 131
127 # Setup RRD 132 # Setup RRD
128 # We expect data to be logged every 5 minutes 133 # We expect data to be logged every 5 minutes
138 'DS:downstreammax:GAUGE:3600:32:150000', # Downstream maximum (kbits) 143 'DS:downstreammax:GAUGE:3600:32:150000', # Downstream maximum (kbits)
139 'DS:nmup:GAUGE:3600:0:100', # Upstream Noise margin (dB) 144 'DS:nmup:GAUGE:3600:0:100', # Upstream Noise margin (dB)
140 'DS:nmdown:GAUGE:3600:0:100', # Downstream Noise margin (dB) 145 'DS:nmdown:GAUGE:3600:0:100', # Downstream Noise margin (dB)
141 'DS:attenup:GAUGE:3600:0:100', # Upstream Attenuation (dB) 146 'DS:attenup:GAUGE:3600:0:100', # Upstream Attenuation (dB)
142 'DS:attendown:GAUGE:3600:0:100', # Downstream Attenuation (dB) 147 'DS:attendown:GAUGE:3600:0:100', # Downstream Attenuation (dB)
148 'DS:fecATUC:DERIVE:3600:0:U', # Upstream FEC error count
149 'DS:fecATUR:DERIVE:3600:0:U', # Downstream FEC error count
150 'DS:powerup:GAUGE:3600:-100:100', # Upstream Power (dBm)
151 'DS:powerdown:GAUGE:3600:-100:100', # Downstream Power (dBm)
152 'DS:uptime:DERIVE:3600:0:U', # Uptime (seconds)
143 'RRA:AVERAGE:0.1:12:168', 153 'RRA:AVERAGE:0.1:12:168',
144 'RRA:AVERAGE:0.1:288:1825', 154 'RRA:AVERAGE:0.1:288:1825',
145 'RRA:MIN:0.1:12:168', 155 'RRA:MIN:0.1:12:168',
146 'RRA:MAX:0.1:12:168') 156 'RRA:MAX:0.1:12:168')
147 157
148 # Update the RRD (format stats as expected) 158 # Update the RRD (format stats as expected)
149 def updaterrd(filename, tstamp, stats): 159 def updaterrd(filename, tstamp, stats):
150 rrdtool.update(filename, 160 rrdtool.update(filename,
151 '%d:%d:%d:%d:%d:%f:%f:%f:%f' % (tstamp, 161 '%d:%d:%d:%d:%d:%f:%f:%f:%f:%d:%d:%f:%f:%d' % (
152 stats.upstream, 162 tstamp,
153 stats.downstream, 163 stats.upstream,
154 stats.upstreammax, 164 stats.downstream,
155 stats.downstreammax, 165 stats.upstreammax,
156 stats.nmup, 166 stats.downstreammax,
157 stats.nmdown, 167 stats.nmup,
158 stats.attenup, 168 stats.nmdown,
159 stats.attendown)) 169 stats.attenup,
170 stats.attendown,
171 stats.fecATUC,
172 stats.fecATUR,
173 stats.uppower,
174 stats.downpower,
175 stats.uptime))
160 176
161 # Open the URL and call the parser 177 # Open the URL and call the parser
162 def getdata(): 178 def getdata():
163 stats = getstats() 179 stats = getstats()
164 return stats 180 return stats
174 '--slope-mode', 190 '--slope-mode',
175 191
176 'DEF:upstream=%s:upstream:AVERAGE' % rrdname, 192 'DEF:upstream=%s:upstream:AVERAGE' % rrdname,
177 'DEF:upstreammin=%s:upstream:MIN' % rrdname, 193 'DEF:upstreammin=%s:upstream:MIN' % rrdname,
178 'DEF:upstreammax=%s:upstream:MAX' % rrdname, 194 'DEF:upstreammax=%s:upstream:MAX' % rrdname,
195 'CDEF:upstreamdif=upstreammax,upstreammin,-',
196 'DEF:maxupstream=%s:upstreammax:AVERAGE' % rrdname,
197
198 'LINE0:upstreammin#000000:',
199 'AREA:upstreamdif#00dc76::STACK',
200 'LINE1:upstream#00ff00:Upstream',
201
202 'LINE1:maxupstream#0000ff:Upstream (maximum)',
179 203
180 'DEF:downstream=%s:downstream:AVERAGE' % rrdname, 204 'DEF:downstream=%s:downstream:AVERAGE' % rrdname,
181 'DEF:downstreammin=%s:downstream:MIN' % rrdname, 205 'DEF:downstreammin=%s:downstream:MIN' % rrdname,
182 'DEF:downstreammax=%s:downstream:MAX' % rrdname, 206 'DEF:downstreammax=%s:downstream:MAX' % rrdname,
183
184 'CDEF:upstreamdif=upstreammax,upstreammin,-',
185 'CDEF:downstreamdif=downstreammax,downstreammin,-', 207 'CDEF:downstreamdif=downstreammax,downstreammin,-',
186
187 'DEF:maxupstream=%s:upstreammax:AVERAGE' % rrdname,
188 'DEF:maxdownstream=%s:downstreammax:AVERAGE' % rrdname, 208 'DEF:maxdownstream=%s:downstreammax:AVERAGE' % rrdname,
189
190 'LINE0:upstreammin#000000:',
191 'AREA:upstreamdif#00dc76::STACK',
192 'LINE1:upstream#00ff00:Upstream',
193 209
194 'LINE0:downstreammin#000000:', 210 'LINE0:downstreammin#000000:',
195 'AREA:downstreamdif#ff8686::STACK', 211 'AREA:downstreamdif#ff8686::STACK',
196 'LINE1:downstream#ff0000:Downstream', 212 'LINE1:downstream#ff0000:Downstream',
197 213
198 'LINE1:maxupstream#0000ff:Upstream (maximum)',
199 'LINE1:maxdownstream#000000:Downstream (maximum)' 214 'LINE1:maxdownstream#000000:Downstream (maximum)'
200 ) 215 )
201 216
202 signalargs = ( 217 signalargs = (
203 '-a', 'SVG', 218 '-a', 'SVG',
204 '--vertical-label', 'dB', 219 '--vertical-label', 'dB',
205 '--slope-mode', 220 '--slope-mode',
206 '-l', '0', 221 '-l', '0',
207 222
208 'DEF:upstream=%s:upstream:AVERAGE' % rrdname,
209 'DEF:downstream=%s:downstream:AVERAGE' % rrdname,
210
211 'DEF:nmup_=%s:nmup:AVERAGE' % rrdname, 223 'DEF:nmup_=%s:nmup:AVERAGE' % rrdname,
212 'DEF:nmupmin_=%s:nmup:MIN' % rrdname, 224 'DEF:nmupmin_=%s:nmup:MIN' % rrdname,
213 'DEF:nmupmax_=%s:nmup:MAX' % rrdname, 225 'DEF:nmupmax_=%s:nmup:MAX' % rrdname,
214
215 'DEF:nmdown_=%s:nmdown:AVERAGE' % rrdname,
216 'DEF:nmdownmin_=%s:nmdown:MIN' % rrdname,
217 'DEF:nmdownmax_=%s:nmdown:MAX' % rrdname,
218
219 'DEF:attenup=%s:attenup:AVERAGE' % rrdname,
220 'DEF:attenupmin=%s:attenup:MIN' % rrdname,
221 'DEF:attenupmax=%s:attenup:MAX' % rrdname,
222
223 'DEF:attendown=%s:attendown:AVERAGE' % rrdname,
224 'DEF:attendownmin=%s:attendown:MIN' % rrdname,
225 'DEF:attendownmax=%s:attendown:MAX' % rrdname,
226 226
227 'CDEF:nmup=nmup_,10,*', 227 'CDEF:nmup=nmup_,10,*',
228 'CDEF:nmupmin=nmupmin_,10,*', 228 'CDEF:nmupmin=nmupmin_,10,*',
229 'CDEF:nmupmax=nmupmax_,10,*', 229 'CDEF:nmupmax=nmupmax_,10,*',
230 'CDEF:nmupdif=nmupmax,nmupmin,-', 230 'CDEF:nmupdif=nmupmax,nmupmin,-',
231 231
232 'LINE0:nmupmin#000000:',
233 'AREA:nmupdif#5c5cff::STACK',
234 'LINE1:nmup#0000ff:Noise Margin - Up (1/10 dB)',
235
236 'DEF:nmdown_=%s:nmdown:AVERAGE' % rrdname,
237 'DEF:nmdownmin_=%s:nmdown:MIN' % rrdname,
238 'DEF:nmdownmax_=%s:nmdown:MAX' % rrdname,
239
232 'CDEF:nmdown=nmdown_,10,*', 240 'CDEF:nmdown=nmdown_,10,*',
233 'CDEF:nmdownmin=nmdownmin_,10,*', 241 'CDEF:nmdownmin=nmdownmin_,10,*',
234 'CDEF:nmdownmax=nmdownmax_,10,*', 242 'CDEF:nmdownmax=nmdownmax_,10,*',
235 'CDEF:nmdowndif=nmdownmax,nmdownmin,-', 243 'CDEF:nmdowndif=nmdownmax,nmdownmin,-',
236 244
245 'DEF:attenup=%s:attenup:AVERAGE' % rrdname,
246 'DEF:attenupmin=%s:attenup:MIN' % rrdname,
247 'DEF:attenupmax=%s:attenup:MAX' % rrdname,
248
237 'CDEF:attenupdif=attenupmax,attenupmin,-', 249 'CDEF:attenupdif=attenupmax,attenupmin,-',
238 250
251 'DEF:attendown=%s:attendown:AVERAGE' % rrdname,
252 'DEF:attendownmin=%s:attendown:MIN' % rrdname,
253 'DEF:attendownmax=%s:attendown:MAX' % rrdname,
254
239 'CDEF:attendowndif=attendownmax,attendownmin,-', 255 'CDEF:attendowndif=attendownmax,attendownmin,-',
240 256
241 'LINE0:nmupmin#000000:', 257 'DEF:powerup_=%s:powerup:AVERAGE' % rrdname,
242 'AREA:nmupdif#5c5cff::STACK', 258 'DEF:powerupmin_=%s:powerup:MIN' % rrdname,
243 'LINE1:nmup#0000ff:Noise Margin - Up (1/10 dB)', 259 'DEF:powerupmax_=%s:powerup:MAX' % rrdname,
260
261 'DEF:powerdown_=%s:powerdown:AVERAGE' % rrdname,
262 'DEF:powerdownmin_=%s:powerdown:MIN' % rrdname,
263 'DEF:powerdownmax_=%s:powerdown:MAX' % rrdname,
264
244 265
245 'LINE0:nmdownmin#000000:', 266 'LINE0:nmdownmin#000000:',
246 'AREA:nmdowndif#009a00::STACK', 267 'AREA:nmdowndif#009a00::STACK',
247 'LINE1:nmdown#00ff00:Noise Margin - Down (1/10 dB)', 268 'LINE1:nmdown#00ff00:Noise Margin - Down (1/10 dB)',
248 269
317 up.min 0 338 up.min 0
318 down.label Down 339 down.label Down
319 down.type GAUGE 340 down.type GAUGE
320 down.max 15000 341 down.max 15000
321 down.min 0 342 down.min 0
322 upmax.label Up 343 upmax.label Up (max)
323 upmax.type GAUGE 344 upmax.type GAUGE
324 upmax.max 150000 345 upmax.max 150000
325 upmax.min 0 346 upmax.min 0
326 downmax.label Down 347 downmax.label Down (max)
327 downmax.type GAUGE 348 downmax.type GAUGE
328 downmax.max 150000 349 downmax.max 150000
329 downmax.min 0''' 350 downmax.min 0'''
330 sys.exit(0) 351 sys.exit(0)
331 if options.update or options.munin: 352 if options.update or options.munin: