From 6abc523a58df6fa62f8ce5c7b1ffa379ccbe480e Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Sat, 7 Dec 2019 22:41:20 -0800 Subject: [PATCH] add plugin for Technicolor TC8715D cable modem stats --- plugins/router/technicolor_tc8715d | 233 +++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100755 plugins/router/technicolor_tc8715d diff --git a/plugins/router/technicolor_tc8715d b/plugins/router/technicolor_tc8715d new file mode 100755 index 000000000..49a6c927a --- /dev/null +++ b/plugins/router/technicolor_tc8715d @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 + +""" +=pod + +=encoding utf8 + +=head1 NAME + +technicolor_tc8715d - Munin plugin for graphing statistics from +Technicolor TC8715D cable modems. + +=head1 DESCRIPTION + +The following values are graphed: + +=over + +=item * + +upstream and downstream power levels + +=item * + +downstream signal to noise ratio + +=item * + +downstream signal statistics (codeword counts) + +=back + +=head1 USAGE + +Install as you would with any Munin plugin. No configuration is +needed. Requires the multigraph and dirtyconfig capabilities available +in munin 2.0 and newer. + +=head1 NOTES + +Developed and tested with Python 3.7.3, Technicolor TC8715D cable +modem hardware revision 1.1, software image name +TC8715D-01.EF.04.38.00-180405-S-FF9-D.img, advanced services +2.6.30-1.0.11mp1-g24a0ad5-dirty. + +=head1 DEVELOPMENT + +The latest version of this plugin can be found in the munin contrib +repository at https://github.com/munin-monitoring/contrib. Issues +with this plugin may be reported there. Patches accepted through the +normal github process of forking the repository and submitting a +pull request with your commits. + +=head1 AUTHOR + +Copyright © 2019 Kenyon Ralph + +=head1 LICENSE + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +=cut + +""" + +import html.parser +import urllib.request +import sys + + +class TechnicolorHTMLParser(html.parser.HTMLParser): + def __init__(self): + html.parser.HTMLParser.__init__(self) + self.signaldatapage = list() + + # Number of downstream channels. + self.downstream_channels = 0 + + # Number of upstream channels. + self.upstream_channels = 0 + + self.downstream_SNRs = list() + self.downstream_powers = list() + self.upstream_powers = list() + self.unerrored_codewords = list() + self.correctable_codewords = list() + self.uncorrectable_codewords = list() + + def handle_data(self, data): + data = data.strip() + if data != "": + self.signaldatapage.append(data) + + def process(self): + # Delete the junk before the statistics start. This list + # should start with 'Downstream' after this deletion. + del self.signaldatapage[0:119] + + index_positions = [i for i, x in enumerate(self.signaldatapage) if x == "Index"] + lock_status_positions = [ + i for i, x in enumerate(self.signaldatapage) if x == "Lock Status" + ] + self.downstream_channels = lock_status_positions[0] - index_positions[0] - 1 + self.upstream_channels = lock_status_positions[1] - index_positions[1] - 1 + + self.downstream_SNRs = self.signaldatapage[ + 6 + 3 * self.downstream_channels : 22 + 3 * self.downstream_channels + ] + self.downstream_SNRs = [x.split()[0] for x in self.downstream_SNRs] + + self.downstream_powers = self.signaldatapage[ + 7 + 4 * self.downstream_channels : 23 + 4 * self.downstream_channels + ] + self.downstream_powers = [x.split()[0] for x in self.downstream_powers] + + self.upstream_powers = self.signaldatapage[ + 15 + + 6 * self.downstream_channels + + 4 * self.upstream_channels : 15 + + 6 * self.downstream_channels + + 5 * self.upstream_channels + ] + self.upstream_powers = [x.split()[0] for x in self.upstream_powers] + + self.unerrored_codewords = self.signaldatapage[ + 19 + + 6 * self.downstream_channels + + 7 * self.upstream_channels : 19 + + 7 * self.downstream_channels + + 7 * self.upstream_channels + ] + + self.correctable_codewords = self.signaldatapage[ + 20 + + 7 * self.downstream_channels + + 7 * self.upstream_channels : 20 + + 8 * self.downstream_channels + + 7 * self.upstream_channels + ] + + self.uncorrectable_codewords = self.signaldatapage[ + 21 + + 8 * self.downstream_channels + + 7 * self.upstream_channels : 21 + + 9 * self.downstream_channels + + 7 * self.upstream_channels + ] + + +if len(sys.argv) != 2 or sys.argv[1] != "config": + print( + "Error: plugin designed for the dirtyconfig protocol, must be run with the config argument" + ) + sys.exit(1) + +parser = TechnicolorHTMLParser() + +for line in urllib.request.urlopen("http://192.168.100.1/vendor_network.asp"): + parser.feed(line.decode()) + +parser.process() + +print( + """multigraph technicolor_tc8715d_power +graph_title Technicolor TC8715D Cable Modem Power +graph_vlabel Signal Strength (dBmV) +graph_info This graph shows the channel power values reported by a Technicolor TC8715D cable modem. +graph_category network""" +) + +for i in range(parser.downstream_channels): + print( + f"""ds_power_{i+1}.label Channel {i+1} Downstream Power +ds_power_{i+1}.type GAUGE +ds_power_{i+1}.value {parser.downstream_powers[i]}""" + ) + +for i in range(parser.upstream_channels): + print( + f"""us_power_{i+1}.label Channel {i+1} Upstream Power +us_power_{i+1}.type GAUGE +us_power_{i+1}.value {parser.upstream_powers[i]}""" + ) + +print( + """multigraph technicolor_tc8715d_snr +graph_title Technicolor TC8715D Cable Modem SNR +graph_vlabel Signal-to-Noise Ratio (dB) +graph_info This graph shows the downstream signal-to-noise ratio reported by a Technicolor TC8715D cable modem. +graph_category network""" +) + +for i in range(parser.downstream_channels): + print( + f"""snr_{i+1}.label Channel {i+1} SNR +snr_{i+1}.type GAUGE +snr_{i+1}.value {parser.downstream_SNRs[i]}""" + ) + +print( + """multigraph technicolor_tc8715d_codewords +graph_title Technicolor TC8715D Cable Modem Codewords +graph_vlabel Codewords/${graph_period} +graph_info This graph shows the downstream codeword rates reported by a Technicolor TC8715D cable modem. +graph_category network""" +) + +for i in range(parser.downstream_channels): + print( + f"""unerr_{i+1}.label Channel {i+1} Unerrored Codewords +unerr_{i+1}.type DERIVE +unerr_{i+1}.min 0 +unerr_{i+1}.value {parser.unerrored_codewords[i]} +corr_{i+1}.label Channel {i+1} Correctable Codewords +corr_{i+1}.type DERIVE +corr_{i+1}.min 0 +corr_{i+1}.value {parser.correctable_codewords[i]} +uncorr_{i+1}.label Channel {i+1} Uncorrectable Codewords +uncorr_{i+1}.type DERIVE +uncorr_{i+1}.min 0 +uncorr_{i+1}.value {parser.uncorrectable_codewords[i]}""" + )