monitor
37 Topicssnmp-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 Found2KViews2likes5CommentsVIPRION external monitor
Problem this snippet solves: This VIPRION specific external monitor script is written in bash and utilizes TMSH to extend the built-in monitoring functionality of BIG-IP version 10.2.3. This write-up assumes the reader has working knowledge writing BIG-IP LTM external monitors. The following link is a great starting point LTM External Monitors: The Basics | DevCentral Logical network diagram: NOTE: The monitor is written to meet very specific environmental requirements. Therefore, your implementation may vary greatly. This post is inteded to show you some requirements for writing external monitors on the VIPRION platform while offering some creative ways to extend the functionality of external monitors using TMSH. The VIPRION acts as a hop in the default path of traffic destined for the Internet. Specific application flows are vectored to optimization servers and all other traffic is passed to the next hop router (Router C) toward the Internet. Router A and Router C are BGP neighbors through the VIPRION. Router B is a BGP neighbor with the VIPRION via ZebOS. A virtual address has route health injection enabled. The script monitors a user defined (agrument to the script) pool and transitions into the failed state when the available pool member count drops below a threshold value (argument to the script). In the failed state the following actions are performed once, effectively stopping client traffic flow through the VIPRION. Two virtual servers (arguments to the script) are disable to stop traffic through VIPRION. A virtual address (argument to the script) is disabled to disable route health injection of the address. All non Self-IP BGP connections are found in the connection table and deleted. NOTE: Manual intervention is required to enable virtual servers and virtual address when the monitor transitions from failed state to successful state before normal traffic flows will proceed. How to use this snippet: The monitor definition: monitor eavbgpv3 { defaults from external interval 20 timeout 61 args "poolhttp 32 vsforward1 vsforward2 10.10.10.1"v DEBUG "0"v run "rhi_v3.bsh" } This external monitor is configured to check for available members in the pool "poolhttp". When the available members falls below 32 the monitor transistions into the failed state and disables the virtual servers "vsforward1" and "vs_forward2" and disables the virtual address "10.10.10.1". When the available pool members increases above 32 neither the virtuals servers nor the virtual address is enabled. This will require manual intervention. The external monitor is assigned to a phantom pool with a single member "1.1.1.1:4353". No traffic is sent to the pool member. This pool and pool member are in place so the operator can see the current status of the external monitor. The Pool definition: pool bgpmonitor { monitor all eavbgp_v3 members 1.1.1.1:f5-iquery {} } You can download the script here: rhi_v3.bsh CODE: #!/bin/bash # (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. # # author: Paul DeHerrera pauld@f5.com # # 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 # # these arguments must be supplied in the monitor configuration: # $3 = name of pool to monitor # $4 = threshold value of the pool. If the available pool member count drops below this value the monitor will respond in 'failed' state # $5 = first Virtual server to disable # $6 = second Virtual server to disable # $7 = first Virtual address to disable # $8 = second Virtual address to disable ### Check for the 'DEBUG' variable, set it here if not present. # is the DEBUG variable passed as a variable? if [ -z "$DEBUG" ] then # If the monitor config didn't specify debug as a variable then enable/disable it here DEBUG=0 fi ### If Debug is on, output the script start time to /var/log/ltm # capture and log (when debug is on) a timestamp when this eav starts export ST=`date +%Y%m%d-%H:%M:%S` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): started at $ST" | logger -p local0.debug; fi ### Do not execute this script within the first 300 seconds after BIG-IP boot. This is a customer specific requirement # this section is used to introduce a delay of 300 seconds after system boot before executing this eav for the first time BOOT_DATE=`who -b | grep -i 'system boot' | awk {'print $3 " " $4 " " $5'}` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): boot_date: ($BOOT_DATE)" | logger -p local0.debug; fi EPOCH_DATE=`date -d "$BOOT_DATE" +%s` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): epoch_date: ($EPOCH_DATE)" | logger -p local0.debug; fi EPOCH_DATE=$((${EPOCH_DATE}+300)) if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): epoch_date +300: ($EPOCH_DATE)" | logger -p local0.debug; fi CUR_DATE=`date +%s` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): current_date: ($CUR_DATE)" | logger -p local0.debug; fi if [ $CUR_DATE -ge $EPOCH_DATE ] then ### Assign a value to variables. The VIPRION requires some commands to be executed on the Primary slot as you will see later in this script # export some variables if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): exporting variables..." | logger -p local0.debug; fi export REMOTEUSER="root" export HOME="/root" export IP=`echo $1 | sed 's/::ffff://'` export PORT=$2 export POOL=$3 export MEMBER_THRESHOLD=$4 export VIRTUAL_SERVER1=$5 export VIRTUAL_SERVER2=$6 export VIRTUAL_ADDRESS1=$7 export VIRTUAL_ADDRESS2=$8 export PIDFILE="/var/run/`basename $0`.$IP.$PORT.pid" export TRACKING_FILENAME=/var/tmp/rhi_bsh_monitor_status export PRIMARY_SLOT=`tmsh list sys db cluster.primary.slot | grep -i 'value' | sed -e 's/\"//g' | awk {'print $NF'}` ### Output the Primary slot to /var/log/ltm if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): the primary blade is in slot number: ($PRIMARY_SLOT)..." | logger -p local0.debug; fi ### This section is for debugging only. Check to see if this script is executing on the Primary blade and output to /var/log/ltm if [ $DEBUG -eq 1 ]; then export PRIMARY_BLADE=`tmsh list sys db cluster.primary | grep -i "value" | sed -e 's/\"//g' | awk {'print $NF'}`; fi if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): is this monitor executing on the primary blade: ($PRIMARY_BLADE)" | logger -p local0.debug; fi ### Standard EAV check to see if an instance of this script is already running for the memeber. If so, kill the previous instance and output to /var/log/ltm # is there already an instance of this EAV running for this member? if [ -f $PIDFILE ] then if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): pid file is present, killing process..." | logger -p local0.debug; fi kill -9 `cat $PIDFILE` > /dev/null 2>&1 echo "EAV `basename $0` ($$): exceeded monitor interval, needed to kill ${IP}:${PORT} with PID `cat $PIDFILE`" | logger -p local0.error fi ### Create a new pid file to track this instance of the monitor for the current member # create a pidfile if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): creating new pid file..." | logger -p local0.debug; fi echo "$$" > $PIDFILE ### Export variables for available pool members and total pool members # export more variables (these require tmsh) export AVAILABLE=`tmsh show /ltm pool $POOL members all-properties | grep -i "Availability" | awk {'print $NF'} | grep -ic "available"` export TOTAL_POOL_MEMBERS=`tmsh show /ltm pool $POOL members all-properties | grep -c "Pool Member"` let "AVAILABLE-=1" ### If Debug is on, output some variables to /var/log/ltm - helps with troubleshooting if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): Pool ($POOL) has ($AVAILABLE) available of ($TOTAL_POOL_MEMBERS) total members." | logger -p local0.debug; fi if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): Pool ($POOL) threshold = ($MEMBER_THRESHOLD) members. Virtual server1 ($VIRTUAL_SERVER1) and Virtual server2 ($VIRTUAL_SERVER2)" | logger -p local0.debug; fi if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): Member Threshold ($MEMBER_THRESHOLD)" | logger -p local0.debug; fi ### If the available members is less than the threshold then we are in a 'failed' state. # main monitor logic if [ "$AVAILABLE" -lt "$MEMBER_THRESHOLD" ] then ### If Debug is on, output status to /var/log/ltm ### notify log - below threshold and disabling virtual server1 if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): AVAILABLE < MEMBER_THRESHOLD, disabling the virtual server..." | logger -p local0.debug; fi if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disabling Virtual Server 1 ($VIRTUAL_SERVER1)" | logger -p local0.debug; fi ### Disable the first virtual server, which may exist in an administrative partition. For version 10.2.3 (possibly others) the script is required to change the 'update-partition' before disabling the virtual server. To accomplish this we first determine the administrative partition name where the virtual is configured then we build a list construct to execute both commands consecutively. ### disable virtual server 1 ### obtain the administrative partition for the virtual. if no administrative partition is found, assume common export VS1_PART=`tmsh list ltm virtual $VIRTUAL_SERVER1 | grep 'partition' | awk {'print $NF'}` if [ -z ${VS1_PART} ]; then ### no administrative partition was found so execute a list construct to change the update-partition to Common and disable the virtual server consecutively export DISABLE1=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "tmsh modify cli admin-partitions update-partition Common && tmsh modify /ltm virtual $VIRTUAL_SERVER1 disabled"` ### If Debug is on, output the command to /var/log/ltm if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disable cmd1: ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT 'tmsh modify cli admin-partitions update-partition Common && tmsh modify /ltm virtual $VIRTUAL_SERVER1 disabled'" | logger -p local0.debug; fi else ### the administrative partition was found so execute a list construct to change the update-partition and disable the virtual server consecutively. The command is sent to the primary slot via SSH export DISABLE1=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "tmsh modify cli admin-partitions update-partition $VS1_PART && tmsh modify /ltm virtual $VIRTUAL_SERVER1 disabled"` ### If Debug is on, output the command to /var/log/ltm if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disable cmd1: ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT 'tmsh modify cli admin-partitions update-partition $VS1_PART && tmsh modify /ltm virtual $VIRTUAL_SERVER1 disabled'" | logger -p local0.debug; fi fi ### If Debug is on, output status to /var/log/ltm if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disabling Virtual Server 2 ($VIRTUAL_SERVER2)" | logger -p local0.debug; fi ### Disable the second virtual server. This section is the same as above, so I will skip the detailed comments here. ### disable virtual server 2 export VS2_PART=`tmsh list ltm virtual $VIRTUAL_SERVER2 | grep 'partition' | awk {'print $NF'}` if [ -z ${VS2_PART} ]; then export DISABLE2=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "tmsh modify cli admin-partitions update-partition Common && tmsh modify /ltm virtual $VIRTUAL_SERVER2 disabled"` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disable cmd2: ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT 'tmsh modify cli admin-partitions update-partition Common && tmsh modify /ltm virtual $VIRTUAL_SERVER2 disabled'" | logger -p local0.debug; fi else export DISABLE2=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "tmsh modify cli admin-partitions update-partition $VS2_PART && tmsh modify /ltm virtual $VIRTUAL_SERVER2 disabled"` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disable cmd2: ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT 'tmsh modify cli admin-partitions update-partition $VS2_PART && tmsh modify ltm virtual $VIRTUAL_SERVER2 disabled'" | logger -p local0.debug; fi fi ### notify log - disconnecting all BGP connection if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): Pool ($POOL) disconnecting all BGP connections..." | logger -p local0.debug; fi ### acquire a list of self IPs SELF_IPS=(`tmsh list net self | grep 'net self' | sed -e 's/\//\ /g' | awk {'print $3'}`) ### start to build our TMSH command excluding self IPs BGP_CONNS="tmsh show sys conn cs-server-port 179 | sed -e 's/\:/\ /g' | egrep -v '" COUNT=1 if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 1 - ${BGP_CONNS}" | logger -p local0.debug; fi ### loop through the self IPs for ip in "${SELF_IPS[@]}" do if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 2 - ${ip}" | logger -p local0.debug; fi ### continue to build our TMSH command - append self IPs to ignore if [ ${COUNT} -gt 1 ] then BGP_CONNS=${BGP_CONNS}"|${ip}" else BGP_CONNS=${BGP_CONNS}"${ip}" fi (( COUNT++ )) done ### if debug is on log a message with the TMSH command up until this point if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 3 - ${BGP_CONNS}" | logger -p local0.debug; fi ### finish the TMSH command to show BGP connections not including self IPs BGP_CONNS=${BGP_CONNS}"' | egrep -v 'Sys|Total' | awk {'print \$1'}" if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 4 - ${BGP_CONNS}" | logger -p local0.debug; fi ### gather all BGP connection not including those to self IPs DISCONNS=(`eval $BGP_CONNS`) DISCMD='' NEWCOUNT=1 if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 5 - ${DISCONNS}" | logger -p local0.debug; fi ### loop through the resulting BGP connections and build another TMSH command to delete these connections from the connection table for newip in "${DISCONNS[@]}" do if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 6" | logger -p local0.debug; fi if [ ${NEWCOUNT} -gt 1 ] then DISCMD=${DISCMD}" && tmsh delete sys connection cs-client-addr ${newip} cs-server-port 179" else DISCMD=${DISCMD}"tmsh delete sys connection cs-client-addr ${newip} cs-server-port 179" fi (( NEWCOUNT++ )) done ### if debug is on log the command we just assembled if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 7 - ${DISCMD}" | logger -p local0.debug; fi ### One the primary slot execute the command to delete the non self IP BGP connections. export CONNECTIONS=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "${DISCMD}"` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 8 - $CONNECTIONS" | logger -p local0.debug; fi ### disable virtual address 1 if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): VA1 ($VIRTUAL_ADDRESS1)" | logger -p local0.debug; fi if [ ! -z "$VIRTUAL_ADDRESS1" ]; then if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disabling Virtual Address 1 ($VIRTUAL_ADDRESS1)" | logger -p local0.debug; fi export VA1_PART=`tmsh list ltm virtual-address $VIRTUAL_ADDRESS1 | grep 'partition' | awk {'print $NF'}` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): cmd: ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT tmsh modify cli admin-partitions update-partition $VA1_PART && tmsh modify /ltm virtual-address $VIRTUAL_ADDRESS1 enabled no " | logger -p local0.debug; fi export VA2_UPCMD=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "tmsh modify cli admin-partitions update-partition $VA1_PART && tmsh modify /ltm virtual-address $VIRTUAL_ADDRESS1 enabled no"` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): virtual address 1 disabled?" | logger -p local0.debug; fi fi ### disable virtual address 2 if [ ! -z "$VIRTUAL_ADDRESS2" ]; then if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disabling Virtual Address 2 ($VIRTUAL_ADDRESS2)" | logger -p local0.debug; fi export VA2_PART=`tmsh list ltm virtual-address $VIRTUAL_ADDRESS2 | grep 'partition' | awk {'print $NF'}` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): update-partition - $VA2_PART" | logger -p local0.debug; fi export VA2_UPCMD=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "tmsh modify cli admin-partitions update-partition $VA2_PART && tmsh modify /ltm virtual-address $VIRTUAL_ADDRESS2 enabled no"` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): cmd: virtual address 2 disabled?" | logger -p local0.debug; fi fi ### track number of times this monitor has failed if [ -e "$TRACKING_FILENAME" ] then export COUNT=`cat $TRACKING_FILENAME` export NEW_COUNT=$((${COUNT}+1)) echo $NEW_COUNT > $TRACKING_FILENAME else echo 1 > $TRACKING_FILENAME export NEW_COUNT=1 fi ### notify log - failure count echo "EAV `basename $0` ($$): Pool $POOL only has $AVAILABLE available of $TOTAL_POOL_MEMBERS total members, failing site. Virtual servers ($VIRTUAL_SERVER1 and $VIRTUAL_SERVER2) will be disabled and all connections with destination port 179 will be terminated. Virtual servers must be manually enabled after pool $MEMBER_THRESHOLD or more pool members are available. This monitor has failed $NEW_COUNT times." | logger -p local0.debug # remove the pidfile if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): removing the pidfile..." | logger -p local0.debug; fi export PIDBGONE=`rm -f $PIDFILE` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): pidfile has been removed ($PIDBGONE)" | logger -p local0.debug; fi export END=`date +%Y%m%d-%H:%M:%S` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): stopped at $END" | logger -p local0.debug; fi else if [ -e "$TRACKING_FILENAME" ] then ### log the status echo "EAV `basename $0` ($$): Pool $POOL has $AVAILABLE members of $TOTAL_POOL_MEMBERS total members. No change to virtual servers ($VIRTUAL_SERVER1 and $VIRTUAL_SERVER2). No change to port 179 connections. Virtual servers must be manually enabled to pass traffic if they are disabled." | logger -p local0.debug rm -f $TRACKING_FILENAME fi ### remove the pidfile if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): removing the pidfile..." | logger -p local0.debug; fi export PIDBGONE=`rm -f $PIDFILE` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): pidfile has been removed ($PIDBGONE)" | logger -p local0.debug; fi export END=`date +%Y%m%d-%H:%M:%S` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): stopped at $END" | logger -p local0.debug; fi echo "UP" fi fi307Views0likes0CommentsTACACS+ 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+ 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 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.3KViews1like0CommentsExternal SMTP monitor (written in Go)
Problem this snippet solves: We wrote an external SMTP monitor to work around K99840695. It moderatly customizable, supports STARTTLS and has builtin support to test content filtering via EICAR/GTUBE. The code is on Github, auto-generated releases are on the project's [https://github.com/hreese/f5-smtp-monitor/releases](release page). Code : https://github.com/hreese/f5-smtp-monitor Tested this on version: 11.6352Views0likes0CommentsHTTP 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.9KViews0likes6CommentsMonitor SNMP OID result check
Problem this snippet solves: The purpose of this monitor is to check for the value of an SNMP OID and mark the server up if it equals the result variable which is customizable. Copy the monitor text below to a new file called snmp-check and move that file to the /usr/bin/monitors folder in BIG-IP. Make it executable by typing “chmod 755 snmp-check” Create a new monitor of type External. Set the Interval and Timeouts desired. Define the location of the file. Add 3 variables, one with Name “OID” (case-sensitive) which contains the OID you wish to poll. Add the next with Name “community” which contains the community string. The last with the Name “result” which contains the value you are looking for. In the Configuration Drop-down box, select Advanced and then under the Alias Service Port, select SNMP or type in the customer SNMP port number. Code : # (file not fixed yet)429Views0likes1CommentInterface Failsafe Monitor
Problem this snippet solves: Another slick solution for a couple of common requirement in high performance environments: To fail over immediately when a physical network connection fails connectivity, or if the number of active members in a trunk falls below the configured minimum. The first sample supports individual interface monitoring. The second supports the more complex trunk management. How to use this snippet: http://devcentral.f5.com/Default.aspx?tabid=63&articleType=ArticleView&articleId=166 Monitor Source: Interface Failsafe only #!/bin/bash # (c) Copyright 2007 F5 Networks, Inc. # Kirk Bauer # Pass in each interface to monitor via the Arguments field in the GUI # Collect arguments (first remove IP and port as we don't use those) shift shift interfaces="$*" b interface show > /tmp/b_interface_show for i in $interfaces ; do status=`grep "^ *$i " /tmp/b_interface_show | awk '{print $2}'` if [ "$status" != "UP" ] ; then logger -p local0.notice "$MON_TMPL_NAME: interface $i is not up (status: $status)" exit 1 fi done # All specified interfaces are up... echo "up" exit 0 Monitor Source: Interface Failsafe w/Trunk Minumum Active Members support Code : #!/bin/bash # (c) Copyright 2007 F5 Networks, Inc. # Kirk Bauer # Pass in each interface to monitor via the Arguments field in the GUI. # Each interface may be one of the following: # Physical interface (1.1, 1.2, etc) # Name of a trunk (if at least one interface is up the trunk is considered up) # trunk=min where trunk is the name of the trunk and min is the minimum number # of physical interfaces in the trunk that must be up # For example, the following arguments will make sure that the 1.1 interface is # up and test_trunk has at least two interfaces in it that are up: # 1.1 test_trunk=2 # To test on command-line, use fillers for first two arguments: # /usr/bin/monitors/interface_monitor.sh X X 1.1 1.2 1.3 ... # Collect arguments (first remove IP and port as we don't use those) shift shift interfaces="$*" b interface show > /tmp/b_interface_show for i in $interfaces ; do if grep -q "^ *$i " /tmp/b_interface_show ; then # Physical Interface status=`grep "^ *$i " /tmp/b_interface_show | awk '{print $2}'` if [ "$status" != "UP" ] ; then logger -p local0.notice "$MON_TMPL_NAME: interface $i is not up (status: $status)" exit 1 fi else # Not a physical interface, assume a trunked interface trunk="$i" reqcount=1 if echo "$trunk" | grep -q '=' ; then trunk="$(echo "$i" | sed 's/=.*//')" reqcount="$(echo "$i" | sed 's/^.*=//')" fi upcount=0 for status in `grep "$trunk" /tmp/b_interface_show | awk '{print $2}'` ; do [ "$status" == "UP" ] && upcount=$[$upcount+1] done if [ $upcount -lt $reqcount ] ; then logger -p local0.notice "$MON_TMPL_NAME: trunk $trunk is down (has only $upcount active interfaces, $reqcount required)" exit 1 fi fi done # All specified interfaces are up... echo "up" exit 0356Views0likes4CommentsF5 StoreFront XML Broker Monitor
Problem this snippet solves: I successfully was able to manually build a monitor outside of the iApp for Citrix Storefront deployment, that is a little more complex than the original one. How to use this snippet: Just copy and past this into your send string for your monitor. There are a few things that will have to be adjusted, such as the host, the username, and password, and content-length. To find the proper content length, plug in your information that is necessary. Then take the characters between the '<?xml version' and '</NFuseProtocol>' and past them into a text editor. Remove all escape characters such as "\". Highlight the string and if your text editor has the option, it will show you the character count that you selected. This is your new content-length. If the code is executed correctly you will see a list of published Apps, such as Notepad. If you wanted to have a monitor that didn't pass the username and password in clear text, you will need to do so in an external monitor. In my use case, the user permissions were locked down just enough to make the monitor work and that is all. Code : POST /scripts/wpnbr.dll HTTP/1.1\r\nContent-Length: 492\r\nContent-Type: text/xml\r\nConnection: close\r\nHost: hostname\r\n\r\n permissions all ica30 content user password domain Tested this on version: 13.0493Views3likes0CommentsHTTPS 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.6KViews1like6CommentsNTP 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.5KViews0likes23Comments