Pool Member Status HTML5 Page
Problem this snippet solves:
This allows a publicly accessible status page of Pools and Pool Members. No cron job is needed.
How to install:
- If you only have one pool, you will probably have to comment out the "errmsg" HTML response code. For some reason one pool in the Data Group List kicks out an error. "Errmsg" is on lines 60 and 121.
Edits to the code:
- Line 3 - Modify to your IP scheme to lock down access, or allow all
- Line 8 - Document title information (shows in browser tab)
- Line 10 - Author information
- Line 11 - Auto refresh interval (update line 30 if you change this)
- Line 34 - Page title information
- Lines 44-45 - Links to other locations if you have devices in different locations/groups
- Line 122 - Comment out with a "#" if you don't wish to log each time someone connects or the page refreshes
*HTML source and inspiration taken from user The Bhattman and the code is revamped: https://devcentral.f5.com/codeshare/pool-member-status-page-on-a-virtual-server-v10
How to use this snippet:
Screenshot
Code :
when HTTP_REQUEST {
if { [HTTP::uri] eq "/status" } {
set response "<!DOCTYPE html>\r\n"
append response "<html lang=\"en\">\r\n"
append response "<head>\r\n"
append response "<title>F5 LTM Server Pool Status</title>\r\n"
append response "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\r\n"
append response "<meta name=\"author\" content=\"Troy Luschen 16Mar2017\"/>\r\n"
append response "<meta http-equiv=\"refresh\" content=\"30\"/>\r\n"
append response "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>\r\n"
# ------ Style Section ------
append response "<style>\r\n"
append response "h1 {font-weight:normal;text-transform:uppercase;color:#000000;background-color:#D9D7DE;border: 5px groove #699635;font-size:100%;text-align:center;font-family:lucida sans unicode, lucida grande, sans-serif;line-height:3;margin:0px;padding:5px;min-width:675px;width:50%;}\r\n"
append response "h2 {font-weight:normal;font-style:italic;color:#000000;letter-spacing:0pt;word-spacing:1pt;border: 0px;font-size:80%;text-align:center;font-family:lucida sans unicode, lucida grande, sans-serif;line-height:1;margin:0px;padding:0px;min-width:675px;width:50%;}\r\n"
append response "h4 {font-weight:bold;color:#000000;letter-spacing:0pt;word-spacing:1pt;border: 0px;font-size:90%;text-align:left;font-family:lucida sans unicode, lucida grande, sans-serif;line-height:1;margin:0px;padding:0px;}\r\n"
append response "div.layout1 {width:50%;padding:5px;min-width:675px;}\r\n"
append response "div.layout2 {width:50%;padding:7px;min-width:675px;font-size:90%;font-family:lucida sans unicode, lucida grande, sans-serif;}\r\n"
append response "p {font-weight:normal;color:#000000;border:0px;font-size:90%;text-align:left;font-family:lucida sans unicode, lucida grande, sans-serif;}\r\n"
append response "p.time {font-weight:normal;color:#000000;letter-spacing:1pt;word-spacing:1pt;font-size:80%;text-align:left;font-family:arial,helvetica,sans-serif;line-height:1;margin:0px;padding:0px;}\r\n"
append response "p.time2 {font-weight:normal;color:#000000;letter-spacing:1pt;word-spacing:1pt;font-size:70%;text-align:left;font-family:arial,helvetica,sans-serif;line-height:1;margin:0px;padding:0px;}\r\n"
append response "li {font-weight:normal;color:#000000;border:0px;font-size:90%;text-align:left;font-family:lucida sans unicode, lucida grande, sans-serif;line-height:1;margin:0px;padding:0px;}\r\n"
append response "#pool_status th {font-weight:bold;padding-top:12px;padding-bottom:12px;text-align:left;background-color:#85c1e9;color:#434474;}\r\n"
append response "#pool_status tr table tr:nth-child(even){background-color:#ddd;}\r\n"
append response "#pool_status tr table tr:hover {font-weight:bold;font-size:110%;font-variant:small-caps;background-color:#B7950B;}\r\n"
append response "</style>\r\n"
# ------ End Style Section ------
append response "</head>\r\n"
append response "<body>\r\n"
append response "<p class=\"time\">Status Check Time: [clock format [clock seconds]]</p>\r\n"
append response "<p class=\"time2\">Auto Refreshes Every 30 Seconds</p>\r\n"
append response "<br>\r\n"
append response "<h1>F5 LTM Server Pool Status</h1>\r\n"
append response "<h2>Status of Virtual Servers and their Pool Members</h2>\r\n"
append response "<br>\r\n"
# Table containing "F5 Locations" and "Critical" sections.
append response "<div class=\"layout1\">\r\n"
append response "<table style=\"width:100%;font-family:lucida sans unicode, lucida grande, sans-serif;\">\r\n"
append response " <tr>\r\n"
append response " <td style=\"width:50%;text-align:left;vertical-align:top;\">\r\n"
append response " <h4>F5 Locations</h4>\r\n"
append response " <ul>\r\n"
append response " <li><a href=\"https://<;IP>/status\">LTM Pool Status at Location 1</a></li>\r\n"
append response " <li><a href=\"https://<;IP>/status\">LTM Pool Status at Location 2</a></li>\r\n"
append response " </ul>\r\n"
append response " </td>\r\n"
append response " <td style=\"width:50%;border-style:dashed;border-width:3px;padding:5px;font-size:90%;text-align:left;vertical-align:top;\">\r\n"
append response " <h4 style=\"font-size:100%;font-weight:bold;color:#FF0000;padding-bottom:10px;\">Critical - No Available Pool Members</h4>\r\n"
foreach { selectedpool } [class get BIG-IP_Pool_Member_Status] {
set thispool [getfield $selectedpool " " 1]
if { [catch {
if { [active_members $thispool] < 1 } {
# Pool Status for pools with no active members
append response " <div style=\"line-height:125%;\">$thispool</div>\r\n"
} else {
# Skip
}
} errmsg ] } {
append response " <div style=\"line-height:125%;\">$thispool <span style=\"font-weight:bold;color:#FF0000\">ERROR: Invalid pool name</span></div>\r\n"
}
}
append response " </td>\r\n"
append response " </tr>\r\n"
append response "</table>\r\n"
append response "</div>\r\n"
# End Table containing "F5 Locations" and "Critical" sections.
# Tables containing Pools with at least one available pool member.
foreach { selectedpool } [class get BIG-IP_Pool_Member_Status] {
set thispool [getfield $selectedpool " " 1]
if { [catch {
if { [active_members $thispool] < 1 } {
# Pool Status for pools with no active members
# Skip. This is implemented above in the first table.
} else {
# Pool Status
append response "<div class=\"layout2\">\r\n"
append response "<table style=\"width:100%;\" id=\"pool_status\">\r\n"
append response " <tr>\r\n"
append response " <th colspan=\"2\">$thispool</th>\r\n"
append response " </tr>\r\n"
append response " <tr>\r\n"
append response " <td style=\"width:50%;text-align:left;vertical-align:top;\">\r\n"
append response " <h4>Pool Member Status</h4>\r\n"
# Pool Member Status Section
append response " <table>\r\n"
foreach { pmem } [members -list $thispool] {
append response " <tr>\r\n"
append response " <td style=\"font-size:90%;\">\r\n"
append response " [join $pmem ":"] is "
set nodestatus "[LB::status pool $thispool member [getfield $pmem " " 1] [getfield $pmem " " 2]]"
if {$nodestatus == "up"} {
append response "<span style=\"font-weight:bold;color:#17ab4a;\">Up</span>\r\n"
} elseif {$nodestatus == "down"} {
append response "<span style=\"font-weight:bold;color:#FF0000;\">Down</span>\r\n"
} elseif {$nodestatus == "session_disabled"} {
append response "<span style=\"font-weight:bold;color:#B7950B;\">Disabled</span>\r\n"
} else {
append response "<span style=\"font-weight:bold;color:#17ab4a;\">$nodestatus</span>\r\n"
}
set nodestatus null
append response " </td>\r\n"
append response " </tr>\r\n"
}
append response " </table>\r\n"
# End Pool Member Status Section
append response " </td>\r\n"
append response " <td style=\"width:50%;font-size:90%;text-align:left;vertical-align:top;\">\r\n"
append response " </td>\r\n"
append response " </tr>\r\n"
append response "</table>\r\n"
append response "</div>\r\n"
append response "<div style=\"padding-bottom:5px;\"></div>\r\n"
}
} errmsg ] } {
append response "<div style=\"line-height:125%;\">$thispool <span style=\"font-weight:bold;color:#FF0000\">ERROR: Invalid pool name</span></div>\r\n"
}
}
# End Tables containing Pools with at least one available pool member.
append response "</body>\r\n"
append response "</html>\r\n"
HTTP::respond 200 content $response "Content-Type" "text/html"
}
log local0.info "$response"
}
Tested this on version:
12.1
- LoyalSoldierAltostratus
Cool. Can you upload the whole code for an XML solution?
- Maneesh_72711Cirrostratus
Above my comment I had already posted it.
- ANNimbostratus
Hi Maneesh,, Does you code allow users to select/search particular pool??
- Maneesh_72711Cirrostratus
For searching we would have to write a HTML page which calls XML using javascript for search capability.
I am asking a developer to assist me writing a HTML page for the same which will give search capability for finding pools. XML was taking long to load hence have coded a JSON i-rule which would be faster fetching results.
I would post the JSON code as well below this comment.
- Maneesh_72711Cirrostratus
when HTTP_REQUEST { IP Block. Allow specific users/groups to access the status page. if { [IP::addr [IP::client_addr] equals 10.0.0.0/8 ] } { if { [HTTP::uri] eq "/json" } { set response "{ \"PoolDet\": ["
foreach { selectedpool } [class get test] { set thispool [getfield $selectedpool " " 1] if { [catch { if { [active_members $thispool] < 1 } { Pool Status for pools with no active members append response "{\"PoolName\": \"$thispool\"," append response "\"PoolStatus\":\"Inactive\"}," } else { Pool Status append response "{" append response "\"PoolName\": \"$thispool\"," append response "\"PoolStatus\":\"Active\"," append response "\"PoolMemberDet\": \[" Pool Member Status Section foreach { pmem } [members -list $thispool] { append response "{\"PoolMember\": \"$pmem\"," set nodestatus "[LB::status pool $thispool member [getfield $pmem " " 1] [getfield $pmem " " 2]]" if {$nodestatus == "up"} { append response "\"PoolMemberStatus\": \"Up\"" } elseif {$nodestatus == "down"} { append response "\"PoolMemberStatus\": \"Down\"" } elseif {$nodestatus == "session_disabled"} { append response "\"PoolMemberStatus\": \"Disabled\"" } else { append response "\"PoolMemberStatus\": \"$nodestatus\"" } set nodestatus null append response "}," } append response "{ \"PoolMember\": \"filler\", \"PoolMemberStatus\": \"filler\" }\]}," } } errmsg ] } { append response "\"PoolNameError\": \"$thispool: ERROR: Invalid pool name\"" } } append response "{" append response "\"PoolName\": \"filler\"," append response "\"PoolStatus\":\"filler\"" append response "}\]}" End Pool Member Status Section HTTP::respond 200 content $response "Content-Type" "application/json" }
} }
- Jason_291422Nimbostratus
This is exactly what I have been looking for! But I am running into a little issue where pool members that are Offline still show as Up. The status of Disabled members show correctly. Any thoughts on what could be the issue?
Also, how hard would it be to add some additional information like hostname next to the IP:PORT and the description of the Pool next to the pool name?
Thanks in advance!
- LoyalSoldierAltostratus
Jason,
 
