Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / router / arris-sb6183 @ 77e53059

Historique | Voir | Annoter | Télécharger (10,2 ko)

1 7b078749 Nathaniel Clark
#!/usr/bin/env python3
2 1671e356 Nathaniel Clark
3 7b078749 Nathaniel Clark
# Copyright 2020 Nathaniel Clark <nathaniel.clark@misrule.us>
4 1671e356 Nathaniel Clark
#
5 7b078749 Nathaniel Clark
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU Library General Public License as published by
7
# the Free Software Foundation; version 2 only
8 1671e356 Nathaniel Clark
#
9 7b078749 Nathaniel Clark
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Library General Public License for more details.
13 1671e356 Nathaniel Clark
#
14 7b078749 Nathaniel Clark
# You should have received a copy of the GNU Library General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 1671e356 Nathaniel Clark
#
18
19
"""
20
=head1 NAME
21
22
arris-sb6183 - Health monitoring plugin for Arris SB6183 Cable Modem
23
24 7b078749 Nathaniel Clark
=head1 DESCRIPTION
25
26
This provides the following multigraphs:
27
* upstream and downstream power levels
28
* downstream signal to noise ratio
29
* downstream error counts
30 77e53059 Nathaniel Clark
* uptime
31 7b078749 Nathaniel Clark
32
The values are retrieved from the cable modem's status web pages at
33
192.168.100.1. So, this plugin must be installed on a munin node
34
which can access those pages.
35
36 1671e356 Nathaniel Clark
=head1 CONFIGURATION
37
38 13e4b4aa Nathaniel Clark
Make sure 192.168.100.1 is accessible through your firewall.
39 1671e356 Nathaniel Clark
40
To have this register with munin as it's own host set the "env.hostname" in config.
41
Also ensure that the hostname set is listed in munin.conf.
42
43
[arris*]
44
env.hostname modem
45
46 7b078749 Nathaniel Clark
=head1 TESTING
47
48
Developed and tested with:
49
firmware:         D30CM-OSPREY-2.4.0.1-GA-02-NOSH
50
hardware version: 1
51
52 1671e356 Nathaniel Clark
=head1 VERSION
53
54
0.0.1
55
56
=head1 AUTHOR
57
58
Nathaniel Clark <nathaniel.clark@misrule.us>
59
60
=head1 LICENSE
61
62
GPLv2
63
64
=head1 MAGIC MARKERS
65
66
 #%# family=contrib
67
 #%# capabilities=autoconf
68
69
=cut
70
"""
71
72
import re
73
import os
74
import sys
75 7b078749 Nathaniel Clark
from urllib import request
76 1671e356 Nathaniel Clark
77
HOSTNAME = os.getenv("hostname", None)
78 77e53059 Nathaniel Clark
STATUS_URL = "http://192.168.100.1/RgConnect.asp"
79
INFO_URL = "http://192.168.100.1/RgSwInfo.asp"
80 7b078749 Nathaniel Clark
UPCOUNT = 4
81
DOWNCOUNT = 16
82 1671e356 Nathaniel Clark
83
if len(sys.argv) == 2:
84
    if sys.argv[1] == "config":
85
        if HOSTNAME:
86
            print("host_name {0}\n".format(HOSTNAME))
87
88 77e53059 Nathaniel Clark
        # UPTIME
89
        print(
90
            """multigraph arris_uptime
91
graph_title Modem Uptime
92
graph_category system
93
graph_args --base 1000 -l 0
94
graph_vlabel uptime in days
95
graph_scale no
96
graph_category system
97
graph_info This graph shows the number of days that the the host is up and running so far.
98
uptime.label uptime
99
uptime.info The system uptime itself in days.
100
uptime.draw AREA
101
"""
102
        )
103
104 1671e356 Nathaniel Clark
        # POWER
105
        print("multigraph arris_power")
106
        print("graph_title Arris Power (dBmV)")
107
        print("graph_vlabel Power (dBmV)")
108
        print("graph_category network")
109
        for i in range(1, DOWNCOUNT + 1):
110
            print("down_{0}.label Down Ch {1}".format(i, i))
111
            print("down_{0}.type GAUGE".format(i))
112
            print("down_{0}.draw LINE1".format(i))
113
        for i in range(1, UPCOUNT + 1):
114
            print("up_{0}.label Up Ch {1}".format(i, i))
115
            print("up_{0}.type GAUGE".format(i))
116 7b078749 Nathaniel Clark
            print("up_{0}.draw LINE1".format(i))
117 1671e356 Nathaniel Clark
118
        for i in range(1, DOWNCOUNT + 1):
119
            name = "down_{0}".format(i)
120
            print("\nmultigraph arris_power.{0}".format(name))
121
            print("graph_title Downstream Power for Channel {0} (dBmV)".format(i))
122
            print("graph_category network")
123
            print("power.label dBmV")
124
            print("power.type GAUGE")
