diff --git a/plugins/network/ath9k_ b/plugins/network/ath9k_ new file mode 100755 index 00000000..405b93c2 --- /dev/null +++ b/plugins/network/ath9k_ @@ -0,0 +1,460 @@ +#!/bin/sh +# weird shebang? See below: "interpreter selection" +# +# Collect information related to ath9k wireless events and states. +# * rate control statistics ("rc_stats") +# * events (dropped, transmitted, beacon loss, ...) +# * traffic (packets, bytes) +# +# All data is collected for each separate station (in case of multiple +# connected peers). Combined graphs are provided as a summary. +# +# +# This plugin works with the following python interpreters: +# * Python 3 +# * micropython +# +# +# Copyright (C) 2015 Lars Kruse +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Magic markers +#%# capabilities=autoconf suggest +#%# family=auto + +"""true" +# ****************** Interpreter Selection *************** +# This unbelievable dirty hack allows to find a suitable python interpreter. +# This is specifically useful for OpenWRT where typically only micropython is available. +# +# This "execution hack" works as follows: +# * the script is executed by busybox ash or another shell +# * the above line (three quotes before and one quote after 'true') evaluates differently for shell and python: +# * shell: run "true" (i.e. nothing happens) +# * python: ignore everything up to the next three consecutive quotes +# Thus we may place shell code here that will take care for selecting an interpreter. + +# prefer micropython if it is available - otherwise fall back to any python (2 or 3) +if which micropython >/dev/null; then + /usr/bin/micropython "$0" "$@" +else + python3 "$0" "$@" +fi +exit $? + +# For shell: ignore everything starting from here until the last line of this file. +# This is necessary for syntax checkers that try to complain about invalid shell syntax below. +true < IP """ + arp_cache = {} + # example content: + # IP address HW type Flags HW address Mask Device + # 192.168.2.70 0x1 0x0 00:00:00:00:00:00 * eth0.10 + # 192.168.12.76 0x1 0x2 24:a4:3c:fd:76:98 * eth1.10 + for line in open("/proc/net/arp", "r").read().split("\n"): + # skip empty lines + if not line: continue + tokens = line.split() + ip, mac = tokens[0], tokens[3] + # the header line can be ignored - all other should have well-formed MACs + if not ":" in mac: continue + # ignore remote peers outside of the broadcast domain + if mac == "00:00:00:00:00:00": continue + arp_cache[mac] = ip + return arp_cache + + def _parse_stations(self): + stations_base = os.path.join(self._path, "stations") + arp_cache = self._parse_arp_cache() + for item in os.listdir(stations_base): + peer_mac = item + # use the IP or fall back to the MAC without separators (":") + if peer_mac in arp_cache: + label = arp_cache[peer_mac] + key = peer_mac.replace(":", "") + else: + label = peer_mac + key = "host_" + peer_mac.replace(":", "").replace(".", "") + yield Station(label, key, os.path.join(stations_base, item)) + + def get_config(self, scope): + yield from Station.get_summary_config(scope, self.stations, self._graph_base) + for station in self.stations: + yield from station.get_config(scope, self._graph_base) + yield "" + + def get_values(self, scope): + yield from Station.get_summary_values(scope, self.stations, self._graph_base) + for station in self.stations: + yield from station.get_values(scope, self._graph_base) + yield "" + + +class Ath9kDriver: + + def __init__(self, path, graph_base): + self._path = path + self._graph_base = graph_base + self.interfaces = tuple(self._parse_interfaces()) + + def _parse_interfaces(self): + for phy in os.listdir(self._path): + phy_path = os.path.join(self._path, phy) + for item in os.listdir(phy_path): + if item.startswith("netdev:"): + wifi = item.split(":", 1)[1] + label = "{phy}/{interface}".format(phy=phy, interface=wifi) + wifi_path = os.path.join(phy_path, item) + graph_base = "{base}_{phy}_{interface}".format(base=self._graph_base, phy=phy, interface=wifi) + yield WifiInterface(label, wifi_path, graph_base) + + def get_config(self, scope): + for interface in self.interfaces: + yield from interface.get_config(scope) + + def get_values(self, scope): + for interface in self.interfaces: + yield from interface.get_values(scope) + + + +def _get_up_down_pair(unit, key_up, key_down, factor=None, divider=None, use_negative=True): + """ return all required statements for a munin-specific up/down value pair + "factor" or "divider" can be given for unit conversions + """ + for key in (key_up, key_down): + if use_negative: + yield "{key}.label {unit}".format(key=key, unit=unit) + else: + yield "{key}.label {key} {unit}".format(key=key, unit=unit) + yield "{key}.type COUNTER".format(key=key) + if factor: + yield "{key}.cdef {key},{factor},*".format(key=key, factor=factor) + if divider: + yield "{key}.cdef {key},{divider},/".format(key=key, divider=divider) + if use_negative: + yield "{key_down}.graph no".format(key_down=key_down) + yield "{key_up}.negative {key_down}".format(key_up=key_up, key_down=key_down) + + +def get_scope(): + called_name = os.path.basename(sys.argv[0]) + name_prefix = "ath9k_" + if called_name.startswith(name_prefix): + scope = called_name[len(name_prefix):] + if not scope in PLUGIN_SCOPES: + print_error("Invalid scope requested: {0} (expected: {1})".format(scope, PLUGIN_SCOPES)) + sys.exit(2) + else: + print_error("Invalid filename - failed to discover plugin scope") + sys.exit(2) + return scope + + +def print_error(message): + # necessary fallback for micropython + linesep = getattr(os, "linesep", "\n") + sys.stderr.write(message + linesep) + + +if __name__ == "__main__": + ath9k = Ath9kDriver(SYS_BASE_DIR, GRAPH_BASE_NAME) + # parse arguments + if len(sys.argv) > 1: + if sys.argv[1]=="config": + for item in ath9k.get_config(get_scope()): + print(item) + sys.exit(0) + elif sys.argv[1] == "autoconf": + if os.path.exists(SYS_BASE_PATH): + print('yes') + else: + print('no') + sys.exit(0) + elif sys.argv[1] == "suggest": + for scope in PLUGIN_SCOPES: + print(scope) + sys.exit(0) + elif sys.argv[1] == "version": + print_error('olsrd Munin plugin, version %s' % plugin_version) + sys.exit(0) + elif sys.argv[1] == "": + # ignore + pass + else: + # unknown argument + print_error("Unknown argument") + sys.exit(1) + + # output values + for item in ath9k.get_values(get_scope()): + print(item) + +# final marker for shell / python hybrid script (see "Interpreter Selection") +EOF = True +EOF