monitor
37 TopicsHTTP Monitor cURL Basic GET
Problem this snippet solves: External HTTP monitor script that requests a URI from the pool member to which it is applied, marking it up if the expected response is received. URI and response string are user-configurable. cURL by default uses HTTP/1.1 and, since no hostname is specified in the cURL command, inserts the IP address in the Host header. NOTE: Use external monitors only when a built-in monitor won't do the trick. This example is intended to demonstrate the basic use of cURL (which offers a large number of other useful options) in an external monitor. However, if you don't need those extra options, a very simple HTTP monitor such as this is much more efficiently configured using the built-in HTTP monitor template instead. How to use this snippet: 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. 2. Create a monitor profile of type "External" with the following values: External Program: . . the name of the script file created in step 1 Variables: Name.......Value URI . . . . .the URI to request from the server RECV . . . . the expected response Adjust the interval and timeout as appropriate for your application. Jan 3 00:00:00 local/bigip err logger: EAV exceeded runtime needed to kill 10.0.0.10:80 If the interval and timeout is smaller then the execution time of the script, the monitor marks the element down and logs a message in /var/log/ltm. This is a false negative. To fix this, please increase the interval and timeout accordingly. Code : #!/bin/sh # # (c) Copyright 1996-2007 F5 Networks, Inc. # # This software is confidential and may contain trade secrets that are the # property of F5 Networks, Inc. No part of the software may be disclosed # to other parties without the express written consent of F5 Networks, Inc. # It is against the law to copy the software. No part of the software may # be reproduced, transmitted, or distributed in any form or by any means, # electronic or mechanical, including photocopying, recording, or information # storage and retrieval systems, for any purpose without the express written # permission of F5 Networks, Inc. Our services are only available for legal # users of the program, for instance in the event that we extend our services # by offering the updating of files via the Internet. # # @(#) $Id: http_monitor_cURL+GET,v 1.0 2007/06/28 16:10:15 deb Exp $ # (based on sample_monitor,v 1.3 2005/02/04 18:47:17 saxon) # # these arguments supplied automatically for all external monitors: # $1 = IP (IPv6 notation. IPv4 addresses are passed in the form # ::ffff:w.x.y.z # where "w.x.y.z" is the IPv4 address) # $2 = port (decimal, host byte order) # # Additional command line arguments ($3 and higher) may be specified in the monitor template # This example does not expect any additional command line arguments # # Name/Value pairs may also be specified in the monitor template # This example expects the following Name/Vaule pairs: # URI = the URI to request from the server # RECV = the expected response (not case sensitive) # # remove IPv6/IPv4 compatibility prefix (LTM passes addresses in IPv6 format) IP=`echo ${1} | sed 's/::ffff://'` PORT=${2} PIDFILE="/var/run/`basename ${0}`.${IP}_${PORT}.pid" # kill of the last instance of this monitor if hung and log current pid if [ -f $PIDFILE ] then echo "EAV exceeded runtime needed to kill ${IP}:${PORT}" | logger -p local0.error kill -9 `cat $PIDFILE` > /dev/null 2>&1 fi echo "$$" > $PIDFILE # send request & check for expected response curl -fNs http://${IP}:${PORT}${URI} | grep -i "${RECV}" 2>&1 > /dev/null # mark node UP if expected response was received if [ $? -eq 0 ] then rm -f $PIDFILE echo "UP" else rm -f $PIDFILE fi exit4.7KViews0likes6CommentsHTTP Monitor cURL Basic POST
Problem this snippet solves: External HTTP monitor script that sends a POST request to the pool member to which it is applied, marking it up if the expected response is received. URI, POST data, and response string are user-configurable. cURL by default uses HTTP/1.1 and, since no hostname is specified in the cURL command, inserts the IP address in the Host header. NOTE: Use external monitors only when a built-in monitor won't do the trick. This monitor is intended as an example of using cURL (which offers a large number of other useful options) to perform a POST. More basic HTTP monitors are much more efficiently configured using the built-in HTTP monitor template instead. UPDATE: The script below had a logic error in it where by it was using the NODE and PORT variables to create a PID file before the variables were defined. This meant that if your monitor took long enough to run the PID running monitor was killed before it finished and a new process ran in its place. This gave the appearence of the monitor not functioning correctly. I have corrected this below. How to use this snippet: 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. Create a monitor profile of type "External" with the following values: External Program: . . the name of the script file created in step 1 Variables: Name.......Value URI . . . . .the URI to which the POST will be sent (URI only, no hostname) DATA . . . . the POST data to be sent to the server RECV . . . . the expected response Adjust the interval and timeout as appropriate for your application Jan 3 00:00:00 local/bigip err logger: EAV exceeded runtime needed to kill 10.0.0.10:80 If the interval and timeout is smaller then the execution time of the script, the monitor marks the element down and logs a message in /var/log/ltm. This is a false negative. To fix this, please increase the interval and timeout accordingly. Code : #!/bin/sh # # (c) Copyright 1996-2007 F5 Networks, Inc. # # This software is confidential and may contain trade secrets that are the # property of F5 Networks, Inc. No part of the software may be disclosed # to other parties without the express written consent of F5 Networks, Inc. # It is against the law to copy the software. No part of the software may # be reproduced, transmitted, or distributed in any form or by any means, # electronic or mechanical, including photocopying, recording, or information # storage and retrieval systems, for any purpose without the express written # permission of F5 Networks, Inc. Our services are only available for legal # users of the program, for instance in the event that we extend our services # by offering the updating of files via the Internet. # # @(#) $Id: http_monitor_cURL+POST,v 1.0 2007/06/28 16:36:11 deb Exp $ # (based on sample_monitor,v 1.3 2005/02/04 18:47:17 saxon) # # # these arguments supplied automatically for all external monitors: # $1 = IP (nnn.nnn.nnn.nnn notation) # $2 = port (decimal, host byte order) # # additional command line arguments ($3 and higher) may be specified in the monitor template # This example does not expect any additional command line arguments # # Name/Value pairs may also be specified in the monitor template # This example expects the following Name/Value pairs: # URI = the URI to which the POST will be sent # DATA = the POST data to send to the server # RECV = the expected response (not case sensitive) # # remove IPv6/IPv4 compatibility prefix (LTM passes addresses in IPv6 format) NODE=`echo ${1} | sed 's/::ffff://'` PORT=${2} PIDFILE="/var/run/`basename ${0}`.${NODE}_${PORT}.pid" # kill of the last instance of this monitor if hung and log current pid if [ -f $PIDFILE ] then echo "EAV exceeded runtime needed to kill ${IP}:${PORT}" | logger -p local0.error kill -9 `cat $PIDFILE` > /dev/null 2>&1 fi echo "$$" > $PIDFILE # send request & check for expected response curl -fNs http://${NODE}:${PORT}${URI} -d "${DATA}" | grep -i "${RECV}" 2>&1 > /dev/null # mark node UP if expected response was received if [ $? -eq 0 ] then # Remove the PID file rm -f $PIDFILE echo "UP" else # Remove the PID file rm -f $PIDFILE fi exit3.3KViews0likes2Commentssnmp-check external monitor
Problem this snippet solves: This external monitor script runs an snmpget to pool members and marks the members up or down based upon the result. Specifically created for this GTM/APM use case, but can be modified as needed. How to use this snippet: copy the contents of this file into /config/monitors/snmp-check, and then in the external monitor configuration, reference the monitor and provide the following variable key/value pairs: result=<result> community=<community> OID=<oid> Code : #!/bin/sh # # (c) Copyright 1996-2005 F5 Networks, Inc. # # This software is confidential and may contain trade secrets that are the # property of F5 Networks, Inc. No part of the software may be disclosed # to other parties without the express written consent of F5 Networks, Inc. # It is against the law to copy the software. No part of the software may # be reproduced, transmitted, or distributed in any form or by any means, # electronic or mechanical, including photocopying, recording, or information # storage and retrieval systems, for any purpose without the express written # permission of F5 Networks, Inc. Our services are only available for legal # users of the program, for instance in the event that we extend our services # by offering the updating of files via the Internet. # # @(#) $Id: sample_monitor,v 1.3 2005/02/04 18:47:17 saxon Exp $ # # # these arguments supplied automatically for all external pingers: # $1 = IP (nnn.nnn.nnn.nnn notation or hostname) # $2 = port (decimal, host byte order) # $3 and higher = additional arguments # # $MONITOR_NAME = name of the monitor # # In this sample script, $3 is the regular expression # #These lines are required to control the process ID of the monitor pidfile="/var/run/$MONITOR_NAME.$1..$2.pid" if [ -f $pidfile ] then kill -9 `cat $pidfile` > /dev/null 2>&1 fi echo "$$" > $pidfile #Since version9 uses the ipv6 native version of the IP address, parse that down #for usage node_ip=`echo $1 | sed 's/::ffff://'` #Log the variables for debugging #echo IP= $node_ip Port =$2 OID= $OID comm= $community result= $result >> /var/tmp/test #Create a variable called answer that contains the result of the snmpwalk. answer=`snmpget $node_ip -c $community -O v $OID | awk '{print $2}'` #Log the answer for debugging #echo Answer= $answer >> /var/tmp/test if [ $answer -lt $result ] then echo "up" fi rm -f $pidfile Tested this on version: No Version Found1.9KViews2likes5CommentsHTTPS SNI Monitor
Problem this snippet solves: Hi, You may or may not already have encountered a webserver that requires the SNI (Server Name Indication) extension in order to know which website it needs to serve you. It comes down to "if you don't tell me what you want, I'll give you a default website or even simply reset the connection". A typical IIS8.5 will do this, even with the 'Require SNI' checkbox unchecked. So you have your F5, with its HTTPS monitors. Those monitors do not yet support SNI, as they have no means of specifying the hostname you want to use for SNI. In comes a litle script, that will do exactly that. Here's a few quick steps to get you started: Download the script from this article (it's posted on pastebin: http://pastebin.com/hQWnkbMg, listed below and added as attachment). Import it under 'System' > 'File Management' > 'External Monitor Program File List'. Create a monitor of type 'External' and select the script from the picklist under 'External Program'. Add your specific variables (explanation below). Add the monitor to a pool and you are good to go. A quick explanation of the variables: METHOD (GET, POST, HEAD, OPTIONS, etc. - defaults to 'GET') URI ("the part after the hostname" - defaults to '/') HTTPSTATUS (the status code you want to receive from the server - defaults to '200') HOSTNAME (the hostname to be used for SNI and the Host Header - defaults to the IP of the node being targetted) TARGETIP and TARGETPORT (same functionality as the 'alias' fields in the original monitors - defaults to the IP of the node being targetted and port 443) DEBUG (set to 0 for nothing, set to 1 for logs in /var/log/ltm - defaults to '0') RECEIVESTRING (the string that needs to be present in the server response - default is empty, so not checked) HEADERX (replace the X by a number between 1 and 50, the value for this is a valid HTTP header line, i.e. "User-Agent: Mozilla" - no defaults) EXITSTATUS (set to 0 to make the monitor always mark te pool members as up; it's fairly useless, but hey... - defaults to 1) There is a small thing you need to know though: due to the nature of the openssl binary (more specifically the sclient), we are presented with a "stdin redirection problem". The bottom line is that your F5 cannot be "slow" and by slow I mean that if it requires more than 3 seconds to pipe a string into openssl sclient, the script will always fail. This limit is defined in the variable "monitorstdinsleeptime" and defaults to '3'. You can set it to something else by adding a variable named 'STDINSLEEPTIME' and giving it a value. From my experience, anything above 3 stalls the "F5 script executer", anything below 2 is too fast for openssl to read the request from stdin, effectively sending nothing and thus yielding 'down'. When you enable debugging (DEBUG=1), you can see what I mean for yourself: no more log entries for the script when STDINSLEEPTIME is set too high; always down when you set it too low. Kind regards, Thomas Schockaert Code : #!/bin/bash ##### Sanity checks ## Strictly IPv4 notation. # The openssl binary doesn't allow the use of IPv6 notation in the -connect parameter of the s_client subcommand. # F5 exports the NODE_IP variable, which always contains the IPv6-form, even when the IP address is IPv4. # Stripping the unwanted part solves this. monitor_targetip=$(echo "$1" | sed 's/::ffff://') ## ## Binary validation # Finding all the binaries is paramount for this script to run successfully. These binaries must be found under one or more directories in the PATH variable. # You can modify the PATH variable under which the monitor executes by explicitly defining it in the Variables section of the monitor definition. required_programs="openssl logger cat grep egrep awk tr seq sleep" missing_programs=0 missing_programs_output="" missing_programs_counter=0 for current_program in $required_programs ; do program_path=$(which $current_program) if [ $? -eq 0 ] ; then eval "$current_program=$program_path" else output="$missing_programs_output$current_program," let missing_programs_counter=$missing_programs_counter+1 fi done if [ $missing_programs_counter -gt 0 ] ; then echo -e "ERROR: An external monitor script failed to locate one or more of its required programs. The script cannot continue unless you fix this." >> /var/log/ltm echo -e "The program(s) that could not be found are: $missing_programs_output" >> /var/log/ltm echo -e "The location of these programs needs to be under one the following directories: '$PATH'" >> /var/log/ltm exit 1 fi ## ##### ##### Preparations before running the checks ## Setting the default settings # These are needed in case the creator of the monitor failed to specify all the required variables monitor_debug=0 monitor_method="GET" monitor_uri="/" monitor_httpstatus="200" monitor_hostname="$monitor_targetip" monitor_targetport="$NODE_PORT" monitor_receivestring="" monitor_header="" monitor_stdin_sleeptime="3" # this is required to make openssl s_client accept the input from stdin before it closes. monitor_exitstatus=1 log_monitor_debug_specified=0 log_monitor_method_specified=0 log_monitor_uri_specified=0 log_monitor_httpstatus_specified=0 log_monitor_hostname_specified=0 log_monitor_targetport_specified=0 log_monitor_receivestring_specified=0 log_monitor_header_specified=0 log_monitor_receivestring_match=0 log_monitor_httpstatus_match=0 ## ## Overriding the default settings if needed # This part loops through the possible variables and checks if they have been defined. # If one has been defined, it checks if it's not empty and adds it to the actual action-variable ($monitor_something). monitor_variable_items="method uri httpstatus hostname targetip targetport debug receivestring header stdin_sleeptime exitstatus" for current_monitor_variable_item in $monitor_variable_items ; do current_monitor_variable_name_for_usage="monitor_${current_monitor_variable_item}" current_monitor_variable_name_for_logging="log_${current_monitor_variable_name_for_usage}_specified" if [ "$current_monitor_variable_item" == "header" ] ; then tmp="" for i in `$seq 1 50` ; do current_monitor_variable_name_for_input="$(echo "$current_monitor_variable_item" | $tr 'a-z' 'A-Z')$i" eval "current_monitor_variable_value_for_input=\$$current_monitor_variable_name_for_input" if ! [ "$current_monitor_variable_value_for_input" == "" ] ; then if [ $i -eq 1 ] ; then tmp="${current_monitor_variable_value_for_input}" else tmp="${tmp}\r\n${current_monitor_variable_value_for_input}" fi fi unset current_monitor_variable_name_for_input done eval "$current_monitor_variable_name_for_usage=\"$tmp\"" eval "$current_monitor_variable_name_for_logging=1" else current_monitor_variable_name_for_input=$(echo "$current_monitor_variable_item" | tr 'a-z' 'A-Z') eval "current_monitor_variable_value_for_input=\$$current_monitor_variable_name_for_input" eval "current_monitor_variable_value_for_usage=\$$current_monitor_variable_name_for_usage" if ! [ "$current_monitor_variable_value_for_input" == "" ] ; then eval "$current_monitor_variable_name_for_usage=$current_monitor_variable_value_for_input" eval "$current_monitor_variable_name_for_logging=1" fi fi unset tmp current_monitor_variable_name_for_usage current_monitor_variable_name_for_logging current_monitor_variable_name_for_input done ### ##### ##### Running the checks ## Obtaining the HTTP content through openssl http_content=`(echo -e "$monitor_method $monitor_uri HTTP/1.1\r\nHost: $monitor_hostname\r\n${monitor_header}\r"; $sleep $monitor_stdin_sleeptime) | $openssl s_client -connect $monitor_targetip:$monitor_targetport -servername $monitor_hostname` ## ## Obtaining the HTTP Status code from the returned contents http_error_code=$(echo "$http_content" | $egrep "HTTP/1\.[0-1] " | $awk '{print $2}') ## ## Determining the 'up' or 'down' status if [ "$http_error_code" == "$monitor_httpstatus" ] ; then log_monitor_httpstatus_match=1 if ! [ "$monitor_receivestring" == "" ] ; then receive_string_check=$(echo "$http_content" | $grep "$monitor_receivestring") if ! [ "$receive_string_check" == "" ] ; then log_monitor_receivestring_match=1 monitor_exitstatus=0 fi else monitor_exitstatus=0 fi fi ## ##### ##### Supplying the debug logs if requested ## Dump each variable if [ "$monitor_debug" == "1" ] ; then this_run=$(date +%s) log_prefix="HTTPS_SNI [$this_run]:" echo "" | $logger -p local0.debug echo "$log_prefix Monitor Description:" | $logger -p local0.debug for current_monitor_variable_item in $monitor_variable_items ; do current_monitor_variable_name_for_usage="monitor_${current_monitor_variable_item}" current_monitor_variable_name_for_logging="log_${current_monitor_variable_name_for_usage}_specified" eval "current_monitor_variable_value_for_usage=\"\$$current_monitor_variable_name_for_usage\"" eval "current_monitor_variable_value_for_logging=\$$current_monitor_variable_name_for_logging" if [ "$current_monitor_variable_value_for_logging" == "1" ] ; then echo "$log_prefix - $current_monitor_variable_name_for_usage => $current_monitor_variable_value_for_usage" | $logger -p local0.debug else echo "$log_prefix - $current_monitor_variable_name_for_usage => default ($current_monitor_variable_value_for_usage)" | $logger -p local0.debug fi unset current_monitor_variable_name_for_usage current_monitor_variable_name_for_logging done echo "$log_prefix Monitor Status:" | $logger -p local0.debug if [ "$log_monitor_receivestring_match" == "0" ] ; then echo "$log_prefix - Receive String: no match" | $logger -p local0.debug else echo "$log_prefix - Receive String: match" | $logger -p local0.debug fi if [ "$log_monitor_httpstatus_match" == "0" ] ; then echo "$log_prefix - HTTP Status: no match" | $logger -p local0.debug else echo "$log_prefix - Receive String: match" | $logger -p local0.debug fi if [ "$monitor_exitstatus" == "0" ] ; then echo "$log_prefix - Final Result: UP" | $logger -p local0.debug else echo "$log_prefix - Final Result: DOWN" | $logger -p local0.debug fi echo "" | logger -p local0.debug echo "" | logger -p local0.debug fi ## ##### ##### Telling F5 the monitor status should be up ## Echoing 'up' to stdout if [ "$monitor_exitstatus" == "0" ] ; then echo "up" fi ## ## Exiting accordingly (0 or 1) exit $monitor_exitstatus ## #####1.5KViews1like6CommentsHTTP Monitor cURL GET With Host Specific Headers
Problem this snippet solves: External HTTP monitor script that requests a URI from the pool member to which it is applied, inserting the appropriate hostname in the Host header, and marking the pool member UP if the expected response is received. URI and response string are user-configurable. The Host header value will be inserted based on the IP-to-hostname mappings specified in the case construct in the monitor script. (If no match is found, the the IP address of the pool member is inserted in the Host header.) NOTE: Use external monitors only when a built-in monitor won't do the trick. This example is intended to demonstrate the use of cURL (which offers a large number of other useful options) to insert variable headers in an external monitor. However, if nothing in the request varies, and don't need those extra options, more basic HTTP monitors are much more efficiently configured using the built-in HTTP monitor template instead. UPDATE: The script below had a logic error in it where by it was using the NODE and PORT variables to create a PID file before the variables were defined. This meant that if your monitor took long enough to run the PID running monitor was killed before it finished and a new process ran in its place. This gave the appearence of the monitor not functioning correctly. I have corrected this below. How to use this snippet: 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. Edit the "case" construct in the code to include the IP addresses and Host header values for each pool member to which this monitor will be applied. Create a monitor profile of type "External" with the following values: External Program: . . the name of the script file created in step 1 Variables: Name.......Value URI . . . . .the URI to request from the server RECV . . . . the expected response Adjust the interval and timeout as appropriate for your application Jan 3 00:00:00 local/bigip err logger: EAV exceeded runtime needed to kill 10.0.0.10:80 If the interval and timeout is smaller then the execution time of the script, the monitor marks the element down and logs a message in /var/log/ltm. This is a false negative. To fix this, please increase the interval and timeout accordingly. Code : #!/bin/sh # # (c) Copyright 1996-2007 F5 Networks, Inc. # # This software is confidential and may contain trade secrets that are the # property of F5 Networks, Inc. No part of the software may be disclosed # to other parties without the express written consent of F5 Networks, Inc. # It is against the law to copy the software. No part of the software may # be reproduced, transmitted, or distributed in any form or by any means, # electronic or mechanical, including photocopying, recording, or information # storage and retrieval systems, for any purpose without the express written # permission of F5 Networks, Inc. Our services are only available for legal # users of the program, for instance in the event that we extend our services # by offering the updating of files via the Internet. # # @(#) $Id: http_monitor_cURL+GET+HostSpecificHeaders,v 1.0 2007/06/28 15:03:15 deb Exp $ # (based on sample_monitor,v 1.3 2005/02/04 18:47:17 saxon) # # # these arguments supplied automatically for all external monitors: # $1 = IP (nnn.nnn.nnn.nnn notation) # $2 = port (decimal, host byte order) # # additional command line arguments ($3 and higher) may be specified in the monitor template # This example does not expect any additional command line arguments # # Name/Value pairs may also be specified in the monitor template # This example expects the following Name/Vaule pairs: # URI = the URI to request from the server # RECV = the expected response (not case sensitive) # # remove IPv6/IPv4 compatibility prefix (LTM passes addresses in IPv6 format) NODE=`echo ${1} | sed 's/::ffff://'` PORT=${2} PIDFILE="/var/run/`basename ${0}`.${NODE}_${PORT}.pid" # kill of the last instance of this monitor if hung and log current pid if [ -f $PIDFILE ] then echo "EAV exceeded runtime needed to kill ${IP}:${PORT}" | logger -p local0.error kill -9 `cat $PIDFILE` > /dev/null 2>&1 fi echo "$$" > $PIDFILE # set the value for the Host header based on IP # (defaults to IP address if IP doesn't match) case "$NODE" in "192.168.1.1") HOST="host1.domain.com" ;; "192.168.1.2") HOST="host2.domain.com" ;; "192.168.1.3") HOST="host3.domain.com" ;; *) HOST="$IP" ;; esac # send request & check for expected response if [ -v DATA ]; then curl -fNs http://${NODE}:${PORT}${URI} -H "Host: ${HOST}" -d "${DATA}" | grep -i "${RECV}" 2>&1 > /dev/null else curl -fNs http://${NODE}:${PORT}${URI} -H "Host: ${HOST}" | grep -i "${RECV}" 2>&1 > /dev/null fi # mark node UP if expected response was received if [ $? -eq 0 ] then # Remove the PID file rm -f $PIDFILE # Echo anything to STDOUT to indicate success echo "UP" else # Remove the PID file and exit rm -f $PIDFILE exit fi1.5KViews0likes1CommentNTP Monitor
Problem this snippet solves: Version 9 (tested on v9.2.4) Based on F5's example monitor, with the NTP check added. We make sure that the node gives us an NTP response and that the node hasn't become stratum 16 (ie, it's lost it's source). We use the CPAN Net::NTP module to make things easy. Just use it like a normal external monitor, it only uses the default IP and port. Code : #!/usr/bin/perl -w ############################################################################### # # # F5 Networks and BIG/ip(c) Copyright # # # # No part of the software may be reproduced or transmitted in any form or by # # any means, electronic or mechanical, for any purpose, without express # # written permission of F5 Networks, Inc. It is against the law to copy the # # software. No part of this program may be reproduced or transmitted in any # # form or by any means, electronic or mechanical, including photocopying, # # recording, or information storage and retrieval systems, for any purpose # # other than the purchasers personal use, without the express written # # permission of F5 Networks, Inc. Copyright (c) 1996-2005 BIG/ip Software. All # # rights reserved. Our services are only available for legal users of the # # program for instance in case we extend our services by offering updating of # # files through Internet. # # # ############################################################################### # # # As for any external monitor program, the first two command # line arguments are the node ip address and the node port. # After that its whatever is supplied via the monitor template. # # Caveat: This does assume that you're using IPv4. # use lib "/shared/lib/perl5"; use strict; use Net::NTP; require 5.005; # Derive and untaint programname. my $programname = '/' . $0; $programname =~ m/^.*\/([^\/]+)$/; $programname = $1; if ($programname eq '') { die "Bad data in program name\n" } # Process ID and file where it's to be stored. The format # is significant. my $node = $ARGV[0]; $node =~ s/^::ffff://; my $port = $ARGV[1]; my $pidfile = "/var/run/$programname.$node..$port.pid"; my $pid = "$$"; # Maintenence. Clean up any existing EAV. if (-f $pidfile ) { open(PID, "<$pidfile"); my $pid = ; close(PID); if ( $pid ) { chomp $pid; $pid =~ m/^(\d+)$/; $pid = $1; if ( $pid ) { kill 9, $pid; } } unlink($pidfile); } # Create a new maintenence file. open(PID, ">$pidfile"); print PID $pid, "\n"; close(PID); ########################################3 ## if (try_ntp_test($node,$port)) { print "OK\n"; } unlink($pidfile); exit(0); ######## sub try_ntp_test { # we just make sure we get a response # and that the server isn't a stratum # 16 server (ie, it's lost it's time source). my ($node,$port) = @_; my %response; eval { %response = get_ntp_response($node,$port); }; if ($@) { return 0; } else { if ($response{'Stratum'} =~ /\d+/) { # if the stratum is a number if ($response{'Stratum'} == 16) { return 0; } else { # and it isn't 16 return 1; } } else { return 0; } } } Tested this on version: 11.01.4KViews0likes23CommentsSMTP scripted monitor
Problem this snippet solves: This scripted monitor performs a health check of an SMTP service How to use this snippet: Create a new file: /usr/bin/monitors/smtp.script Add the expect script below. Customize @localhost to a valid email account on the servers being monitored. As dsirrine suggested, you could use a local account where the mailbox has been redirected to /dev/null. Create a custom scripted monitor which calls the expect script: Local Traffic >> Monitors >> Create Type Scripted Name my_smtp_scripted_monitor File Name /usr/bin/monitors/smtp.script Add the monitor to them SMTP monitor pool and test with the service up, down and account enabled and disabled. If you enable debug on the scripted monitor, the output is written to: /var/log/SCRIPTED_....log Ex: SCRIPTED__Common_smtp_scripted_monitor.\:\:ffff\:10.1.0.100..25.log Code : expect 220 send "HELO localhost\r\n" expect "250" send "MAIL FROM: @localhost\r\n" expect "250" send "RCPT TO: @localhost\r\n" expect "250" send "DATA\r\n" expect "354" send "SUBJECT:F5 LTM SMTP Health Check\r\n" send "\r\n" send "This e-mail is generated by the SMTP health check by the F5 LTM.\r\n" send ".\r\n" expect "250" send "quit\r\n" expect "221"1.2KViews1like0CommentsTACACS+ External Monitor (Python)
Problem this snippet solves: This script is an external monitor for TACACS+ that simulates a TACACS+ client authenticating a test user, and marks the status of a pool member as up if the authentication is successful. If the connection is down/times out, or the authentication fails due to invalid account settings, the script marks the pool member status as down. This is heavily inspired by the Radius External Monitor (Python) by AlanTen. How to use this snippet: Prerequisite This script uses the TACACS+ Python client by Ansible (tested on version 2.6). Create the directory /config/eav/tacacs_plus on BIG-IP Copy all contents from tacacs_plus package into /config/eav/tacacs_plus. You may also need to download six.py from https://raw.githubusercontent.com/benjaminp/six/master/six.py and place it in /config/eav/tacacs_plus. You will need to have a test account provisioned on the TACACS+ server for the script to perform authentication. Installation On BIG-IP, import the code snippet below as an External Monitor Program File. Monitor Configuration Set up an External monitor with the imported file, and configure it with the following environment variables: KEY: TACACS+serversecret USER: Usernamefortestaccount PASSWORD: Passwordfortestaccount MOD_PATH: PathtolocationofPythonpackagetacacs_plus,default:/config/eav TIMEOUT: DurationtowaitforconnectivitytoTACACSservertobeestablished,default:3 Troubleshooting SSH to BIG-IP and run the script locally $ cd /config/filestore/files_d/Common_d/external_monitor_d/ # Get name of uploaded file, e.g.: $ ls -la ... -rwxr-xr-x. 1 tomcat tomcat 1883 2021-09-17 04:05 :Common:tacacs-monitor_39568_7 # Run the script with the corresponding variables $ KEY=<my_tacacs_key> USER=<testuser> PASSWORD=<supersecure> python <external program file, e.g.:Common:tacacs-monitor_39568_7> <TACACS+ server IP> <TACACS+ server port> Code : #!/usr/bin/env python # # Filename : tacacs_plus_mon.py # Author : Leon Seng # Version : 1.2 # Date : 2021/09/21 # Python ver: 2.6+ # F5 version: 12.1+ # # ========== Installation # Import this script via GUI: # System > File Management > External Monitor Program File List > Import... # Name it however you want. # Get, modify and copy the following modules: # ========== Required modules # -- six -- # https://pypi.org/project/six/ # Copy six.py into /config/eav # # -- tacacs_plus -- # https://pypi.org/project/tacacs_plus/ | https://github.com/ansible/tacacs_plus # Copy tacacs_plus directory into /config/eav # ========== Environment Variables # NODE_IP - Supplied by F5 monitor as first argument # NODE_PORT - Supplied by F5 monitor as second argument # KEY - TACACS+ server secret # USER - Username for test account # PASSWORD - Password for test account # MOD_PATH - Path to location of Python package tacacs_plus, default: /config/eav # TIMEOUT - Duration to wait for connectivity to TACACS server to be established, default: 3 import os import socket import sys if os.environ.get('MOD_PATH'): sys.path.append(os.environ.get('MOD_PATH')) else: sys.path.append('/config/eav') # https://github.com/ansible/tacacs_plus from tacacs_plus.client import TACACSClient node_ip = sys.argv[1] node_port = int(sys.argv[2]) key = os.environ.get("KEY") user = os.environ.get("USER") password = os.environ.get("PASSWORD") timeout = int(os.environ.get("TIMEOUT", 3)) # Determine if node IP is IPv4 or IPv6 family = None try: socket.inet_pton(socket.AF_INET, node_ip) family = socket.AF_INET except socket.error: # not a valid address try: socket.inet_pton(socket.AF_INET6, node_ip) family = socket.AF_INET6 except socket.error: sys.exit(1) # Authenticate against TACACS server client = TACACSClient(node_ip, node_port, key, timeout=timeout, family=family) try: auth = client.authenticate(user, password) if auth.valid: print "up" except socket.error: # EAV script marks node as DOWN when no output is present pass Tested this on version: 12.11.1KViews1like0CommentsHTTPS - Monitor SSL Handshake
Problem this snippet solves: External HTTPS monitor script that tests for successful SSL Handshake, then terminates. In BigIP versions v9.3.0 to v10.0.1, when no Receive String is specified in the HTTPS monitor (default setting), the monitor would mark a resource as "Available" upon successful completion of the SSL Handshake. This was an unintended behavior (ID 207411 / CR 120157), which was corrected in v10.1.0, and the HTTPS monitor now requires valid HTTP data in the response even if no Receive String is specified in the monitor; see SOL10904 for further details on this change. However, the original, unintended behavior may still be desirable in certain situations; for instance, HTTPS based VPN gateways which have no facility to provide HTTPS data themselves, and for which the monitor was only intended to verify SSL handshake. One way of working around this specific example is by re-engineering the monitoring to be more sophisticated and test the entire traffic path - i.e.: adding user auth info (User Name and Password) and Send String, so that the device in question may proxy the request successfully and send back a valid HTTP response. However, in circumstances where this is undesired, or the device in question is expected to not respond with HTTP data and only the handshake needs to be tested, the following external health monitor script will provide behavior similar to the pre-v10.1.0 behavior. NOTE: It is worth mentioning that the pre-v10.1.0 behavior basically caused the HTTPS Monitor to behave like a general SSL monitor when no Receive String was specified. IMPORTANT: Use external monitors only when a built-in monitor cannot suffice. This example is merely intended to demonstrate a means of allowing monitoring of successful SSL Handshake where the HTTP response is expected to be null. Otherwise, for an HTTPS resource which can be expected to have some valid HTTP response, but for which the response doesn't matter, you should simply use the built-in HTTPS monitor template with a "null" Receive string. How to use this snippet: 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. Create a monitor profile -- via WebGUI: Local Traffic module -> Monitors menu -> Create... button Type: "External" External Program: [filename from step 1 with full path: /usr/bin/monitors/[filename]] 3. Set Interval and Timeout as desired (typical best practice: Timeout = 3 x Interval + 1), along with any other settings needed Caveats This monitor does NOT support much of the features available in the stock HTTPS monitor. For instance: user auth (User Name & Password), Send String, and, of course, Receive String (though at that point the built-in monitor would be a better fit). It would be trivial to add in this kind of support, but was outside the originating goal for which this script was specifically crafted. Code : #!/bin/sh # # https_v10.0.1_monitor-v1.0 # # I, nor F5, provide any guarantee that this script will function as intended. # it should be thoroughly understood by the implementor (you), and tested # in a dev/qa environment prior to use in a production environment. # Script is provided AS-IS. # # expected arguments (standard): # $1 = IP (nnn.nnn.nnn.nnn notation or hostname) # $2 = port (decimal, host byte order) # # This script has had limited testing to indicate it's function is similar (if not precisely) to the v10.0.1 monitor: # # 1) requires port connection # 2) requires SSL negotiation # 3) does NOT require any data after SSL negotiation -- i.e.: no send string, and no receive string # # NOTE: Has not been written to support IPv6. At minimum would need to verify openssl's IPv6 support and connection syntax. # Check for comments: IPv6 support me=`basename $0` pidfile="/var/run/$me-$1:$2.pid" if [ -f "$pidfile" ]; then kill -9 `cat $pidfile` > /dev/null 2>&1 fi echo "$$" > $pidfile # IPv6 support 1/2: Here is where IPv6 support is stripped out # an if check for ::ffff: would do it: # * if matched, strip as shown # * else do nothing & pass through as a true IPv6 address node_ip=`echo $1 | sed 's/::ffff://'` pm_port="$2" # IPv6 support 2/2: Need to determine valid openssl syntax for connecting to an IPv6 address # (may work as-is, but the ':' port demarcation is a concern) echo 'QUIT'|openssl s_client -verify 1 -connect $node_ip:$pm_port >/dev/null 2>&1 # Mark node up if expected response was received if [ $? -eq 0 ] then # Remove the PID file rm -f $PIDFILE # Echo anything to STDOUT to indicate success echo "UP" else # Remove the PID file and exit rm -f $PIDFILE exit fi1KViews0likes2CommentsDNS Monitor Using Dig
Problem this snippet solves: This monitor uses dig to verify an expected response from load balanced DNS servers. It accepts 2 command-line arguments: the name to be looked up, and a string found in the expected response. NOTE: Use external monitors only when a built-in monitor won't do the trick. This example is intended to demonstrate the basic use of dig in an external monitor to test UDP-based name services. How to use this snippet: Implementation Create a new file containing the code below on the LTM filesystem. Recommended location is /config/eav. Permissions on the file must be 700 or better, giving root rwx access to the file. 2. Create a monitor profile of type "External" with the following values: External Program: . . the name of the script file created in step 1 Arguments: * " ", where string has x.x.x.x format for A lookup and ::::::: format for AAAA lookups 3. Adjust the interval and timeout as appropriate for your application Alternative Monitor Uses dig(1) More Efficiently The Alternative Monitor Script below allows you to adjust dig(1) query type, timeout, and retry count. The dig(1) utility normally waits just 5 seconds for a response to each DNS query, and retries failed queries twice. 5 seconds is too short for many queries across the Internet. Suppose you have DNS servers using different Internet gateways and wish to load-balance client DNS queries (received on a virtual server address) to your DNS servers. You can monitor DNS server and Internet gateway availability at the same time by querying each DNS server for a short-TTL RR from somewhere out on the Internet (short TTL so local caching won't mask gateway failure). In this scenario (and in other cases not discussed here) you may wish to allow a longer dig(1) timeout and limit the number of retries. Some more implementation notes appear as comments in the script. #!/bin/bash # # Script 'dns-monitor' brought to you by COSTCO WHOLESALE CORP. # Prepared 2010-04-23 by Mark Seecof for Costco Wholesale Corp. # LTM gives every external-monitor program the target # node's IP address and port as arguments $1 $2 # We must strip off IPV6 notation node="${1#::ffff:}" port="${2}" # (To save time in the normal case we omit to validate # arguments, though it would not be difficult to do so.) # # When you setup your external monitor, give as 'arguments': # $3, the DNS name to query; # $4, the type of its RR (e.g., 'A'); # $5, a grep(1) pattern which will match a successful # (dig +short) result but not a failed one; # $6, the query timeout in seconds (at least 2 seconds less # than the monitor's overall timeout). # # $7 (optional) if you wish an unanswered query retried (only # needed with a lossy network or overloaded DNS server), give # the retry count as $7--and reduce your $6 query timeout # proportionally (i.e., if you double the number of queries, # cut query timeouts in half, because you must make the sum # of query timeouts less than the monitor's overall timeout). # # EXAMPLE MONITOR SETUP: # Suppose a working DNS server will respond within 10 seconds # maximum to queries for the following RR: #example.comNSns1.example.com # # Copy this script to /config/monitors/dns-monitor (on both # LTM's in a failover pair) and make executable (chmod +x). # For this example, we will check on the DNS server 3 times # per minute. Create an 'external' monitor, set 'external # program' to "/config/monitors/dns-monitor". Set 'interval' # to 20 seconds, 'timeout' to 15 seconds, and set 'arguments' # to "example.com NS ns1.example.com 10". The padding in the # various timeouts allows for variations in system load # affecting real response times. # # ('.' is a regex operator so the pattern "ns1.example.com" # is ambiguous-- but unlikely to cause trouble. If you will # never need to test mildly-variable query results, you may # change "grep -q" to "grep -q -F" below...) # query="${3}" type="${4}" want="${5}" timeout="${6:-5}" retry="${7:-0}" if /usr/bin/dig "@${node}" -p "${port}" +short \ -t "${type}" -q "${query}" "+time=${timeout}" \ "+retry=${retry}" | /bin/grep -q "${want}" 2>/dev/null ; then echo "UP" ; exit 0 ; fi exit 1 #bottom of file Code : #!/bin/sh # (c) Copyright 1996-2007 F5 Networks, Inc. # # This software is confidential and may contain trade secrets that are the # property of F5 Networks, Inc. No part of the software may be disclosed # to other parties without the express written consent of F5 Networks, Inc. # It is against the law to copy the software. No part of the software may # be reproduced, transmitted, or distributed in any form or by any means, # electronic or mechanical, including photocopying, recording, or information # storage and retrieval systems, for any purpose without the express written # permission of F5 Networks, Inc. Our services are only available for legal # users of the program, for instance in the event that we extend our services # by offering the updating of files via the Internet. # # # these arguments supplied automatically for all external monitors: # $1 = IP (nnn.nnn.nnn.nnn notation or hostname) # $2 = port (decimal, host byte order) -- not used in this monitor, assumes default port 53 # $3 = name to be looked up # $4 = string in expected response node_ip=`echo $1 | sed 's/::ffff://'` pidfile="/var/run/`basename $0`.$node_ip..$2.pid" if [ -f $pidfile ] then kill -9 `cat $pidfile` > /dev/null 2>&1 fi echo "$$" > $pidfile dig @${node_ip} ${3} | egrep -v '^$|^;' | grep ${4} > /dev/null 2>&1 # For AAAA lookups, use this instead # dig @${node_ip} ${3} AAAA| egrep -v '^$|^;' | grep ${4} > /dev/null 2>&1 status=$? if [ $status -eq 0 ] then rm -f $pidfile echo "UP" else rm -f $pidfile fi901Views0likes2Comments