125
            print("power.draw LINE1")
126
        for i in range(1, UPCOUNT + 1):
127
            name = "up_{0}".format(i)
128
            print("\nmultigraph arris_power.{0}".format(name))
129
            print("graph_title Upstream Power for Channel {0} (dBmV)".format(i))
130
            print("graph_category network")
131
            print("power.label dBmV")
132
            print("power.type GAUGE")
133
            print("power.draw LINE1")
134
135
        # SNR
136
        print("\nmultigraph arris_snr")
137
        print("graph_title Arris Signal-to-Noise Ratio (dB)")
138
        print("graph_vlabel SNR (dB)")
139
        print("graph_category network")
140
        for i in range(1, DOWNCOUNT + 1):
141
            print("down_{0}.label Ch {1}".format(i, i))
142
            print("down_{0}.type GAUGE".format(i))
143
            print("down_{0}.draw LINE1".format(i))
144
145
        for i in range(1, DOWNCOUNT + 1):
146
            name = "down_{0}".format(i)
147
            print("\nmultigraph arris_snr.{0}".format(name))
148
            print("graph_title SNR on Channel {0} (dB)".format(i))
149
            print("graph_vlabel SNR (dB)")
150
            print("graph_category network")
151
            print("snr.label dB")
152
            print("snr.type GAUGE")
153
            print("snr.draw LINE1")
154
155
        # ERRORS
156
        print("\nmultigraph arris_error")
157
        print("graph_title Arris Channel Errors")
158
        print("graph_category network")
159
        print("graph_args --base 1000")
160
        print("graph_vlabel errors/sec")
161
        print("graph_category network")
162
        print("corr.label Corrected")
163
        print("corr.type DERIVE")
164
        print("corr.min 0")
165
        print("uncr.label Uncorrectable")
166
        print("uncr.type DERIVE")
167
        print("uncr.min 0")
168
        print("uncr.warning 1")
169
170
        for i in range(1, DOWNCOUNT + 1):
171
            name = "down_{0}".format(i)
172
            print("\nmultigraph arris_error.{0}".format(name))
173
            print("graph_title Channel {0} Errors".format(i))
174
            print("graph_args --base 1000")
175
            print("graph_vlabel errors/sec")
176
            print("graph_category network")
177
            print("corr.label Correctable")
178
            print("corr.type DERIVE")
179
            print("corr.min 0")
180
            print("uncr.label Uncorrectable")
181
            print("uncr.type DERIVE")
182
            print("uncr.min 0")
183
            print("uncr.warning 1")
184
185
        sys.exit(0)
186
187
    if sys.argv[1] == "autoconfig":
188
        try:
189 7b078749 Nathaniel Clark
            from lxml import html
190
191 77e53059 Nathaniel Clark
            resp = request.urlopen(STATUS_URL)
192 7b078749 Nathaniel Clark
        except ImportError:
193
            print("no (missing lxml module)")
194
        except OSError:
195 1671e356 Nathaniel Clark
            print("no (no router)")
196
        else:
197 7b078749 Nathaniel Clark
            if resp.status == 200:
198 1671e356 Nathaniel Clark
                print("yes")
199
            else:
200
                print("no (Bad status code: %d)" % page.status_code)
201
        sys.exit(0)
202
203 7b078749 Nathaniel Clark
from lxml import html
204
205 1671e356 Nathaniel Clark
rxblank = re.compile(r"[\x00\n\r\t ]+", re.MULTILINE)
206
rxcomment = re.compile(r"<!--.*?-->")
207
rxscript = re.compile(r"<script.*?</script>", re.MULTILINE)
208
209 77e53059 Nathaniel Clark
210
def process_url(url):
211
    """
212
    Extract simpleTables from page at URL
213
    """
214
    try:
215
        resp = request.urlopen(url)
216
    except OSError:
217
        print("failed to contact router", file=sys.stderr)
218
        return []
219
    if resp.status != 200:
220
        print(
221
            "failed to get status page %d: %s" % (resp.status, resp.reason),
222
            file=sys.stderr,
223
        )
224
        return []
225
    data = rxscript.sub(
226 7b078749 Nathaniel Clark
        "",
227 77e53059 Nathaniel Clark
        rxcomment.sub(
228
            "",
229
            rxblank.sub(
230
                " ", "".join(map(lambda x: x.decode("utf-8"), resp.readlines()))
231
            ),
232
        ),
233
    )
234
    dom = html.fromstring(data)
235
236
    return dom.xpath('//table[contains(@class, "simpleTable")]')
237
238
239
print("multi_graph arris_uptime")
240
arr = process_url(INFO_URL)
241
if arr:
242
    trs = arr[1].findall("tr")
243
    # drop title
244
    trs.pop(0)
245 1671e356 Nathaniel Clark
246 77e53059 Nathaniel Clark
    date = "".join(trs[0].findall("td")[1].itertext()).strip()
