#!/usr/bin/perl -w
#
# RAS-usage: determine the number of users (VPN clients) of the Juniper
# SA-2500 remote access cluster. All members of the cluster know how many
# users there are. The result is reported to Xymon using the clustername
# as hostname.
#
# - Added test cluster, which is reported for each multi-node cluster
# showing which node is active and which node(s) are passive. Based
# on ideas of Tom Schmitt.
#
use strict ;
use POSIX qw/ strftime / ; # Format time
#
# Installation constants.
#
my $XyDisp= $ENV{BBSERVERHOSTNAME} ; # Name of monitor server
my $XySend= $ENV{BB} ; # Monitor interface program
my $FmtDate= '%Y.%m.%d %H:%M:%S' ; # Default date format
$FmtDate= $ENV{BBDATEFORMAT} if defined $ENV{BBDATEFORMAT} ;
my $TestName= 'usage' ; # Xymon test name
#
# Define the Juniper RAS clusters.
# Each entry describes one cluster. The key of an entry is name of the cluster.
# The value of an entry is a reference to a list of names of the physical nodes
# in the cluster. It is assumed that the DNS name is equal to the Xymon name in
# all these cases.
#
my %Ras= (
<cluster> => [ '<Node-0>', '<Node-1>' ],
) ;
#
# Define the number of available licenses per cluster.
#
my %MaxUsers= ( # Number of available licenses
<cluster> => <Count>,
) ;
#
# Define the SNMP query command to determine the number of users. The OIDs
# of interest in subtree .1.3.6.1.4.1.12532. are:
# 2.0 - Number of Signed-In Web Users
# 3.0 - Number of Signed-In Mail Users
# 9.0 - the number of concurrent meeting users
# 12.0 - The Total number of Users Logged In for the IVE Node
# 13.0 - The Total number of Users Logged In for the Cluster
#
my $SnmpUser = 'snmpget -c <ComString> -v 2c %s .1.3.6.1.4.1.12532.2.0' ;
#
# Define the SNMP query to determine which physical server holds the Virtual
# IP address of the cluster.
#
my $SnmpCluster= 'snmpget -c <ComString> -v 2c %s .1.3.6.1.2.1.4.20.1.1.%s' ;
#
# Variable allocation.
#
my $Now= strftime( $FmtDate, localtime ) ; # Timestamp of tests
my $HostName ; # Xymon host name
my $Server ; # RAS node name
my $Cmd ; # Full blown snmpget command
my $Color ; # Test status
my $Users ; # Number of users
my %Cluster ; # Status of cluster nodes
my @Lines ; # Command output
my $Result ; # Test result
#
# Function UnpackIp transforms a binary formatted IPv4 address into a
# dotted decimal encoded string.
#
sub UnpackIp($) { return join('.',unpack('C4',$_[0])) } ;
#
# Main program.
# -------------
#
foreach $HostName ( keys %Ras ) {
$Color = 'green' ; # Preset variables
$Users = undef ;
$Result= '' ;
#
# -A- Determine for each cluster the number of web users.
#
# The number of users is retrieved from the nodes. It was found that an
# `snmpget` often fails if it is directed to the cluster service.
#
foreach $Server ( @{$Ras{$HostName}} ) {
$Cmd= sprintf( $SnmpUser, $Server ) ;
@Lines= `$Cmd` ;
next unless defined $Lines[0] ;
next unless $Lines[0]=~ m/ = Gauge32: (\d+)\s*$/ ;
$Users= $1 ; # Concurrent web users
last ;
} # of foreach
#
# Determine the test status and build the tail of the message.
#
unless ( defined $Users ) {
$Color= 'red' ;
$Result= "&$Color Cluster members are not accessible.\"\n" ;
} else {
if ( $Users > 0.9 * $MaxUsers{$HostName} ) {
$Color = 'yellow' ;
$Result= "&$Color High number of users: $Users\n" ;
} # of if
$Result.= "<!--\nusage = $Users\n-->" ;
} # of else
#
# Build the header of the message, and send it to Xymon.
#
$Result= "\"status $HostName.$TestName $Color $Now\n" .
"Number of users of Remote Access Service\n\n" .
$Result . "\"\n" ;
`$XySend $XyDisp $Result` ; # Inform Xymon
#
# -B- Determine the cluster status of each multi-node cluster.
# Test 'cluster' shows which node is active, and which are passive.
#
next unless @{$Ras{$HostName}} > 1 ;
$Color = 'green' ;
$Result = '' ;
%Cluster= () ;
my $VirtualIp= UnpackIp( gethostbyname($HostName) ) ;
foreach $Server ( @{$Ras{$HostName}} ) {
$Cmd= sprintf( $SnmpCluster, $Server, $VirtualIp ) ;
@Lines= `$Cmd` ;
if ( defined $Lines[0] ) {
if ( $Lines[0]=~ m/$VirtualIp$/ ) {
$Cluster{$Server}= [ 'green', "$Server is the active node in cluster $HostName" ] ;
$Result.= "&green $Server active node\n" ;
} elsif ( $Lines[0]=~ m/No Such Instance currently exists at this OID$/ ) {
$Cluster{$Server}= [ 'clear', "$Server is a passive node in cluster $HostName" ] ;
$Result.= "&green $Server passive node\n" ;
} else {
$Color = 'yellow' unless $Color eq 'red' ;
$Cluster{$Server}= [ 'yellow', "$Server has sent an unrecognised response:\n" .
" $Lines[0]" ] ;
$Result.= "&yellow $Server unrecognised response\n" ;
} # of else
} else {
$Color = 'red' ;
$Cluster{$Server}= [ $Color, "$Server has sent no response" ] ;
$Result.= "&$Color $Server no response\n" ;
} # of else
} # of foreach
#
# Report the status of all nodes together for the cluster service.
#
$Result= "\"status $HostName.cluster $Color $Now\n" .
"Node status in this cluster\n\n" .
$Result . "\"\n" ;
`$XySend $XyDisp $Result` ; # Inform Xymon
#
# Report the individual node status to each node individually.
#
foreach $Server ( keys %Cluster ) {
$Color = $Cluster{$Server}[0] ;
$Result= "\"status $Server.cluster $Color $Now\n" .
$Cluster{$Server}[1] . "\"\n" ;
`$XySend $XyDisp $Result` ; # Inform Xymon
} # of foreach
} # of foreach