SNMP Dynamic Ratio Monitor

Problem this snippet solves:

You may want to dynamically load balance to your servers based on things like CPU and memory utilization, but perhaps the built-in SNMP monitor is not flexible enough for you. This Perl EAV can use as many OIDs as you need and perform mathematical calculations on the results to affect the ratio of the various nodes in the pool. You need to set the load balancing method of the pool to "Ratio". You then need to define this EAV (with the name of the pool as the Argument) and edit the configuration at the top of the script. Finally, apply this monitor (as well as your regular health checking monitor) to the pool.

Some example configurations (these are just examples and have not been tested and may not be appropriate for your servers):

# Utilization of a 2-CPU machine (Windows-specific?)
# (utilization of CPU0, divided by 100 to get value between 0-1, utilization of CPU1, then these two
#  results are automatically averaged)
$oids{'cpu'} = [ '.1.3.6.1.4.1.2021.11.11.0 / 100', '.1.3.6.1.4.1.2021.11.11.1 / 100' ];
# Memory utilization (Linux-specific?)
# (RAM used divided by total RAM on the system)
$oids{'memory'} = [ '.1.3.6.1.4.1.2021.4.6.0 / .1.3.6.1.4.1.2021.4.5.0' ];

Create a new file containing the code below in /usr/bin/monitors on the LTM filesystem. Permissions on the file must be 700 or better, giving root rwx access to the file. See comments within the code for documentation.

Code :

#!/usr/bin/perl -w 
use strict;

# This monitor does not actually mark nodes as down -- it simply adjusts
# their ratio within the pool based on system utilization.  Currently you
# can only pass one argument in via the custom monitor declaration -- that
# is the name of the pool.  If it is passed in it overrides the name of the
# pool defined in the configuration section below.

# Note that I do not recommend running this monitor more frequently than
# once every 30 seconds.

# The ratio is calculated as follows:
#    (N)^(c1((t1 - u1)/t1)) + (N)^(c2((t2 - u2)/t2)) ...
# Where:
#    N = Number of nodes in the pool
#    c1 = Coefficient for item #1
#    t1 = Threshold for item #1 in %
#    u1 = Utilization for item #1 in %
#    c2 = Coefficient for item #2
#    t2 = Threshold for item #2 in %
#    u2 = Utilization for item #2 in %
#    ...

#############################################################################
# Configuration Section
# This is the pool that this monitor is applied to.  You can specify it here
# or pass it in as the parameter for the monitor.
my $pool = "";

# If you are using BIG-IP 9.4.0 or higher and the pool being monitored is 
# not part of the Common partition then specify the partition here, otherwise
# leave this empty.
my $partition = "DR_DC";

# SNMP Community to use
my $snmp_community = "public";

# Specify here what items to consider when determining the ratio for the node
my @ratio_items = ( 'cpu' );
#my @ratio_items = ( 'cpu', 'memory', 'disk' );

# Specify the Coefficient for each item here
my %coefficient = (
   'memory' => 4,
   'cpu' => 2,
   'disk' => 1,
);

# Specify the Threshold for each item here (between 0.00 and 1.00)
my %threshold = (
   'memory' => 0.9,
   'cpu' => 0.9,
   'disk' => 0.9,
);

# Define one or more OIDs or calculations for each item to monitor.  If 
# multiple items are listed then the results are averaged.  Each item may be
# a single OID but may also contain multiple OIDs and/or constants separated
# by basic arithmetic operators (Such as +, -, /, and *).  OIDs must be numeric
# and must begin with a period.
# Each item should result in a percentage (between 0.00 and 1.00)
my %oids;
$oids{'cpu'} = [ '.1.3.6.1.4.1.2021.11.11.0 / 100' ];

#############################################################################

# Read arguments
my $node_ip = $ARGV[0];
$node_ip =~ s/::ffff://;
my $port = $ARGV[1];
$pool = $ARGV[2] if defined($ARGV[2]);
my $ratio = 1;

# Check for and create PID file
my $monname = $ENV{MON_TMPL_NAME} || $0;
$monname = $0 unless $monname;
$monname =~ s/^.*\///;
my $pidfile = "/var/run/$monname.$node_ip.pid";
if (-r $pidfile) {
   open(PIDFILE, $pidfile);
   my $oldpid = ;
   close PIDFILE;
   chomp $oldpid;
   system("kill -9 $oldpid");
}
open(PIDFILE, ">$pidfile");
print PIDFILE "$$\n";
close PIDFILE;

# Determine number of active pool members
my @pool_data = `b pool $pool show`;
chomp @pool_data;
my $active_members = $pool_data[0];
$active_members =~ s/^.*MIN\/CUR ACTIVE MEMBERS \d+\/(\d+)/$1/;
print STDERR "Pool $pool has $active_members active members...\n";

my $count = 0;
my $total = 0;
foreach my $item (@ratio_items) {
   $count = 0;
   $total = 0;
   foreach my $oid (@{$oids{$item}}) {
      while ($oid =~ /(\.\d+\b[\d.]+)\b/) {
         # Lookup OID
         my $lookup = $1;
         my $snmp_value = `snmpget -v2c -c $snmp_community $node_ip $lookup`;
         chomp $snmp_value;
         $snmp_value =~ s/^.* = \w+: //;
         $oid =~ s/\.\d+\b[\d.]+\b/$snmp_value/;
         print STDERR "$node_ip($item) OID $lookup = $snmp_value\n";
      }
      my $result = eval $oid;
      print STDERR "$node_ip($item) evaluated formula: $oid = $result\n";
      $count++;
      $total += $result;
   }
   my $result = $total / $count;
   print STDERR "$node_ip($item) average is $result\n";
   $ratio += $active_members ** ($coefficient{$item} * (($threshold{$item} - $result) / $threshold{$item}));
}

# Set node's ratio
if ($partition) {
   system("b shell write partition $partition");
}
system("b pool $pool member $node_ip:$port ratio $ratio");
print STDERR "Setting $node_ip:$port in pool $pool to ratio $ratio\n";

# print results
unlink $pidfile;
print "UP\n";
Published Mar 12, 2015
Version 1.0

Was this article helpful?

No CommentsBe the first to comment