root / plugins / network / motorola_sb6141 @ 99542938
Historique | Voir | Annoter | Télécharger (7,15 ko)
| 1 | 2bb6892e | Kenyon Ralph | #!/usr/bin/env python3 |
|---|---|---|---|
| 2 | |||
| 3 | # This plugin graphs the following values of the Motorola SB6141 cable |
||
| 4 | # modem: |
||
| 5 | # |
||
| 6 | # * upstream and downstream power levels |
||
| 7 | # * downstream signal to noise ratio |
||
| 8 | # * downstream signal statistics (codeword counts) |
||
| 9 | # |
||
| 10 | # The values are retrieved from the cable modem's status web pages at |
||
| 11 | # 192.168.100.1. So, this plugin must be installed on a munin node |
||
| 12 | # which can access those pages. |
||
| 13 | # |
||
| 14 | # To install, place this plugin in the node's plugins directory, |
||
| 15 | # /etc/munin/plugins and restart munin-node. |
||
| 16 | # |
||
| 17 | # Developed and tested with firmware SB_KOMODO-1.0.6.16-SCM00-NOSH |
||
| 18 | # (build time Feb 16 2016 11:28:04), hardware version 7.0, boot |
||
| 19 | # version PSPU-Boot(25CLK) 1.0.12.18m3. |
||
| 20 | # |
||
| 21 | # Requires the multigraph and dirtyconfig capabilities available in |
||
| 22 | # munin 2.0 and newer. |
||
| 23 | # |
||
| 24 | # Copyright © 2016 Kenyon Ralph <kenyon@kenyonralph.com> |
||
| 25 | # |
||
| 26 | # This program is free software. It comes without any warranty, to the |
||
| 27 | # extent permitted by applicable law. You can redistribute it and/or |
||
| 28 | # modify it under the terms of the Do What The Fuck You Want To Public |
||
| 29 | # License, Version 2, as published by Sam Hocevar. See |
||
| 30 | # http://www.wtfpl.net/ for more details. |
||
| 31 | # |
||
| 32 | # The latest version of this plugin can be found in the munin contrib |
||
| 33 | # repository at https://github.com/munin-monitoring/contrib. Issues |
||
| 34 | # with this plugin may be reported there. Patches accepted through the |
||
| 35 | # normal github process of forking the repository and submitting a |
||
| 36 | # pull request with your commits. |
||
| 37 | |||
| 38 | import html.parser |
||
| 39 | import urllib.request |
||
| 40 | import sys |
||
| 41 | |||
| 42 | class MotorolaHTMLParser(html.parser.HTMLParser): |
||
| 43 | def __init__(self): |
||
| 44 | html.parser.HTMLParser.__init__(self) |
||
| 45 | self.signaldatapage = list() |
||
| 46 | self.downstream_channels = list() |
||
| 47 | self.downstream_SNRs = list() |
||
| 48 | self.downstream_powers = list() |
||
| 49 | self.upstream_channels = list() |
||
| 50 | self.upstream_powers = list() |
||
| 51 | self.unerrored_codewords = list() |
||
| 52 | self.correctable_codewords = list() |
||
| 53 | self.uncorrectable_codewords = list() |
||
| 54 | |||
| 55 | def handle_data(self, data): |
||
| 56 | data = data.strip() |
||
| 57 | if data != '': self.signaldatapage.append(data) |
||
| 58 | |||
| 59 | def process(self): |
||
| 60 | # first and last elements are just javascript |
||
| 61 | del self.signaldatapage[0] |
||
| 62 | del self.signaldatapage[-1] |
||
| 63 | |||
| 64 | di = iter(self.signaldatapage) |
||
| 65 | |||
| 66 | element = next(di) |
||
| 67 | while element != 'Channel ID': element = next(di) |
||
| 68 | |||
| 69 | while element != 'Frequency': |
||
| 70 | element = next(di) |
||
| 71 | if element != 'Frequency': self.downstream_channels.append(element) |
||
| 72 | |||
| 73 | while element != 'Signal to Noise Ratio': element = next(di) |
||
| 74 | |||
| 75 | while element != 'Downstream Modulation': |
||
| 76 | element = next(di) |
||
| 77 | if element != 'Downstream Modulation': self.downstream_SNRs.append(element.split()[0]) |
||
| 78 | |||
| 79 | while element != 'The Downstream Power Level reading is a snapshot taken at the time this page was requested. Please Reload/Refresh this Page for a new reading': element = next(di) |
||
| 80 | |||
| 81 | while element != 'Upstream': |
||
| 82 | element = next(di) |
||
| 83 | if element != 'Upstream': self.downstream_powers.append(element.split()[0]) |
||
| 84 | |||
| 85 | while element != 'Channel ID': element = next(di) |
||
| 86 | |||
| 87 | while element != 'Frequency': |
||
| 88 | element = next(di) |
||
| 89 | if element != 'Frequency': self.upstream_channels.append(element) |
||
| 90 | |||
| 91 | while element != 'Power Level': element = next(di) |
||
| 92 | |||
| 93 | while element != 'Upstream Modulation': |
||
| 94 | element = next(di) |
||
| 95 | if element != 'Upstream Modulation': self.upstream_powers.append(element.split()[0]) |
||
| 96 | |||
| 97 | while element != 'Total Unerrored Codewords': element = next(di) |
||
| 98 | |||
| 99 | while element != 'Total Correctable Codewords': |
||
| 100 | element = next(di) |
||
| 101 | if element != 'Total Correctable Codewords': self.unerrored_codewords.append(element) |
||
| 102 | |||
| 103 | while element != 'Total Uncorrectable Codewords': |
||
| 104 | element = next(di) |
||
| 105 | if element != 'Total Uncorrectable Codewords': self.correctable_codewords.append(element) |
||
| 106 | |||
| 107 | while True: |
||
| 108 | try: |
||
| 109 | element = next(di) |
||
| 110 | self.uncorrectable_codewords.append(element) |
||
| 111 | except StopIteration: |
||
| 112 | break |
||
| 113 | |||
| 114 | def main(): |
||
| 115 | if len(sys.argv) != 2 or sys.argv[1] != 'config': |
||
| 116 | print('Error: plugin designed for the dirtyconfig protocol, must be run with the config argument')
|
||
| 117 | sys.exit(1) |
||
| 118 | |||
| 119 | parser = MotorolaHTMLParser() |
||
| 120 | for line in urllib.request.urlopen("http://192.168.100.1/cmSignalData.htm"):
|
||
| 121 | parser.feed(line.decode()) |
||
| 122 | parser.process() |
||
| 123 | |||
| 124 | print('multigraph motorola_sb6141_power')
|
||
| 125 | print('graph_title Motorola SB6141 Cable Modem Power')
|
||
| 126 | print('graph_vlabel Signal Strength (dBmV)')
|
||
| 127 | print('graph_info This graph shows the downstream and upstream power reported by a Motorola SB6141 cable modem.')
|
||
| 128 | print('graph_category network')
|
||
| 129 | for c, p in zip(parser.downstream_channels, parser.downstream_powers): |
||
| 130 | print('ds_power_{0}.label Channel {0} Downstream Power'.format(c))
|
||
| 131 | print('ds_power_{0}.type GAUGE'.format(c))
|
||
| 132 | print('ds_power_{0}.value {1}'.format(c, p))
|
||
| 133 | for c, p in zip(parser.upstream_channels, parser.upstream_powers): |
||
| 134 | print('us_power_{0}.label Channel {0} Upstream Power'.format(c))
|
||
| 135 | print('us_power_{0}.type GAUGE'.format(c))
|
||
| 136 | print('us_power_{0}.value {1}'.format(c, p))
|
||
| 137 | |||
| 138 | print('multigraph motorola_sb6141_snr')
|
||
| 139 | print('graph_title Motorola SB6141 Cable Modem SNR')
|
||
| 140 | print('graph_vlabel Signal-to-Noise Ratio (dB)')
|
||
| 141 | print('graph_info This graph shows the downstream signal-to-noise ratio reported by a Motorola SB6141 cable modem.')
|
||
| 142 | print('graph_category network')
|
||
| 143 | for c, snr in zip(parser.downstream_channels, parser.downstream_SNRs): |
||
| 144 | print('snr_chan_{0}.label Channel {0} SNR'.format(c))
|
||
| 145 | print('snr_chan_{0}.type GAUGE'.format(c))
|
||
| 146 | print('snr_chan_{0}.value {1}'.format(c, snr))
|
||
| 147 | |||
| 148 | print('multigraph motorola_sb6141_codewords')
|
||
| 149 | print('graph_title Motorola SB6141 Cable Modem Codewords')
|
||
| 150 | print('graph_vlabel Codewords/${graph_period}')
|
||
| 151 | print('graph_info This graph shows the downstream codeword rates reported by a Motorola SB6141 cable modem.')
|
||
| 152 | print('graph_category network')
|
||
| 153 | for c, unerr in zip(parser.downstream_channels, parser.unerrored_codewords): |
||
| 154 | print('unerr_chan_{0}.label Channel {0} Unerrored Codewords'.format(c))
|
||
| 155 | print('unerr_chan_{0}.type DERIVE'.format(c))
|
||
| 156 | print('unerr_chan_{0}.min 0'.format(c))
|
||
| 157 | print('unerr_chan_{0}.value {1}'.format(c, unerr))
|
||
| 158 | for c, corr in zip(parser.downstream_channels, parser.correctable_codewords): |
||
| 159 | print('corr_chan_{0}.label Channel {0} Correctable Codewords'.format(c))
|
||
| 160 | print('corr_chan_{0}.type DERIVE'.format(c))
|
||
| 161 | print('corr_chan_{0}.min 0'.format(c))
|
||
| 162 | print('corr_chan_{0}.value {1}'.format(c, corr))
|
||
| 163 | for c, uncorr in zip(parser.downstream_channels, parser.uncorrectable_codewords): |
||
| 164 | print('uncorr_chan_{0}.label Channel {0} Uncorrectable Codewords'.format(c))
|
||
| 165 | print('uncorr_chan_{0}.type DERIVE'.format(c))
|
||
| 166 | print('uncorr_chan_{0}.min 0'.format(c))
|
||
| 167 | print('uncorr_chan_{0}.value {1}'.format(c, uncorr))
|
||
| 168 | |||
| 169 | if __name__ == "__main__": |
||
| 170 | main() |
