root / plugins / icecast / icecast_ @ 17f78427
Historique | Voir | Annoter | Télécharger (5,65 ko)
| 1 | 45b75591 | Markus Lindenberg | #!/usr/bin/env python2.5 |
|---|---|---|---|
| 2 | """ |
||
| 3 | Plugin to monitor icecast2 streaming servers. |
||
| 4 | |||
| 5 | Author: Markus Lindenberg <markus.lindenberg@gmail.com> |
||
| 6 | 97c567b2 | Markus Lindenberg and others | COntributors: Julien 'Lta' BALLET <elthariel@gmail.com> |
| 7 | 45b75591 | Markus Lindenberg | Version: 2009111101 |
| 8 | |||
| 9 | This program is free software: you can redistribute it and/or modify |
||
| 10 | it under the terms of the GNU General Public License as published by |
||
| 11 | the Free Software Foundation, either version 3 of the License, or |
||
| 12 | (at your option) any later version. |
||
| 13 | |||
| 14 | This program is distributed in the hope that it will be useful, |
||
| 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 17 | GNU General Public License for more details. |
||
| 18 | |||
| 19 | You should have received a copy of the GNU General Public License |
||
| 20 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
| 21 | |||
| 22 | Usage: |
||
| 23 | - Link or copy to /etc/munin/plugins |
||
| 24 | - To enable listening duration, traffic and uptime stats, |
||
| 25 | also link to icecast_duration, icecast_traffic and |
||
| 26 | icecast_uptime respectively. |
||
| 27 | |||
| 28 | Configuration: |
||
| 29 | - Enter your server, user and pw below |
||
| 30 | (The plugin does not need to run on |
||
| 31 | the same host as your icecast2 server) |
||
| 32 | - Optionally provide hosts to exclude. |
||
| 33 | 97c567b2 | Markus Lindenberg and others | - Optionally provide sources to exclude. |
| 34 | 45b75591 | Markus Lindenberg | |
| 35 | Possible TODOs: |
||
| 36 | - use autoconf |
||
| 37 | - use munin's configuration system |
||
| 38 | - documentation |
||
| 39 | |||
| 40 | """ |
||
| 41 | |||
| 42 | # CONFIGURATION |
||
| 43 | |||
| 44 | 97c567b2 | Markus Lindenberg and others | server = "localhost:8000" |
| 45 | user = "admin" |
||
| 46 | pw = "yourpassword" |
||
| 47 | 45b75591 | Markus Lindenberg | |
| 48 | # Exclude these hosts when calculating listening duration / listeners count |
||
| 49 | # (we use this to prevent our aircheck recording system from appearing in the stats) |
||
| 50 | #exclude = ("123.123.123.123",)
|
||
| 51 | exclude = () |
||
| 52 | |||
| 53 | 17f78427 | Lars Kruse | # Exclude these sources from calculation. This is useful to excluse special sources like |
| 54 | 97c567b2 | Markus Lindenberg and others | # fallback sources which doesn't expose the same informations and then break this script |
| 55 | ac7a7ccf | Julien 'Lta' BALLET | # Ever add fallback sources to this list |
| 56 | #source_exclude = ["/fallback.mp3", "/fallback.ogg"] |
||
| 57 | source_exclude = ("/fallback.mp3", "/fallback.ogg")
|
||
| 58 | |||
| 59 | 45b75591 | Markus Lindenberg | # /CONFIGURATION |
| 60 | |||
| 61 | from sys import argv, exit, stderr |
||
| 62 | import urllib |
||
| 63 | from xml.etree.ElementTree import ElementTree |
||
| 64 | from os.path import basename |
||
| 65 | |||
| 66 | class IcecastURLopener(urllib.FancyURLopener): |
||
| 67 | def prompt_user_passwd(self, host, realm): |
||
| 68 | return (user, pw) |
||
| 69 | |||
| 70 | opener = IcecastURLopener() |
||
| 71 | f = opener.open("http://%s/admin/listmounts" % server)
|
||
| 72 | |||
| 73 | tree = ElementTree() |
||
| 74 | tree.parse(f) |
||
| 75 | f.close() |
||
| 76 | |||
| 77 | sources = [] |
||
| 78 | for s in tree.getiterator("source"):
|
||
| 79 | ac7a7ccf | Julien 'Lta' BALLET | if s.attrib["mount"] not in source_exclude: |
| 80 | sources.append({"mount": s.attrib["mount"],
|
||
| 81 | "listeners": s.find("listeners").text,
|
||
| 82 | "connected": s.find("Connected").text})
|
||
| 83 | 45b75591 | Markus Lindenberg | |
| 84 | plugin_name = basename(argv[0]) |
||
| 85 | |||
| 86 | try: |
||
| 87 | if argv[1] == "config": |
||
| 88 | if plugin_name == "icecast_duration": |
||
| 89 | print "graph_title Icecast client listening duration" |
||
| 90 | print "graph_args --base 1000 -l 0" |
||
| 91 | print "graph_scale no" |
||
| 92 | e3899a30 | Gabriele Pohl | print "graph_category streaming" |
| 93 | 45b75591 | Markus Lindenberg | print "graph_vlabel minutes" |
| 94 | print "avg.label average listening duration" |
||
| 95 | print "mdn.label median listening duration" |
||
| 96 | elif plugin_name == "icecast_uptime": |
||
| 97 | print "graph_title Icecast source uptime" |
||
| 98 | print "graph_args --base 1000 -l 0" |
||
| 99 | print "graph_scale no" |
||
| 100 | e3899a30 | Gabriele Pohl | print "graph_category streaming" |
| 101 | 45b75591 | Markus Lindenberg | print "graph_vlabel hours" |
| 102 | for s in sources: |
||
| 103 | ac7a7ccf | Julien 'Lta' BALLET | print "%s.label source %s" % (s["mount"].strip("/").replace(".","_").replace("-","_"), s["mount"])
|
| 104 | 45b75591 | Markus Lindenberg | elif plugin_name == "icecast_traffic": |
| 105 | print "graph_title Icecast outgoing traffic" |
||
| 106 | print "graph_args --base 1024 -l 0" |
||
| 107 | e3899a30 | Gabriele Pohl | print "graph_category streaming" |
| 108 | 45b75591 | Markus Lindenberg | print "graph_vlabel bytes / second" |
| 109 | is_first = True |
||
| 110 | for s in sources: |
||
| 111 | ac7a7ccf | Julien 'Lta' BALLET | sname = s["mount"].strip("/").replace(".","_").replace("-","_")
|
| 112 | 45b75591 | Markus Lindenberg | print "%s.label source %s" % (sname, s["mount"]) |
| 113 | print "%s.type DERIVE" % sname |
||
| 114 | print "%s.min 0" % sname |
||
| 115 | if is_first: |
||
| 116 | print "%s.draw AREA" % sname |
||
| 117 | is_first = False |
||
| 118 | else: |
||
| 119 | print "%s.draw STACK" % sname |
||
| 120 | else: |
||
| 121 | print "graph_title Icecast listeners count" |
||
| 122 | print "graph_args --base 1000 -l 0" |
||
| 123 | print "graph_scale no" |
||
| 124 | e3899a30 | Gabriele Pohl | print "graph_category streaming" |
| 125 | 45b75591 | Markus Lindenberg | print "graph_vlabel listeners" |
| 126 | is_first = True |
||
| 127 | for s in sources: |
||
| 128 | ac7a7ccf | Julien 'Lta' BALLET | sname = s["mount"].strip("/").replace(".","_").replace("-","_")
|
| 129 | 45b75591 | Markus Lindenberg | print "%s.label source %s" % (sname, s["mount"]) |
| 130 | if is_first: |
||
| 131 | print "%s.draw AREA" % sname |
||
| 132 | is_first = False |
||
| 133 | else: |
||
| 134 | print "%s.draw STACK" % sname |
||
| 135 | |||
| 136 | exit(0) |
||
| 137 | |||
| 138 | except IndexError: |
||
| 139 | pass |
||
| 140 | |||
| 141 | if plugin_name == "icecast_uptime": |
||
| 142 | for s in sources: |
||
| 143 | ac7a7ccf | Julien 'Lta' BALLET | print "%s.value %s" % (s["mount"].strip("/").replace(".","_").replace("-","_"), int(s["connected"]) / 3600.)
|
| 144 | 17f78427 | Lars Kruse | |
| 145 | 45b75591 | Markus Lindenberg | elif plugin_name == "icecast_traffic": |
| 146 | f = opener.open("http://%s/admin/stats.xml" % server)
|
||
| 147 | tree = ElementTree() |
||
| 148 | tree.parse(f) |
||
| 149 | f.close() |
||
| 150 | for s in tree.getiterator("source"):
|
||
| 151 | ac7a7ccf | Julien 'Lta' BALLET | print "%s.value %s" % (s.attrib["mount"].strip("/").replace(".","_").replace("-","_"), s.find("total_bytes_sent").text)
|
| 152 | 45b75591 | Markus Lindenberg | |
| 153 | else: |
||
| 154 | durations = {}
|
||
| 155 | for s in sources: |
||
| 156 | durations[s["mount"]] = [] |
||
| 157 | f = opener.open("http://%s/admin/listclients?mount=%s" % (server, s["mount"]))
|
||
| 158 | tree = ElementTree() |
||
| 159 | tree.parse(f) |
||
| 160 | f.close() |
||
| 161 | for l in tree.getiterator("listener"):
|
||
| 162 | if l.find("IP").text not in exclude:
|
||
| 163 | durations[s["mount"]].append(int(l.find("Connected").text))
|
||
| 164 | |||
| 165 | if plugin_name == "icecast_duration": |
||
| 166 | if not durations: |
||
| 167 | exit(0) |
||
| 168 | alldurations = reduce(lambda x, y: x+y, durations.values()) |
||
| 169 | alldurations.sort() |
||
| 170 | print "avg.value %s" % (sum(alldurations) / float(len(alldurations)) / 60.,) |
||
| 171 | if len(alldurations) % 2: |
||
| 172 | median = alldurations[len(alldurations) / 2] / 60. |
||
| 173 | elif len(alldurations): |
||
| 174 | median = (alldurations[len(alldurations) / 2 - 1] + alldurations[len(alldurations) / 2]) / 2. / 60. |
||
| 175 | else: |
||
| 176 | median = 0 |
||
| 177 | print "mdn.value %s" % median |
||
| 178 | else: |
||
| 179 | for s in sources: |
||
| 180 | ac7a7ccf | Julien 'Lta' BALLET | print "%s.value %s" % (s["mount"].strip("/").replace(".","_").replace("-","_"), len(durations[s["mount"]]))
|
