root / plugins / shoutcast / shoutcast2_multi @ 17f78427
Historique | Voir | Annoter | Télécharger (13,8 ko)
| 1 | 55e02ba4 | Matt West | #!/usr/bin/perl |
|---|---|---|---|
| 2 | # |
||
| 3 | =head1 Shoutcast 2.0.x Plugin |
||
| 4 | |||
| 5 | A Plugin for monitoring a Shoutcast 2.0.x Server (Multigraph) |
||
| 6 | |||
| 7 | =head1 Munin Configuration |
||
| 8 | |||
| 9 | [shoutcast2_multi] |
||
| 10 | env.host 127.0.0.1 *default* |
||
| 11 | env.port 8000 *default* |
||
| 12 | env.pass changeme *default* |
||
| 13 | |||
| 14 | =head2 Munin Configuration Explanation |
||
| 15 | |||
| 16 | host = host we are attempting to connection to, can be ip, or hostname |
||
| 17 | port = port we need to connect to in order to get to admin.cgi |
||
| 18 | pass = password to use to authenticate as admin user |
||
| 19 | |||
| 20 | =head1 AUTHOR |
||
| 21 | |||
| 22 | Matt West < https://github.com/mhwest13 > |
||
| 23 | |||
| 24 | =head1 License |
||
| 25 | |||
| 26 | GPLv2 |
||
| 27 | |||
| 28 | =head1 Magic Markers |
||
| 29 | |||
| 30 | #%# family=auto |
||
| 31 | #%# capabilities=autoconf |
||
| 32 | |||
| 33 | =cut |
||
| 34 | |||
| 35 | use strict; |
||
| 36 | use warnings; |
||
| 37 | use LWP::UserAgent; |
||
| 38 | use XML::Simple; |
||
| 39 | use Munin::Plugin; |
||
| 40 | |||
| 41 | need_multigraph(); |
||
| 42 | |||
| 43 | =head1 Variable Declarations |
||
| 44 | |||
| 45 | This section is mainly for importing / declaring our environment variables. |
||
| 46 | This is were we will import the data from our plugin-conf.d file so we can |
||
| 47 | override the default settings which will only work for Shoutcast test configs. |
||
| 48 | |||
| 49 | =cut |
||
| 50 | |||
| 51 | my $host = $ENV{host} || '127.0.0.1';
|
||
| 52 | my $port = $ENV{port} || 8000;
|
||
| 53 | my $pass = $ENV{pass} || 'changeme';
|
||
| 54 | |||
| 55 | # Initialize hashref for storing results information... |
||
| 56 | my $dataRef; |
||
| 57 | |||
| 58 | # Create a hashref for our graph information that we will call up later... |
||
| 59 | my $graphsRef; |
||
| 60 | |||
| 61 | my $ua = LWP::UserAgent->new(); |
||
| 62 | $ua->agent('Munin Shoutcast Plugin/0.1');
|
||
| 63 | $ua->timeout(5); |
||
| 64 | $ua->credentials($host.':'.$port, 'Shoutcast Server', 'admin'=>$pass); |
||
| 65 | |||
| 66 | =head1 Graphs Declarations |
||
| 67 | |||
| 68 | The following section of code contains our graph information. This is the data |
||
| 69 | provided to Munin, so that it may properly draw our graphs based on the values |
||
| 70 | the plugin returns. |
||
| 71 | |||
| 72 | While you are free to change colors or labels changing the type, min, or max |
||
| 73 | could cause unfortunate problems with your graphs. |
||
| 74 | |||
| 75 | =cut |
||
| 76 | |||
| 77 | $graphsRef->{active} = {
|
||
| 78 | config => {
|
||
| 79 | args => '--base 1000 --lower-limit 0', |
||
| 80 | vlabel => 'Is a DJ Actively Connected?', |
||
| 81 | a8d024ac | dipohl | category => 'streaming', |
| 82 | 55e02ba4 | Matt West | title => 'Active States', |
| 83 | info => 'This graph shows us the active state or not, depending on if a DJ is connected', |
||
| 84 | }, |
||
| 85 | datasrc => [ |
||
| 86 | { name => 'active', draw => 'AREA', min => '0', max => '1', label => 'On or Off', type => 'GAUGE' },
|
||
| 87 | ], |
||
| 88 | }; |
||
| 89 | |||
| 90 | $graphsRef->{listeners} = {
|
||
| 91 | config => {
|
||
| 92 | args => '--base 1000 --lower-limit 0', |
||
| 93 | vlabel => 'Listener Count', |
||
| 94 | a8d024ac | dipohl | category => 'streaming', |
| 95 | 55e02ba4 | Matt West | title => 'Listeners', |
| 96 | info => 'This graph shows us the various counts for listener states we are tracking', |
||
| 97 | }, |
||
| 98 | datasrc => [ |
||
| 99 | { name => 'maxlisteners', draw => 'AREA', min => '0', label => 'Max Listeners', type => 'GAUGE' },
|
||
| 100 | { name => 'currlisteners', draw => 'STACK', min => '0', label => 'Current Listeners', type => 'GAUGE' },
|
||
| 101 | ], |
||
| 102 | }; |
||
| 103 | |||
| 104 | $graphsRef->{sid_active} = {
|
||
| 105 | config => {
|
||
| 106 | args => '--base 1000 --lower-limit 0', |
||
| 107 | vlabel => 'Is a DJ Actively Connected?', |
||
| 108 | title => 'Active State', |
||
| 109 | info => 'This graph shows us the active state or not, depending on if a DJ is connected', |
||
| 110 | }, |
||
| 111 | datasrc => [ |
||
| 112 | { name => 'active', draw => 'AREA', min => '0', max => '1', label => 'On or Off', type => 'GAUGE', xmlkey => 'STREAMSTATUS' },
|
||
| 113 | ], |
||
| 114 | }; |
||
| 115 | |||
| 116 | $graphsRef->{sid_listeners} = {
|
||
| 117 | config => {
|
||
| 118 | args => '--base 1000 --lower-limit 0', |
||
| 119 | vlabel => 'Listener Count', |
||
| 120 | title => 'Listeners', |
||
| 121 | info => 'This graph shows us the various counts for listener states we are tracking', |
||
| 122 | }, |
||
| 123 | datasrc => [ |
||
| 124 | { name => 'maxlisteners', draw => 'AREA', min => '0', label => 'Max Listeners', type => 'GAUGE', xmlkey => 'MAXLISTENERS' },
|
||
| 125 | { name => 'currlisteners', draw => 'STACK', min => '0', label => 'Current Listeners', type => 'GAUGE', xmlkey => 'CURRENTLISTENERS' },
|
||
| 126 | { name => 'peaklisteners', draw => 'LINE2', min => '0', label => 'Peak Listeners', type => 'GAUGE', xmlkey => 'PEAKLISTENERS' },
|
||
| 127 | { name => 'uniqlisteners', draw => 'LINE2', min => '0', label => 'Unique Listeners', type => 'GAUGE', xmlkey => 'UNIQUELISTENERS' },
|
||
| 128 | ], |
||
| 129 | }; |
||
| 130 | |||
| 131 | if (defined($ARGV[0]) && ($ARGV[0] eq 'config')) {
|
||
| 132 | config(); |
||
| 133 | exit; |
||
| 134 | } |
||
| 135 | |||
| 136 | if (defined($ARGV[0]) && (($ARGV[0] eq 'autoconf') || ($ARGV[0] eq 'suggest'))) {
|
||
| 137 | check_autoconf(); |
||
| 138 | exit; |
||
| 139 | } |
||
| 140 | |||
| 141 | # I guess we are collecting stats to return, execute main subroutine. |
||
| 142 | main(); |
||
| 143 | |||
| 144 | exit; |
||
| 145 | |||
| 146 | =head1 Subroutines |
||
| 147 | |||
| 148 | The following is a description of what each subroutine is for and does |
||
| 149 | |||
| 150 | =head2 main |
||
| 151 | |||
| 152 | This subroutine is our main routine should we not be calling up autoconf |
||
| 153 | or config. Ultimately this routine will print out the values for each graph |
||
| 154 | and graph data point we are tracking. |
||
| 155 | |||
| 156 | =cut |
||
| 157 | |||
| 158 | sub main {
|
||
| 159 | my ($returnBit,$adminRef) = fetch_admin_data(); |
||
| 160 | if ($returnBit == 0) {
|
||
| 161 | exit; |
||
| 162 | } |
||
| 163 | my $streamConfigRef = $adminRef->{STREAMCONFIGS}->{STREAMCONFIG};
|
||
| 164 | my $sidDataRef; |
||
| 165 | if ($adminRef->{STREAMCONFIGS}->{TOTALCONFIGS} == 1) {
|
||
| 166 | my $sid = $streamConfigRef->{id};
|
||
| 167 | my ($return,$tmpSidRef) = fetch_sid_data($sid); |
||
| 168 | if ($return == 0) {
|
||
| 169 | # Only one stream, and we didn't get a good response. |
||
| 170 | exit; |
||
| 171 | } |
||
| 172 | $sidDataRef->{$sid} = $tmpSidRef;
|
||
| 173 | } else {
|
||
| 174 | foreach my $sid (keys %{$streamConfigRef}) {
|
||
| 175 | my ($return,$tmpSidRef) = fetch_sid_data($sid); |
||
| 176 | if ($return == 0) {
|
||
| 177 | next; |
||
| 178 | } |
||
| 179 | $sidDataRef->{$sid} = $tmpSidRef;
|
||
| 180 | } |
||
| 181 | } |
||
| 182 | print_active_data($sidDataRef); |
||
| 183 | print_listener_data($adminRef->{STREAMCONFIGS}->{SERVERMAXLISTENERS}, $sidDataRef);
|
||
| 184 | return; |
||
| 185 | } |
||
| 186 | |||
| 187 | =head2 print_active_data |
||
| 188 | |||
| 189 | Thie subroutine prints out the active graph values for each stream and ultimately for |
||
| 190 | the entire shoutcast service. Should 1 Stream be active, but 5 streams available, |
||
| 191 | the global graph should show the state as active for the service, but clicking into |
||
| 192 | that graph, should give you a stream level view of which stream was in use during |
||
| 193 | what time periods. |
||
| 194 | |||
| 195 | =cut |
||
| 196 | |||
| 197 | sub print_active_data {
|
||
| 198 | my ($sidDataRef) = (@_); |
||
| 199 | my $globalActive = 0; |
||
| 200 | foreach my $sid (sort keys %{$sidDataRef}) {
|
||
| 201 | print "multigraph shoutcast2_active.active_sid_$sid\n"; |
||
| 202 | foreach my $dsrc (@{$graphsRef->{sid_active}->{datasrc}}) {
|
||
| 203 | 17f78427 | Lars Kruse | print "$dsrc->{name}.value $sidDataRef->{$sid}->{$dsrc->{xmlkey}}\n";
|
| 204 | 55e02ba4 | Matt West | if ($sidDataRef->{$sid}->{$dsrc->{xmlkey}} == 1) {
|
| 205 | $globalActive = 1; |
||
| 206 | } |
||
| 207 | } |
||
| 208 | } |
||
| 209 | print "multigraph shoutcast2_active\n"; |
||
| 210 | foreach my $dsrc (@{$graphsRef->{active}->{datasrc}}) {
|
||
| 211 | print "$dsrc->{name}.value $globalActive\n";
|
||
| 212 | } |
||
| 213 | return; |
||
| 214 | } |
||
| 215 | |||
| 216 | =head2 print_listener_data |
||
| 217 | |||
| 218 | This subroutine prints out the listener graph values for each stream and ultimately |
||
| 219 | 17f78427 | Lars Kruse | adds all of the current users together to show that against the maxserver count in |
| 220 | 55e02ba4 | Matt West | the global graph. Clicking on the global graph will reveal a bit more information |
| 221 | about the users on a stream by stream basis. |
||
| 222 | |||
| 223 | =cut |
||
| 224 | |||
| 225 | sub print_listener_data {
|
||
| 226 | my ($maxListeners,$sidDataRef) = (@_); |
||
| 227 | my $globalListeners = 0; |
||
| 228 | foreach my $sid (sort keys %{$sidDataRef}) {
|
||
| 229 | print "multigraph shoutcast2_listeners.listeners_sid_$sid\n"; |
||
| 230 | foreach my $dsrc (@{$graphsRef->{sid_listeners}->{datasrc}}) {
|
||
| 231 | 17f78427 | Lars Kruse | print "$dsrc->{name}.value $sidDataRef->{$sid}->{$dsrc->{xmlkey}}\n";
|
| 232 | 55e02ba4 | Matt West | if ($dsrc->{name} eq 'currlisteners') {
|
| 233 | $globalListeners += $sidDataRef->{$sid}->{$dsrc->{xmlkey}};
|
||
| 234 | } |
||
| 235 | } |
||
| 236 | } |
||
| 237 | print "multigraph shoutcast2_listeners\n"; |
||
| 238 | foreach my $dsrc (@{$graphsRef->{listeners}->{datasrc}}) {
|
||
| 239 | if ($dsrc->{name} eq 'maxlisteners') {
|
||
| 240 | print "$dsrc->{name}.value $maxListeners\n";
|
||
| 241 | } else {
|
||
| 242 | print "$dsrc->{name}.value $globalListeners\n";
|
||
| 243 | } |
||
| 244 | } |
||
| 245 | return; |
||
| 246 | } |
||
| 247 | |||
| 248 | =head2 config |
||
| 249 | |||
| 250 | The config subroutine can be seen as the main config routine, which |
||
| 251 | will call up to your shoutcast server to figure out how many streams |
||
| 252 | you have running, and then print out the appropriate multigraph info. |
||
| 253 | Ultimately this subroutine will call two more routines to print out |
||
| 254 | the graph args / configuration information. |
||
| 255 | |||
| 256 | =cut |
||
| 257 | |||
| 258 | sub config {
|
||
| 259 | my ($returnBit,$adminRef) = fetch_admin_data(); |
||
| 260 | if ($returnBit == 0) {
|
||
| 261 | # $adminRef returned a string, we'll just print it out. |
||
| 262 | print "no (error response: $adminRef)\n"; |
||
| 263 | exit; |
||
| 264 | } |
||
| 265 | my $streamConfigRef = $adminRef->{STREAMCONFIGS}->{STREAMCONFIG};
|
||
| 266 | my $sidDataRef; |
||
| 267 | if ($adminRef->{STREAMCONFIGS}->{TOTALCONFIGS} == 1) {
|
||
| 268 | my $sid = $streamConfigRef->{id};
|
||
| 269 | my ($return,$tmpSidRef) = fetch_sid_data($sid); |
||
| 270 | if ($return == 0) {
|
||
| 271 | # Only one stream, and we didn't get a good response. |
||
| 272 | exit; |
||
| 273 | } |
||
| 274 | $sidDataRef->{$sid} = $tmpSidRef;
|
||
| 275 | } else {
|
||
| 276 | foreach my $sid (keys %{$streamConfigRef}) {
|
||
| 277 | my ($return,$tmpSidRef) = fetch_sid_data($sid); |
||
| 278 | if ($return == 0) {
|
||
| 279 | next; |
||
| 280 | } |
||
| 281 | $sidDataRef->{$sid} = $tmpSidRef;
|
||
| 282 | } |
||
| 283 | } |
||
| 284 | print_active_config($sidDataRef); |
||
| 285 | print_listener_config($sidDataRef); |
||
| 286 | 17f78427 | Lars Kruse | return; |
| 287 | 55e02ba4 | Matt West | } |
| 288 | |||
| 289 | =head2 print_active_config |
||
| 290 | |||
| 291 | This subroutine prints out the graph information for our active graphs. |
||
| 292 | It prints the sub-multigraphs first based on stream id, and finally the |
||
| 293 | root active graph. Its not suggested that you mess with this routine |
||
| 294 | unless you fully understand what its doing and how munin graph_args work. |
||
| 295 | |||
| 296 | =cut |
||
| 297 | |||
| 298 | sub print_active_config {
|
||
| 299 | my ($sidDataRef) = (@_); |
||
| 300 | foreach my $sid (sort keys %{$sidDataRef}) {
|
||
| 301 | # Print the Config Info First |
||
| 302 | print "multigraph shoutcast2_active.active\_sid\_$sid\n"; |
||
| 303 | print "graph_title ".$graphsRef->{sid_active}->{config}->{title}." for StreamID: $sid\n";
|
||
| 304 | print "graph_args ".$graphsRef->{sid_active}->{config}->{args}."\n";
|
||
| 305 | print "graph_vlabel ".$graphsRef->{sid_active}->{config}->{vlabel}."\n";
|
||
| 306 | print "graph_category streamid_$sid\n"; |
||
| 307 | print "graph_info ".$graphsRef->{sid_active}->{config}->{info}."\n";
|
||
| 308 | # Print the Data Value Info |
||
| 309 | foreach my $dsrc (@{$graphsRef->{sid_active}->{datasrc}}) {
|
||
| 310 | while ( my ($key, $value) = each (%{$dsrc})) {
|
||
| 311 | next if ($key eq 'name'); |
||
| 312 | next if ($key eq 'xmlkey'); |
||
| 313 | print "$dsrc->{name}.$key $value\n";
|
||
| 314 | } |
||
| 315 | } |
||
| 316 | } |
||
| 317 | print "multigraph shoutcast2_active\n"; |
||
| 318 | print "graph_title ".$graphsRef->{active}->{config}->{title}."\n";
|
||
| 319 | print "graph_args ".$graphsRef->{active}->{config}->{args}."\n";
|
||
| 320 | print "graph_vlabel ".$graphsRef->{active}->{config}->{vlabel}."\n";
|
||
| 321 | print "graph_category ".$graphsRef->{active}->{config}->{category}."\n";
|
||
| 322 | print "graph_info ".$graphsRef->{active}->{config}->{info}."\n";
|
||
| 323 | # Print the Data Value Info |
||
| 324 | foreach my $dsrc (@{$graphsRef->{active}->{datasrc}}) {
|
||
| 325 | while ( my ($key, $value) = each (%{$dsrc})) {
|
||
| 326 | next if ($key eq 'name'); |
||
| 327 | print "$dsrc->{name}.$key $value\n";
|
||
| 328 | } |
||
| 329 | } |
||
| 330 | return; |
||
| 331 | } |
||
| 332 | |||
| 333 | =head2 print_listener_config |
||
| 334 | |||
| 335 | This subroutine prints out the graph information for our listeners graphs. |
||
| 336 | It prints the sub-multigraphs first based on stream id, and finally the |
||
| 337 | root listeners graph. Its not suggested that you mess with this routine |
||
| 338 | unless you fully understand what its doing and how munin graph_args work. |
||
| 339 | |||
| 340 | =cut |
||
| 341 | |||
| 342 | sub print_listener_config {
|
||
| 343 | my ($sidDataRef) = (@_); |
||
| 344 | foreach my $sid (sort keys %{$sidDataRef}) {
|
||
| 345 | # Print the Config Info First |
||
| 346 | print "multigraph shoutcast2_listeners.listeners\_sid\_$sid\n"; |
||
| 347 | print "graph_title ".$graphsRef->{sid_listeners}->{config}->{title}." for StreamID: $sid\n";
|
||
| 348 | print "graph_args ".$graphsRef->{sid_listeners}->{config}->{args}."\n";
|
||
| 349 | print "graph_vlabel ".$graphsRef->{sid_listeners}->{config}->{vlabel}."\n";
|
||
| 350 | print "graph_category streamid_$sid\n"; |
||
| 351 | print "graph_info ".$graphsRef->{sid_listeners}->{config}->{info}."\n";
|
||
| 352 | # Print the Data Value Info |
||
| 353 | foreach my $dsrc (@{$graphsRef->{sid_listeners}->{datasrc}}) {
|
||
| 354 | while ( my ($key, $value) = each (%{$dsrc})) {
|
||
| 355 | next if ($key eq 'name'); |
||
| 356 | next if ($key eq 'xmlkey'); |
||
| 357 | print "$dsrc->{name}.$key $value\n";
|
||
| 358 | } |
||
| 359 | } |
||
| 360 | } |
||
| 361 | print "multigraph shoutcast2_listeners\n"; |
||
| 362 | print "graph_title ".$graphsRef->{listeners}->{config}->{title}."\n";
|
||
| 363 | print "graph_args ".$graphsRef->{listeners}->{config}->{args}."\n";
|
||
| 364 | print "graph_vlabel ".$graphsRef->{listeners}->{config}->{vlabel}."\n";
|
||
| 365 | print "graph_category ".$graphsRef->{listeners}->{config}->{category}."\n";
|
||
| 366 | print "graph_info ".$graphsRef->{listeners}->{config}->{info}."\n";
|
||
| 367 | # Print the Data Value Info |
||
| 368 | foreach my $dsrc (@{$graphsRef->{listeners}->{datasrc}}) {
|
||
| 369 | while ( my ($key, $value) = each (%{$dsrc})) {
|
||
| 370 | next if ($key eq 'name'); |
||
| 371 | print "$dsrc->{name}.$key $value\n";
|
||
| 372 | } |
||
| 373 | } |
||
| 374 | return; |
||
| 375 | } |
||
| 376 | |||
| 377 | =head2 check_autoconf |
||
| 378 | |||
| 379 | This subroutine is called up when we intercept autoconf specified in ARGV[0] |
||
| 380 | If we are able to connect to the shoutcast service as admin and fetch the main |
||
| 381 | admin stats page, we will return ok, otherwise we will return no and the error |
||
| 382 | response we got from LWP::UserAgent. |
||
| 383 | |||
| 384 | =cut |
||
| 385 | |||
| 386 | sub check_autoconf {
|
||
| 387 | my ($returnBit,$adminRef) = fetch_admin_data(); |
||
| 388 | if ($returnBit == 0) {
|
||
| 389 | # $adminRef returned a string, we'll just print it out. |
||
| 390 | print "no (error response: $adminRef)\n"; |
||
| 391 | } else {
|
||
| 392 | print "yes\n"; |
||
| 393 | } |
||
| 394 | return; |
||
| 395 | } |
||
| 396 | |||
| 397 | =head2 fetch_sid_data |
||
| 398 | |||
| 399 | This subroutine is called up to fetch information on a per stream mentality. |
||
| 400 | If we are able to connect to the shoutcast service and get the stats we will |
||
| 401 | return 1 and a hashref of the de-coded xml information, otherwise we return 0 |
||
| 402 | so that we know that we have failed and can handle it gracefully. |
||
| 403 | |||
| 404 | =cut |
||
| 405 | |||
| 406 | sub fetch_sid_data {
|
||
| 407 | my ($sid) = (@_); |
||
| 408 | my $url = 'http://'.$host.':'.$port.'/stats?sid='.$sid; |
||
| 409 | my $response = $ua->get($url); |
||
| 410 | if ($response->is_success) {
|
||
| 411 | my $returnRef = XMLin($response->decoded_content); |
||
| 412 | 17f78427 | Lars Kruse | return (1, $returnRef); |
| 413 | 55e02ba4 | Matt West | } else {
|
| 414 | return (0, $response->status_line); |
||
| 415 | } |
||
| 416 | } |
||
| 417 | |||
| 418 | =head2 fetch_admin_data |
||
| 419 | |||
| 420 | This subroutine is called up to fetch information from the admin page to get stream ids. |
||
| 421 | This subroutine is also used to test that we can connect to the shoutcast service |
||
| 422 | and if not we can fail gracefully. If we are able to connect to the shoutcast service |
||
| 423 | and get the stats we will return 1 and a hashref of the de-coded xml information, |
||
| 424 | otherwise we return 0 so that we know that we have failed and can handle it gracefully. |
||
| 425 | |||
| 426 | =cut |
||
| 427 | |||
| 428 | sub fetch_admin_data {
|
||
| 429 | my $url = 'http://'.$host.':'.$port.'/admin.cgi?sid=1&mode=viewxml&page=6'; |
||
| 430 | my $response = $ua->get($url); |
||
| 431 | if ($response->is_success) {
|
||
| 432 | my $returnRef = XMLin($response->decoded_content); |
||
| 433 | if (($returnRef->{STREAMCONFIGS}->{TOTALCONFIGS} > 0) && (defined($returnRef->{STREAMCONFIGS}->{STREAMCONFIG}))) {
|
||
| 434 | 17f78427 | Lars Kruse | return (1, $returnRef); |
| 435 | 55e02ba4 | Matt West | } else {
|
| 436 | return (0, 'Unable to Detect any Stream Configurations'); |
||
| 437 | } |
||
| 438 | } else {
|
||
| 439 | return (0, $response->status_line); |
||
| 440 | } |
||
| 441 | } |
||
| 442 | |||
| 443 | a8d024ac | dipohl | # for Munin Plugin Gallery |
| 444 | # graph_category streaming |
