Pool Member Status Page on a Virtual Server v9¶
Contributed by: Bhattman, based off Samples: pool_status_page¶
Description¶
/etc/cron.daily/pool_member_status_list.cron
#!/bin/bash
# The command below is included because it was required when patched to 9.3.1 HF6
b db bigpipe.displayservicenames false
b pool all member all | grep pool_ | awk '{print "\""$3"\""}'> | sort >/var/class/pool_member_status_list.class
### Only performs a b load when it's on the active unit.###
if [ "`/bin/ps1`" == "Active" ]; then
b load
fi
exit 0
chmod +755 /etc/cron.daily/pool_member_status_list.cron
Main >> Local Traffic >> iRules
Select Data Group List
Click create
enter name: pool_member_status_list
enter Type: External
Path / Filename: /var/class/pool_member_status_list.class
File Content: String
iRule Source¶
when HTTP_REQUEST {
if { [HTTP::uri] eq "/status" } {
set response "<html><head><title>BIGIP Pool Member Status - \
[clock format [clock seconds]]</title><meta http-equiv='refresh' content='300;\
url=http://[HTTP::host]/status'></head><h1>BIGIP Pool Member Status - [clock format [clock seconds]]</h1>\
<table border='1'><tr><th>Status</th><th>Pool Name</th><th>Member</th><th>Port</th></tr>"
foreach { selectedpool } $::pool_member_status_list {
if { [catch {
scan $selectedpool {%[^/]/%[^:]:%s} poolname addr port
switch -glob [LB::status pool $poolname member $addr $port] {
"up" {
append response "<tr><td><font style=\"color:green\"><b>UP</b></font></td>\
<td>[string tolower $poolname]</td><td>$addr</td><td>$port</td><tr>"
}
"down" {
append response "<tr><td><font style=\"color:red\"><b>DOWN</b></font></td>\
<td>[string tolower $poolname]</<d><td>$addr</td><td>$port</td><tr>"
}
"session_enabled" {
append response "<tr><td><font style=\"color:blue\"><b>ENABLED</b></font></td>\
<td>[string tolower $poolname]</td><td>$addr</td><td>$port</td><tr>"
}
"session_disabled" {
append response "<tr><td><font style=\"color:black\"><b>DISABLED</b></font></td>\
<td>[string tolower $poolname]</td><td>$addr</td><td>$port</td><tr>"
}
Default {
append response "<tr><td><font style=\"color:orange\"><b>INVALID</b></font></td>\
<td>[string tolower $poolname]</td><td>$addr</td><td>$port</td><tr>"
}
}
#SWITCH END
} errmsg] } {
append response "<tr><td><font style=\"color:orange\"><b>INVALID</b></font></td>\
<td>[string tolower $poolname]</td><td>$addr</td><td>$port</td><tr>"
}
}
append response "</table></body></html>"
HTTP::respond 200 content $response "Content-Type" "text/html" "Cache-Control" "no-cache, must-revalidate" "Expires" "Mon, 26 Jul 1997 05<!--:00:00 GMT"-->
}
if { [HTTP::uri] eq "/rss" } {
set response "<?xml version=\"1.0\" encoding=\"utf-8\"?><rss version=\"2.0\"><channel> \
<title>BigIP Server Pool Status</title><description>Server Pool Status</description> \
<language>en</language><pubDate>[clock format [clock seconds]]</pubDate>\<ttl>60</ttl>"
foreach { selectedpool } $::pool_member_status_list {
if { [catch {
scan $selectedpool {%[^/]/%[^:]:%s} poolname addr port
switch -glob [LB::status pool $poolname member $addr $port] {
"up" {
append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:green\">UP</font></b></description></item>"
}
"down" {
append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:red\">DOWN</font></b></description></item>"
}
"session_enabled" {
append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:blue\">ENABLED</font></b></description></item>"
}
"session_disabled" {
append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:black\">DISABLED</font></b></description></item>"
}
Default {
append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:orange\">INVALID</font></b></description></item>"
}
}
#SWITCH END
} errmsg] } {
append response "<item><title>[string tolower $poolname] Status</title><description>Member $addr:$port is <b>\
<font style=\"color:orange\">INVALID</font></b></description></item>"
}
}
append response "</channel></rss>"
HTTP::respond 200 content $response "Content-Type" "text/xml" "Cache-Control" "no-cache, must-revalidate" "Expires" "Mon, 26 Jul 1997 05<!--:00:00 GMT"-->
}
}
This code was reworked by David Larsen¶
/etc/cron.daily/pool_member_status_list.cron
#!/bin/bash
b db bigpipe.displayservicenames false
b pool all |grep "POOL MEMBER" | awk '{sub(":any",":0",$4);print "\""$4"\","}' | sort >/var/class/pool_member_status_list.class
b load
exit 0
chmod +755 /etc/cron.daily/pool_member_status_list.cron
Main >> Local Traffic >> iRules
Select Data Group List
Click create
enter name: pool_member_status_list
enter Type: External
Path / Filename: /var/class/pool_member_status_list.class
File Content: String
iRule Source¶
when HTTP_REQUEST {
if { [HTTP::uri] eq "/status" } {
set response "<html><head><title>BIGIP Pool Member Status - \
[clock format [clock seconds]]</title><meta http-equiv='refresh' content='300;\
url=http://[HTTP::host]/status'></head><h1>BIGIP Pool Member Status - [clock format [clock seconds]]</h1>\
<table border='1'><tr><th>Status</th><th>Pool Name</th><th>Member</th><th>Port</th></tr>"
foreach { selectedpool } $::pool_member_status_list {
if { [catch {
scan $selectedpool {%[^/]/%[^:]:%s} poolname addr port
switch -glob [LB::status pool $poolname member $addr $port] {
"up" {
append response "<tr><td><font style=\"color:green\"><b>UP</b></font></td>\
<td>[string tolower $poolname]</td><td>$addr</td><td>$port</td><tr>"
}
"down" {
append response "<tr><td><font style=\"color:red\"><b>DOWN</b></font></td>\
<td>[string tolower $poolname]</<d><td>$addr</td><td>$port</td><tr>"
}
"session_enabled" {
append response "<tr><td><font style=\"color:blue\"><b>ENABLED</b></font></td>\
<td>[string tolower $poolname]</td><td>$addr</td><td>$port</td><tr>"
}
"session_disabled" {
append response "<tr><td><font style=\"color:black\"><b>DISABLED</b></font></td>\
<td>[string tolower $poolname]</td><td>$addr</td><td>$port</td><tr>"
}
Default {
append response "<tr><td><font style=\"color:orange\"><b>INVALID</b></font></td>\
<td>[string tolower $poolname]</td><td>$addr</td><td>$port</td><tr>"
}
}
#SWITCH END
} errmsg] } {
append response "<tr><td><font style=\"color:orange\"><b>INVALID</b></font></td>\
<td>[string tolower $poolname]</td><td>$addr</td><td>$port</td><tr>"
}
}
append response "</table></body></html>"
HTTP::respond 200 content $response "Content-Type" "text/html" "Cache-Control" "no-cache, must-revalidate" "Expires" "Mon, 26 Jul 1997 05<!--:00:00 GMT"-->
}
if { [HTTP::uri] eq "/rss" } {
set response "<?xml version=\"1.0\" encoding=\"utf-8\"?><rss version=\"2.0\"><channel> \
<title>BigIP Server Pool Status</title><description>Server Pool Status</description> \
<language>en</language><pubDate>[clock format [clock seconds]]</pubDate>\<ttl>60</ttl>"
foreach { selectedpool } $::pool_member_status_list {
if { [catch {
scan $selectedpool {%[^/]/%[^:]:%s} poolname addr port
switch -glob [LB::status pool $poolname member $addr $port] {
"up" {
append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:green\">UP</font></b></description></item>"
}
"down" {
append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:red\">DOWN</font></b></description></item>"
}
"session_enabled" {
append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:blue\">ENABLED</font></b></description></item>"
}
"session_disabled" {
append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:black\">DISABLED</font></b></description></item>"
}
Default {
append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:orange\">INVALID</font></b></description></item>"
}
}
#SWITCH END
} errmsg] } {
append response "<item><title>[string tolower $poolname] Status</title><description>Member $addr:$port is <b>\
<font style=\"color:orange\">INVALID</font></b></description></item>"
}
}
append response "</channel></rss>"
HTTP::respond 200 content $response "Content-Type" "text/xml" "Cache-Control" "no-cache, must-revalidate" "Expires" "Mon, 26 Jul 1997 05<!--:00:00 GMT"-->
}
}
ALTERNATE VERSION¶
http://<status_vip>/status?p=poolname_here
or
http://<status_vip>/rss?p=poolname_here
when RULE_INIT {
######################################################
# BEGIN CONFIGURATION
# The name of your company.
set ::org_name "OrgName"
# Base login URL for your loadbalancer. This should be set to the floating IP in the case of HA BigIPs.
# It should be in URL form without trailing slash (e.g., "https://10.1.1.1").
set ::lb_address "https://10.10.10.10"
# The friendly name of this particular load balancer group.
set ::lb_name "Load Balancer Group One"
######################################################
# PAGE CONTENT BELOW THIS POINT
# Note: All delivered pages are set to not cache at the browser and immediately expire.
# HTML page displayed when an error condition must be handled.
set error_404 {
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>404 Not Found</title>
<style><!--
#body \{font-family: arial,sans-serif\}
#div.nav \{margin-top: 1ex\}
#div.nav A \{font-size: 10pt; font-family: arial,sans-serif\}
#span.nav \{font-size: 10pt; font-family: arial,sans-serif; font-weight: bold\}
#div.nav A,span.big \{font-size: 12pt; color: #0000cc\}
#div.nav A \{font-size: 10pt; color: black\}
#A.l:link \{color: #6f6f6f\}
#A.u:link \{color: green\}
//--></style>
</head>
<body text=#000000 bgcolor=#ffffff>
<table border=0 cellpadding=2 cellspacing=0 width="100%"><tr><td rowspan=3 width="1%" nowrap>
<b><font face=times color=#0039b6 size=10>$::org_name </font></b>
<td> </td></tr>
<tr><td bgcolor=#3366cc><font face=arial,sans-serif color=#ffffff> <b>404 (Page not found) Error</b></td></tr>
<tr><td> </td></tr></table>
<blockquote>
<H1>Not Found</H1>
The requested URL <code>$HTTP_URI</code> was not found on this server.
<p>
</blockquote>
<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=#3366cc><img alt="" width=1 height=4></td></tr></table>
</body></html>
}
# HTML page displayed bookending the generated table of pool members.
set page_200 {
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>Pool Member Status - $::lb_name</title>
<style><!--
#body \{font-family: arial,sans-serif\}
#div.nav \{margin-top: 1ex\}
#div.nav A \{font-size: 10pt; font-family: arial,sans-serif\}
#span.nav \{font-size: 10pt; font-family: arial,sans-serif; font-weight: bold\}
#div.nav A,span.big \{font-size: 12pt; color: #0000cc\}
#div.nav A \{font-size: 10pt; color: black\}
#A.l:link \{color: #6f6f6f\}
#A.u:link \{color: green\}
//--></style>
</head>
<body text=#000000 bgcolor=#ffffff>
<table border=0 cellpadding=2 cellspacing=0 width="100%"><tr><td rowspan=3 width="1%" nowrap>
<b><font face=times color=#0039b6 size=10>$::org_name </font></b>
<td> </td></tr>
<tr><td bgcolor=#3366cc><font face=arial,sans-serif color=#ffffff> <b>Pool Node Status - ($::lb_name)</b></td></tr>
<tr><td> </td></tr></table>
<blockquote>
<h2>$timestamp</h2>
<p><b>Note:</b> <i>Access to the pool links is restricted to load balancer administrators. Click <a href=\"$::lb_address\">here</a> to log in first.</i></p>
$response
<p>
</blockquote>
<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=#3366cc><img alt="" width=1 height=4></td></tr></table>
</body></html>
}
# END CONFIGURATION
######################################################
}
when HTTP_REQUEST {
######################################################
# SETUP AND DATA COLLECTION
# Catch attempts to do cross-site scripting to our generated HTML. Hopefully
# you're not generating the status page in your trusted sites zone in IE... but
# if you are...
if { [HTTP::uri] matches_regex {((\%3C)|<)[^\n]+((\%3E)|>)} } {
set HTTP_URI " !! *withheld* - CSS attempt !! "
} else {
set HTTP_URI [HTTP::uri]
}
# Process the querystring from the HTML request if there is one. Ignore all but p=.
if { [HTTP::query] ne "" } {
set query 1
set namevals [split [HTTP::query] "&"]
for {set i 0} {$i < [llength $namevals]} {incr i} {
set params [split [lindex $namevals $i] "="]
set pnum [expr $i+1]
if { ([lindex $params 0] eq "p") } {
set manualpool "[URI::query [HTTP::uri] [lindex $params 0]]"
}
}
} else {
set query 0
}
######################################################
# PAGE GENERATION - HUMAN READABLE STATUS
# Generate the status page with either all content or content from a single pool.
if { ([HTTP::uri] eq "/status") || (([HTTP::uri] starts_with "/status?") && ($query) && [info exists manualpool]) } {
set timestamp "[clock format [clock seconds]]"
set response "<table border='1'><tr><th>Status</th><th>Pool Name</th><th>Member</th><th>Port</th></tr>"
set count 0
foreach { selectedpool } $::pool_member_status_list {
set display 1
if { [catch {
scan $selectedpool {%[^/]/%[^:]:%s} poolname addr port
switch -glob [LB::status pool $poolname member $addr $port] {
"up" {
if { ($query) && ($manualpool ne "") && ($poolname ne $manualpool) } { set display 0 } else { set display 1 }
if {$display} { append response "<tr><td><font style=\"color:green\"><b>UP</b></font></td>\
<td><a href=\"$::lb_address/tmui/Control/jspmap/tmui/locallb/pool/stats.jsp?name=[string tolower $poolname]\">[string tolower $poolname]</a></td><td>$addr</td><td>$port</td><tr>"
incr count }
}
"down" {
if { ($query) && ($manualpool ne "") && ($poolname ne $manualpool) } { set display 0 } else { set display 1 }
if {$display} { append response "<tr><td><font style=\"color:red\"><b>DOWN</b></font></td>\
<td><a href=\"$::lb_address/tmui/Control/jspmap/tmui/locallb/pool/stats.jsp?name=[string tolower $poolname]\">[string tolower $poolname]</a></td><td>$addr</td><td>$port</td><tr>"
incr count }
}
"session_enabled" {
if { ($query) && ($manualpool ne "") && ($poolname ne $manualpool) } { set display 0 } else { set display 1 }
if {$display} { append response "<tr><td><font style=\"color:blue\"><b>ENABLED</b></font></td>\
<td><a href=\"$::lb_address/tmui/Control/jspmap/tmui/locallb/pool/stats.jsp?name=[string tolower $poolname]\">[string tolower $poolname]</a></td><td>$addr</td><td>$port</td><tr>"
incr count }
}
"session_disabled" {
if { ($query) && ($manualpool ne "") && ($poolname ne $manualpool) } { set display 0 } else { set display 1 }
if {$display} { append response "<tr><td><font style=\"color:black\"><b>DISABLED</b></font></td>\
<td><a href=\"$::lb_address/tmui/Control/jspmap/tmui/locallb/pool/stats.jsp?name=[string tolower $poolname]\">[string tolower $poolname]</a></td><td>$addr</td><td>$port</td><tr>"
incr count }
}
Default {
if { ($query) && ($manualpool ne "") && ($poolname ne $manualpool) } { set display 0 } else { set display 1 }
if {$display} { append response "<tr><td><font style=\"color:orange\"><b>INVALID</b></font></td>\
<td><a href=\"$::lb_address/tmui/Control/jspmap/tmui/locallb/pool/stats.jsp?name=[string tolower $poolname]\">[string tolower $poolname]</a></td><td>$addr</td><td>$port</td><tr>"
incr count }
}
}
#SWITCH END
} errmsg] } {
# All else has failed. Punt.
if { ($query) && ($manualpool ne "") && ($poolname ne $manualpool) } { set display 0 } else { set display 1 }
if {$display} { append response "<tr><td><font style=\"color:orange\"><b>INVALID</b></font></td>\
<td><a href=\"$::lb_address/tmui/Control/jspmap/tmui/locallb/pool/stats.jsp?name=[string tolower $poolname]\">[string tolower $poolname]</a></td><td>$addr</td><td>$port</td><tr>"
incr count }
}
}
# If we made it all the way through the generation of the table and didn't display anything, there must
# have been no matching pool.
if { $count == 0 } {
append response "<tr><td colspan=4><center>No pool members selected.</center></td></tr></table>"
} else {
append response "</table>"
}
HTTP::respond 200 content [subst $::page_200] "Content-Type" "text/html" "Cache-Control" "no-cache, must-revalidate" "Expires" "Mon, 26 Jul 1997 05<!--:00:00 GMT"-->
}
######################################################
# PAGE GENERATION - RSS FEED
# Generate an RSS feed element with either all content or content from a single pool.
if { ([HTTP::uri] eq "/rss") || (([HTTP::uri] starts_with "/rss?") && ($query) && [info exists manualpool]) } {
set response "<?xml version=\"1.0\" encoding=\"utf-8\"?><rss version=\"2.0\"><channel> \
<title>Server Pool Status ($::lb_name)</title><description>Server Pool Status ($::lb_name)</description> \
<language>en</language><pubDate>[clock format [clock seconds]]</pubDate>\<ttl>60</ttl>"
set count 0
foreach { selectedpool } $::pool_member_status_list {
set display 1
if { [catch {
scan $selectedpool {%[^/]/%[^:]:%s} poolname addr port
switch -glob [LB::status pool $poolname member $addr $port] {
"up" {
if { ($query) && ($manualpool ne "") && ($poolname ne $manualpool) } { set display 0 } else { set display 1 }
if {$display} { append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:green\">UP</font></b></description></item>"
incr count }
}
"down" {
if { ($query) && ($manualpool ne "") && ($poolname ne $manualpool) } { set display 0 } else { set display 1 }
if {$display} { append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:red\">DOWN</font></b></description></item>"
incr count }
}
"session_enabled" {
if { ($query) && ($manualpool ne "") && ($poolname ne $manualpool) } { set display 0 } else { set display 1 }
if {$display} { append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:blue\">ENABLED</font></b></description></item>"
incr count }
}
"session_disabled" {
if { ($query) && ($manualpool ne "") && ($poolname ne $manualpool) } { set display 0 } else { set display 1 }
if {$display} { append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:black\">DISABLED</font></b></description></item>"
incr count }
}
Default {
if { ($query) && ($manualpool ne "") && ($poolname ne $manualpool) } { set display 0 } else { set display 1 }
if {$display} { append response "<item><title>[string tolower $poolname] Status</title><description> \
Member $addr:$port is <b><font style=\"color:orange\">INVALID</font></b></description></item>"
incr count }
}
}
#SWITCH END
} errmsg] } {
# All else has failed. Punt.
if { ($query) && ($manualpool ne "") && ($poolname ne $manualpool) } { set display 0 } else { set display 1 }
if {$display} { append response "<item><title>[string tolower $poolname] Status</title><description>Member $addr:$port is <b>\
<font style=\"color:orange\">INVALID</font></b></description></item>"
incr count }
}
}
if { $count == 0 } {
append response "<item><title>No such pool</title><description> \
<b>No such pool</b></description></item></channel></rss>"
} else {
append response "</channel></rss>"
}
HTTP::respond 200 content $response "Content-Type" "text/xml" "Cache-Control" "no-cache, must-revalidate" "Expires" "Mon, 26 Jul 1997 05<!--:00:00 GMT"-->
}
######################################################
# PAGE GENERATION - ERROR HANDLING AND CLEANUP
# Handle any errors (unknown page requests, bad query strings) and unset my status.
if { (([HTTP::uri] eq "/status") || (([HTTP::uri] starts_with "/status?") && ($query) && [info exists manualpool])) || (([HTTP::uri] eq "/rss") || (([HTTP::uri] starts_with "/rss?") && ($query) && [info exists manualpool])) } {
unset display
unset query
if { [info exists manualpool] } { unset manualpool }
} else {
unset query
HTTP::respond 404 content [subst $::error_404] "Cache-Control" "no-cache, must-revalidate" "Expires" "Mon, 26 Jul 1997 05<!--:00:00 GMT"-->
}
}
The BIG-IP API Reference documentation contains community-contributed content. F5 does not monitor or control community code contributions. We make no guarantees or warranties regarding the available code, and it may contain errors, defects, bugs, inaccuracies, or security vulnerabilities. Your access to and use of any code available in the BIG-IP API reference guides is solely at your own risk.