#!/usr/bin/perl -w # # Copyright (C) 2012 Dominik Schulz # # 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; version 2 dated June, # 1991. # # 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, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # Script to monitor DJabberd servers. # Mostly an adaptation of Guillaume Blairon's mogliefsd_activity script. # # Usage: # ln -s /usr/share/munin/plugins/djabberd_ \ # /etc/munin/plugins/djabberd_{connections,memory,latency,counters} # # Configuration variables: # # host (default: '127.0.0.1') # port (default: '5200') # # Parameters: # # config (required) # autoconf (optional - only used by munin-config) # #%# family=auto #%# capabilities=autoconf use strict; use warnings; use IO::Socket; my $djabberd_host = $ENV{host} || "127.0.0.1"; my $djabberd_port = $ENV{port} || 5200; my $mode = undef; my $mode_name = undef; # mapping mode to command from which we get the information my $mode_ref = { 'connections' => { 'cmd' => 'stats', 'title' => 'DJabberd Connections', 'vlabel' => 'connections', 'fields' => { 'connections' => { 'key' => 'connections', 'label' => 'Connections', 'description' => 'Number of current connections', 'draw' => 'LINE1', 'type' => 'GAUGE', }, 'users' => { 'key' => 'users', 'label' => 'Users', 'description' => 'Number of connected users', 'draw' => 'LINE1', 'type' => 'GAUGE', }, }, }, 'memory' => { 'cmd' => 'stats', 'title' => 'DJabberd Memory Statistics', 'vlabel' => 'Bytes', 'fields' => { 'mem_total' => { 'key' => 'mem_total', 'label' => '', 'description' => 'Total memory used by DJabberd', 'draw' => 'LINE1', 'type' => 'GAUGE', 'filter' => \&kb2bytes, }, 'mem_connections' => { 'key' => 'mem_connections', 'label' => '', 'description' => 'Memory used for handling connections', 'draw' => 'LINE1', 'type' => 'GAUGE', 'filter' => \&kb2bytes, }, 'mem_per_connection' => { 'key' => 'mem_per_connection', 'label' => '', 'description' => 'Memory used per connection', 'draw' => 'LINE1', 'type' => 'GAUGE', 'filter' => \&kb2bytes, }, } }, 'latency' => { 'cmd' => 'latency', 'title' => 'DJabberd Latency Statistics', 'vlabel' => 'reqs.', 'fields' => { 'dotzerozerozerofive' => { 'key' => '-0.0005', 'label' => 'lt. 0.0005', 'description' => 'Requests handled in lt. 0.0005s', 'draw' => 'AREA', 'type' => 'COUNTER', }, 'dotzerozeroone' => { 'key' => '-0.001', 'label' => 'lt. 0.001', 'description' => 'Requests handled int lt. 0.001s', 'draw' => 'STACK', 'type' => 'COUNTER', }, 'dotzerozerotwo' => { 'key' => '-0.002', 'label' => 'lt. 0.002', 'description' => 'Requests handled int lt. 0.002s', 'draw' => 'STACK', 'type' => 'COUNTER', }, 'dotzerozerofive' => { 'key' => '-0.005', 'label' => 'lt. 0.005', 'description' => 'Requests handled int lt. 0.005s', 'draw' => 'STACK', 'type' => 'COUNTER', }, 'dotzeroone' => { 'key' => '-0.01', 'label' => 'lt. 0.01', 'description' => 'Requests handled int lt. 0.01s', 'draw' => 'STACK', 'type' => 'COUNTER', }, 'dotzerotwo' => { 'key' => '-0.02', 'label' => 'lt. 0.02', 'description' => 'Requests handled int lt. 0.02s', 'draw' => 'STACK', 'type' => 'COUNTER', }, } }, 'counters' => { 'cmd' => 'counters', 'title' => 'DJabberd Counters', 'vlabel' => 'msgs.', 'fields' => { 'clientin_djabberd_iq' => { 'key' => 'ClientIn:DJabberd::IQ', 'type' => 'COUNTER', }, 'clientin_djabberd_message' => { 'key' => 'ClientIn:DJabberd::Message', 'type' => 'COUNTER', }, 'clientin_djabberd_presence' => { 'key' => 'ClientIn:DJabberd::Presence', 'type' => 'COUNTER', }, 'clientin_djabberd_stanza_sasl' => { 'key' => 'ClientIn:DJabberd::Stanza::SASL', 'type' => 'COUNTER', }, 'clientin_djabberd_stanza_starttls' => { 'key' => 'ClientIn:DJabberd::Stanza::StartTLS', 'type' => 'COUNTER', }, 'iniq_get_info_query' => { 'key' => 'InIQ:get-{http://jabber.org/protocol/disco#info}query', 'type' => 'COUNTER', }, 'iniq_get_items_query' => { 'key' => 'InIQ:get-{http://jabber.org/protocol/disco#items}query', 'type' => 'COUNTER', }, 'iniq_get_roster_query' => { 'key' => 'InIQ:get-{jabber:iq:roster}query', 'type' => 'COUNTER', }, 'iniq_get_bind' => { 'key' => 'InIQ:set-{urn:ietf:params:xml:ns:xmpp-bind}bind', 'type' => 'COUNTER', }, 'iniq_get_session' => { 'key' => 'InIQ:set-{urn:ietf:params:xml:ns:xmpp-session}session', 'type' => 'COUNTER', }, 'serverin_djabberd_stanza_dialback_result' => { 'key' => 'ServerIn:DJabberd::Stanza::DialbackResult', 'type' => 'COUNTER', }, 'serverin_djabberd_stanza_dialback_verify' => { 'key' => 'ServerIn:DJabberd::Stanza::DialbackVerify', 'type' => 'COUNTER', }, 'auth_success' => { 'key' => 'auth_success', 'type' => 'COUNTER', }, 'c2s_message' => { 'key' => 'c2s-Message', 'type' => 'COUNTER', }, 'c2s_presence' => { 'key' => 'c2s-Presence', 'type' => 'COUNTER', }, 'connect' => { 'key' => 'connect', 'type' => 'COUNTER', }, 'deliver_local' => { 'key' => 'deliver_local', 'type' => 'COUNTER', }, 'deliver_s2s' => { 'key' => 'deliver_s2s', 'type' => 'COUNTER', }, 'disconnect' => { 'key' => 'disconnect', 'type' => 'COUNTER', }, }, }, }; if ( $0 =~ m/djabberd_(.*)$/ && $mode_ref->{$1} ) { $mode_name = $1; $mode = $mode_ref->{$mode_name}; } else { print STDERR "ERROR: Unknown mode '$mode'. Exiting.\n"; exit -1; } if ( $ARGV[0] && $ARGV[0] eq 'suggest' ) { print join( "\n", keys %$mode_ref ); exit 0; } elsif ( $ARGV[0] && $ARGV[0] eq "autoconf" ) { my $result_ref = &query_djabberd( $djabberd_host, $djabberd_port, $mode ); if ($result_ref) { print "yes\n"; } else { print "no\n"; } exit 0; } elsif ( $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_title " . $mode->{'title'} . "\n"; print "graph_vlabel " . $mode->{'vlabel'} . "\n"; print "graph_args -l 0\n"; print "graph_category chat\n"; foreach my $field_name ( keys %{ $mode->{'fields'} } ) { my $label = $mode->{'fields'}->{$field_name}->{'label'} || $field_name; my $desc = $mode->{'fields'}->{$field_name}->{'description'} || $mode->{'fields'}->{$field_name}->{'key'}; my $draw = $mode->{'fields'}->{$field_name}->{'draw'} || 'LINE1'; my $type = $mode->{'fields'}->{$field_name}->{'type'} || 'COUNTER'; print $field_name. '.label ' . $label . "\n"; print $field_name. '.description ' . $desc . "\n"; print $field_name. '.draw ' . $draw . "\n"; print $field_name. '.type ' . $type . "\n"; } exit 0; } else { my $result_ref = &query_djabberd( $djabberd_host, $djabberd_port, $mode ); foreach my $field_name ( keys %{ $mode->{'fields'} } ) { my $key = $mode->{'fields'}->{$field_name}->{'key'}; if ( defined( $result_ref->{$key} ) ) { # check for definedness, may well be zero (false for perl) my $value = $result_ref->{$key}; # if there is a filter defined for this key apply it now if ( exists( $mode->{'fields'}->{$field_name}->{'filter'} ) && ref( $mode->{'fields'}->{$field_name}->{'filter'} ) eq 'CODE' ) { $value = &{ $mode->{'fields'}->{$field_name}->{'filter'} }($value); } print $field_name. ".value " . $value . "\n"; } } } sub query_djabberd { my ( $host, $port, $mode ) = @_; my $conn = IO::Socket::INET::->new( PeerAddr => $host, PeerPort => $port, Proto => 'tcp', Timeout => 5, ) or die($!); my $request = $mode->{'cmd'} . "\n"; $conn->syswrite( $request, length($request) ); my @lines = (); while ( my $line = $conn->getline() ) { if ( $line =~ /^\./ ) { last; } push( @lines, $line ); } close($conn); my $result_ref = {}; foreach my $line (@lines) { my ( $key, $value, $unit ) = split /\s+/, $line; if ( $key && $value ) { $result_ref->{$key} = $value; } } return $result_ref; } # transform kb => bytes sub kb2bytes { my $num = shift; if ( $num && $num =~ m/^\d+$/ ) { $num *= 1024; } return $num; }