247 1671e356 Nathaniel Clark
248 77e53059 Nathaniel Clark
    arr = date.split(" ")
249
    rx = re.compile(r"[hms]")
250
    days = int(arr[0])
251
    hms = rx.sub("", arr[2]).split(":")
252 1671e356 Nathaniel Clark
253 77e53059 Nathaniel Clark
    seconds = ((days * 24 + int(hms[0])) * 60 + int(hms[1])) * 60 + int(hms[2])
254
    print("uptime.value {0}".format(seconds / 86400.0))
255
else:
256
    print("uptime.value U")
257
258
259
arr = process_url(STATUS_URL)
260
if arr:
261
    downstream = arr[1]
262
    upstream = arr[2]
263
264
    trs = downstream.findall("tr")
265
    # drop title
266
    trs.pop(0)
267
268
    headings = ["".join(x.itertext()).strip() for x in trs.pop(0).findall("td")]
269
    # ['Channel', 'Lock Status', 'Modulation', 'Channel ID', 'Frequency', 'Power', 'SNR', 'Corrected', 'Uncorrectables']
270
else:
271
    trs = []
272
    headings = []
273 1671e356 Nathaniel Clark
274
# Summation Graphs
275
correct = 0
276
uncorr = 0
277
power = {"up": ["U"] * UPCOUNT, "down": ["U"] * DOWNCOUNT}
278
snr = ["U"] * DOWNCOUNT
279
for row in trs:
280
    data = dict(
281 7b078749 Nathaniel Clark
        zip(headings, ["".join(x.itertext()).strip() for x in row.findall("td")])
282 1671e356 Nathaniel Clark
    )
283
    uncorr += int(data["Uncorrectables"])
284
    correct += int(data["Corrected"])
285
286
    channel = int(data["Channel"])
287
288 77e53059 Nathaniel Clark
    print("\nmultigraph arris_power.down_{0}".format(channel))
289 1671e356 Nathaniel Clark
    value = data["Power"].split(" ")[0]
290
    print("power.value {0}".format(value))
291
    power["down"][channel - 1] = value
292
293
    print("multigraph arris_snr.down_{0}".format(channel))
294
    value = data["SNR"].split(" ")[0]
295
    print("snr.value {0}".format(value))
296
    snr[channel - 1] = value
297
298
    print("multigraph arris_error.down_{0}".format(channel))
299
    print("corr.value {0}".format(data["Corrected"]))
300
    print("uncr.value {0}".format(data["Uncorrectables"]))
301
302
# Fill missing
303
for i in range(len(trs), DOWNCOUNT):
304 77e53059 Nathaniel Clark
    print("\nmultigraph arris_power.down_{0}".format(i + 1))
305 1671e356 Nathaniel Clark
    print("power.value U")
306
307
    print("multigraph arris_snr.down_{0}".format(i + 1))
308
    print("snr.value U")
309
310
    print("multigraph arris_error.down_{0}".format(i + 1))
311
    print("corr.value U")
312
    print("uncr.value U")
313
314
print("multigraph arris_error")
315 77e53059 Nathaniel Clark
if arr:
316
    print("corr.value {0}".format(correct))
317
    print("uncr.value {0}".format(uncorr))
318
else:
319
    print("corr.value U")
320
    print("uncr.value U")
321 1671e356 Nathaniel Clark
322
print("multigraph arris_snr")
323
for i in range(0, DOWNCOUNT):
324
    print("down_{0}.value {1}".format(i + 1, snr[i]))
325
326 77e53059 Nathaniel Clark
if arr:
327
    trs = upstream.findall("tr")
328
    # drop title
329
    trs.pop(0)
330
331
    headings = ["".join(x.itertext()).strip() for x in trs.pop(0).findall("td")]
332
    # ['Channel', 'Lock Status', 'US Channel Type', 'Channel ID', 'Symbol Rate', 'Frequency', 'Power']
333 1671e356 Nathaniel Clark
334
for row in trs:
335
    data = dict(
336 7b078749 Nathaniel Clark
        zip(headings, ["".join(x.itertext()).strip() for x in row.findall("td")])
337 1671e356 Nathaniel Clark
    )
338
    channel = int(data["Channel"])
339
    print("multigraph arris_power.up_{0}".format(channel))
340
    value = data["Power"].split(" ")[0]
341
    print("power.value {0}".format(value))
342
    power["up"][channel - 1] = value
343
344
# Fill missing
345
for i in range(len(trs), UPCOUNT):
346
    print("multigraph arris_power.up_{0}".format(i + 1))
347
    print("power.value U")
348
349
print("multigraph arris_power")
350
for i in range(0, DOWNCOUNT):
351
    print("down_{0}.value {1}".format(i + 1, power["down"][i]))
352
for i in range(0, UPCOUNT):
353
    print("up_{0}.value {1}".format(i + 1, power["up"][i]))