Wiki: iRules API

LTM Maintenance Page


Contributed by: spark, based off several other contributors including citizen_elah

UPDATE: Should now work for v10.x


How to use the LTM as a webserver for a particular directory. In this case, we use it to return a maintenance page with text and images to return when no pool members are available. The maintenance pages are always served up, even if the pool is active. This is partly to handle the case where the pool comes back up while the client is loading all the parts of the maintenance page, and partly to show how to use the LTM as a rudimentary web server.

1. Create the HTML for the maintenance page. It must be on one line, and all double-quotes must be escaped with backslashes., the code must start with " and end with " .

2. Save it to a file /var/class/maint.index.html.class on the LTM.

3. Upload all desired images to /var/images on the BigIP (create the directory as needed).

4. Convert images into base64 format string:

## 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
5. Note that this creates a file called /var/class/images.class -- always run "b load" after creating or updating an external class file!

6. Use the configuration below, changing the error condition and maintenance prefix if needed.

You can easily extend this example to serve up multiple pages, both text and not. You could also just use variables for these files instead of classes, but it seems much more risky to me to edit the actual bigip.conf file or iRule than it does to just edit one file under /var/class/ and do a b load.

Note: The file content is stored in LTM memory upon configuration load, so you should be cautious about serving large amounts of data from the classes.

Configuration, including iRule

The class containing the HTML page will be defined as a Data Group List in the GUI: Local Traffic -> iRules -> Data Group List -> Create...
  • Name: maint_index_html_class
  • Type: (External File)
  • Path/Filename: /var/class/maint.index.html.class
  • File Contents: String
  • Key/Value Pair Separator: :=
  • Access Mode: Read Only

The class containing the base64 encoded image will be defined as a Data Group List in the GUI: Local Traffic -> iRules -> Data Group List -> Create...
  • Name: images_class
  • Type: (External File)
  • Path/Filename: /var/class/images.class
  • File Contents: String
  • Key/Value Pair Separator: :=
  • Access Mode: Read Only

Note: in case of error reduce the size of the image files

iRule which references the HTML and logo class:

when RULE_INIT {
   # set DEBUG to 1 for debug log output
   set static::DEBUG 0


   # Service requests for files (URIs) from the maintenance page.
   # Note that we always service these pages, even if the http_pool is up.
   # The maintenance prefix should be unique in the application and
   #   not conflict with an existing directory on the web servers.
   set maint_prefix "/maintenancepage"

   if { [HTTP::uri] starts_with "$maint_prefix" } {

      # Strip off the $maint_prefix from the URI as you can't easily do variable expansion
      # within a switch statement.
      # Note that requests for the exact maintenance prefix URI will be set to a null string,
      # so handle null in the first switch case.
      set uri [string map [list $maint_prefix ""] [HTTP::uri]]

      # Return the requested page based on the requested URI
      switch -glob -- $uri {
         "" {
            if { $static::DEBUG } { log local0. "Request for $maint_prefix. Redirecting to $maint_prefix/" }
            HTTP::redirect "$maint_prefix/index.html"
         "/" -
         "/index.html" {
            if { $static::DEBUG } { log local0. "Request for index. Responding with content: [class element -value 0 maint_index_html_class]" }
            HTTP::respond 200 content [class element -value 0 maint_index_html_class] "Content-Type" "text/html"
         "/*.png" {
            # NOTE: '17' is derived by the length of /maintenancepage/
            if { [class match [findstr [string tolower [HTTP::path]] "/" 17] equals images_class] } {
               if { $static::DEBUG } { log local0. "Request for [findstr [HTTP::path] "/" 17] found. Responding with binary content" }
               HTTP::respond 200 content [b64decode [class lookup [findstr [HTTP::path] "/" 17] images_class]] "Content-Type" "image/png"
            } else {
               if { $static::DEBUG } { log local0. "Request for [findstr [HTTP::path] "/" 1] NOT found.  Ignoring..." }
         default {
            if { $static::DEBUG } { log local0. "Unrecognized request to URI: [HTTP::uri]" }
            HTTP::respond 404 content "Unrecognized request to [HTTP::uri]" "Content-Type" "text/html"
   # If the all members in the default pool are down, redirect to the maintenance page
   if { [active_members [LB::server pool]] < 1 } {
      if { $static::DEBUG } { log local0. "All Pool Members down: Redirecting to $maint_prefix/index.html" }
      HTTP::redirect "$maint_prefix/index.html"

virtual servertest {
   pool http_pool
   ip protocol tcp
   rule webserver

Contents of the class files


"key" := "<html><head><title>Maintenance page</title></head><body><img src=\"logo.png\"><p><h2>Sorry! This site is down for maintenance.</h2></body></html>",

/var/class/images.class (all one line in reality, split here for the sake of readability. Note the quotes & trailing comma are REQUIRED)