Not sure why your Offline members are showing Up. I checked mine, and I have a couple members offline...one being due to a monitor setting it offline, and the other is due to manually disabling it. Both show Down on the HTML page. Maybe verify your settings/implementation is correct?
 
As far as adding the hostname... I looked into that while building the script and there was no easy/known way to incorporate the hostname from an iRule command, such as "[members -list $thispool]". The "members" command, for example, only pulls the IP and Port. Never tried looking at adding the description field.
 
With that said, you may be able to get those fields added using iCall, but I don't have enough experience with iCall, or time, to attempt it. If you or someone else wants to feel free. Can add that to the script if it works out.
 
One such example: https://devcentral.f5.com/s/feed/0D51T00006pY5mySAC
 
- Jason_291422Nimbostratus
LoyalSoldier,
Yeah its very bizarre as I have a couple pools that are Offline (Enabled) due to monitoring, but the status page still shows those Up. I wouldnt think it mattered if the node showed as Up under Nodes but the monitor has it marked down at the pool, correct?
Even more bizarre, I have a pool that is disabled and I have seen it change to up randomly.
Im supposed to use that actually name of the pool the pool, correct? The only reason I ask is in your screen shot above the pool names look like just random characters.
- LoyalSoldierAltostratus
So I did a test on one of mine and caused a Pool to go Offline (Enabled). All the Nodes are still Available (Enabled). Looking at the Status Page, it now shows that Pool as having no available pool members and lists it in the top Critical box. This is how it should be working. The code is executed based on the pool members, not nodes.
On your disabled pool that randomly changes to Up....does it do that in the Configuration Manager as well? Show the member(s) being marked up in the logs? Would make sure it is operating as expected first outside of the Status Page.
Yes, you must add the actual pool names to the iRule Data Groups area. The screenshot contains gray boxes in areas that will change based on your setup (i.e. Pool Name, IP:Port, etc.). You do NOT put your Pool Names in those areas. The code will do that for you based on the iRule Data Group.
- LenderLive_MitcNimbostratus
Great little script! Thank you for sharing this.