#!/usr/bin/env python2.5 """ Plugin to monitor icecast2 streaming servers. Author: Markus Lindenberg COntributors: Julien 'Lta' BALLET Version: 2009111101 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 . Usage: - Link or copy to /etc/munin/plugins - To enable listening duration, traffic and uptime stats, also link to icecast_duration, icecast_traffic and icecast_uptime respectively. Configuration: - Enter your server, user and pw below (The plugin does not need to run on the same host as your icecast2 server) - Optionally provide hosts to exclude. - Optionally provide sources to exclude. Possible TODOs: - use autoconf - use munin's configuration system - documentation """ # CONFIGURATION server = "localhost:8000" user = "admin" pw = "yourpassword" # Exclude these hosts when calculating listening duration / listeners count # (we use this to prevent our aircheck recording system from appearing in the stats) #exclude = ("123.123.123.123",) exclude = () # Exclude these sources from calculation. This is useful to exclude special sources like # fallback sources which doesn't expose the same information and then break this script # Ever add fallback sources to this list #source_exclude = ["/fallback.mp3", "/fallback.ogg"] source_exclude = ("/fallback.mp3", "/fallback.ogg") # /CONFIGURATION from sys import argv, exit, stderr import urllib from xml.etree.ElementTree import ElementTree from os.path import basename class IcecastURLopener(urllib.FancyURLopener): def prompt_user_passwd(self, host, realm): return (user, pw) opener = IcecastURLopener() f = opener.open("http://%s/admin/listmounts" % server) tree = ElementTree() tree.parse(f) f.close() sources = [] for s in tree.getiterator("source"): if s.attrib["mount"] not in source_exclude: sources.append({"mount": s.attrib["mount"], "listeners": s.find("listeners").text, "connected": s.find("Connected").text}) plugin_name = basename(argv[0]) try: if argv[1] == "config": if plugin_name == "icecast_duration": print "graph_title Icecast client listening duration" print "graph_args --base 1000 -l 0" print "graph_scale no" print "graph_category streaming" print "graph_vlabel minutes" print "avg.label average listening duration" print "mdn.label median listening duration" elif plugin_name == "icecast_uptime": print "graph_title Icecast source uptime" print "graph_args --base 1000 -l 0" print "graph_scale no" print "graph_category streaming" print "graph_vlabel hours" for s in sources: print "%s.label source %s" % (s["mount"].strip("/").replace(".","_").replace("-","_"), s["mount"]) elif plugin_name == "icecast_traffic": print "graph_title Icecast outgoing traffic" print "graph_args --base 1024 -l 0" print "graph_category streaming" print "graph_vlabel bytes / second" is_first = True for s in sources: sname = s["mount"].strip("/").replace(".","_").replace("-","_") print "%s.label source %s" % (sname, s["mount"]) print "%s.type DERIVE" % sname print "%s.min 0" % sname if is_first: print "%s.draw AREA" % sname is_first = False else: print "%s.draw STACK" % sname else: print "graph_title Icecast listeners count" print "graph_args --base 1000 -l 0" print "graph_scale no" print "graph_category streaming" print "graph_vlabel listeners" is_first = True for s in sources: sname = s["mount"].strip("/").replace(".","_").replace("-","_") print "%s.label source %s" % (sname, s["mount"]) if is_first: print "%s.draw AREA" % sname is_first = False else: print "%s.draw STACK" % sname exit(0) except IndexError: pass if plugin_name == "icecast_uptime": for s in sources: print "%s.value %s" % (s["mount"].strip("/").replace(".","_").replace("-","_"), int(s["connected"]) / 3600.) elif plugin_name == "icecast_traffic": f = opener.open("http://%s/admin/stats.xml" % server) tree = ElementTree() tree.parse(f) f.close() for s in tree.getiterator("source"): print "%s.value %s" % (s.attrib["mount"].strip("/").replace(".","_").replace("-","_"), s.find("total_bytes_sent").text) else: durations = {} for s in sources: durations[s["mount"]] = [] f = opener.open("http://%s/admin/listclients?mount=%s" % (server, s["mount"])) tree = ElementTree() tree.parse(f) f.close() for l in tree.getiterator("listener"): if l.find("IP").text not in exclude: durations[s["mount"]].append(int(l.find("Connected").text)) if plugin_name == "icecast_duration": if not durations: exit(0) alldurations = reduce(lambda x, y: x+y, durations.values()) alldurations.sort() print "avg.value %s" % (sum(alldurations) / float(len(alldurations)) / 60.,) if len(alldurations) % 2: median = alldurations[len(alldurations) / 2] / 60. elif len(alldurations): median = (alldurations[len(alldurations) / 2 - 1] + alldurations[len(alldurations) / 2]) / 2. / 60. else: median = 0 print "mdn.value %s" % median else: for s in sources: print "%s.value %s" % (s["mount"].strip("/").replace(".","_").replace("-","_"), len(durations[s["mount"]]))