As I’ve said in a previous post, I’m fairly new to the world of F5; That being said, I’m really enjoying the power and functionality of LTM!
One task I recently undertook was to implement a maintenance/sorry site to display when we do patch releases to our software (or if something was to go horribly wrong at a server level). The solution we opted for was to essentially use our F5 device as a web-server and host the “sorry site” from the NLB appliance. The page would show if the custom monitors we defined on a virtual server reported <1 healthy server in its respective NLB pool; if this criteria was met then an iRule would fire and show our page before relaying the request it onto the VIP.
Since I am using some LTM’s running v10.x and v11.x I’ve opted to use a method that works across both versions. This post has been written for v10.x, but rest assured, it’s a trivial task to get it working on v11; Feel free to ask if you get stuck.
So… Lets get started:
- First we need to generate a .class file with the content of our images (encoded in a Base64 format). This class file is called by our iRule and images are decoded when its run.
Save the following Shell script to a location on your F5; I have called it “Base64encode.sh
## clear images class
echo -n "" > /var/class/images.class
## loop through real images and create base64 data for images class
for i in $(ls /var/images); do
echo "\"`echo \"$i\"|tr '[:upper:]' '[:lower:]'`\" := \"`base64 /var/images/$i|tr -d '\n'`\"," >> /var/class/images.class
done
SCP your image files to your F5 and place them in “/var/images”
Fire up your favourite SSH client and Call Base64encode.sh to enumerate all images in “/var/images” and generate an image.class file (which is exported to /var/class/images.class) of key/values with the following syntax:
“image.png” := “<Base64EncodedString”,
(If you intend to call this class file directly from the iRule OR are referencing this from using a data-group list in LTM v10.x you may need to execute a “B Load” or a “TMSH Load Sys Config” command from SSH so the class file is referenced).
- Next we need to create a Data-group list so our iRule can reference the encoded images, If we were running LTM v11.x we would be forced to download the class file and upload it from “System > File Management > Data Group File List”; however, since this is tutorial is for v10 we can simply reference our class file using a file location. From the GUI navigate to “Local Traffic > iRules > Data Group List > Create” and create the Data group as follows:
Name: images_class
Type: (External File)
Path/Filename: /var/class/images.class
File Contents: String
Key/Value Pair Separator: :=
Access Mode: Read Only
- Now we can assemble our iRule using a template similar to the one written by thepacketmaster. The iRule below does the following:
- Invokes on HTTP Request
- Establishes What VirtualServer pool is responsible for serving up the requested website’s content
- Checks to see if the Active members (health) of the Pool has less than one healthy member
- Adds a verbose entry to F5 log with Client address and requested URL
- Responds with a 200 HTTP code for each image and decodes our Base64 encoded image by referencing the images_class Data Group and subsequently Images.class file we defined in the previous step
- Responds with the HTML of the Sorry/Maintenance Mode Page.
when HTTP_REQUEST {
set VSPool [LB::server pool]
if { [active_members $VSPool] < 1 } {
log local0. "Client [IP::client_addr] requested [HTTP::uri] no active nodes available..."
if { [HTTP::uri] ends_with "bubbles.png" } {
HTTP::respond 200 content [b64decode [lindex $::images_class 0]] "Content-Type" "image/png"
} else {
if { [HTTP::uri] ends_with "background.png" } {
HTTP::respond 200 content [b64decode [lindex $::images_class 0]] "Content-Type" "image/png"
} else {
HTTP::respond 200 content "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
<html xml:lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\"><head>
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">
<title>We'll be back!</title>
<style type=\"text/css\">
body {
background: #f7f4f1 url(background.png) no-repeat top left;
}
#MainContent {
background: url(bubbles.png) no-repeat top right;
height: 500px;
font-family: Verdana, Helvetica, Arial, sans;
font-size: 14px;
color: #625746;
position: absolute;
top: 330px;
left: 180px;
width: 900px;
}
#MainContent p {
width: 450px;
}
a {
color:#60A2B9;
}
a:hover {
text-decoration: none;
}
</style>
</head><body>
<div id=\"MainContent\">
<p><strong>Hi there! Thanks for stopping by.</strong></p>
<p>We're making some changes on the site and expect to be back in a couple of hours.</p>
<p>See you there!</p>
</div>
</body></html>"
}
}
}
}
Replace the CSS/HTML in the above iRule with your own (same goes for the images). Remember that you MUST escape any quote marks in your HTML/JS with a “\”
Please note: I had a hard time getting the above to work with LTM v11; My HTML would show but my text would not. After a bit of head-scratching and research I re-factored the Base64 decode lines (i.e. Lines 6 & 9 above) with the following:
HTTP::respond 200 content [b64decode [class element -value 0 images_class]] "Content-Type" "image/png"
You may also want to look at using the iFile list functionality of LTM11 to serve up images instead of manually base encoding them (even though the above should work): https://devcentral.f5.com/tech-tips/articles/v111-ndashexternal-file-access-from-irules-via-ifiles
- Apply your new iRule to your respective Virtual Server and test it out. Make your Virtual Server monitors “trip” by manually shutting down pool members from the F5 to bring the overall pool into an unhealthy state
I hope this helps anyone struggling to get something similar to this working. As always, feel free to ask questions.
Thanks,
Patrick