iRule Maintenance Windows
A fun, but not well known, feature on BIG-IP is the Statistics Profile. This tech tip is the second in a series on how the Statistics Profile and iRules, when working together, can save time, productivity, and your sleep!
The Scenario:
Imagine yourself, Mr./Mrs. IT Manager, working for a company that's business relies on updated content on it's back-end web servers. Your company has a web team that has decided that it's test cycle will always end at 3:00AM (your time) on Saturday morning. So, here you are, arriving at home after a night of wild karaoke and one too many drinks with cheap umbrellas and you get a call from the dev team that they need the site taken off line so they can push out the new content.
In the past, you gave the dev lead the keys to the castle (meaning the admin credentials for your BIG-IP) and he/she somehow thought "rm -rf /" was how you took a virtual server offline. After an emergency restore of the software you vowed to never give that department direct access to your servers - ever... So, now you are stuck with being on call whenever they deem updates are needed. If only, you could provide your dev team access to control taking their application offline while they are doing updates. Oh, and they should also be able to view the current status as well as being able to bring it back online.
Never fear, help is on the way! There are a couple of handy features within iRules that make this problem a thing of the past.
The Setup:
To get things going, you'll need a Virtual Server setup fronting your web application. Since your application is up and running, that step is already taken care of. Next, you'll need to create a Statistics Profile, assign it to your Virtual Server and write an iRule. So, first we'll create the statistics profile...
Create the Statistics Profile
- Login to the BIG-IP Administrative GUI.
- Select the Profiles option under Local Traffic and Virtual Servers
- Select the Statistics from the Other menu.
- Click the Create button
- Enter "maintenance_window" for the Profile name
- Add the following fields: day, start_time, end_time
- Click Update to create the profile
Create the iRule
Now that the virtual server is setup with the statistics profile, you'll need to create an iRule to to add the controlling functions as well as the maintenance window enforcement.
- Select Rules from the Local Traffic/Virtual Servers menu.
- Click the Create button.
- Enter "maintenance_window" for the iRule name and enter the iRule from below into the Definition text box.
- Click Finished to save the iRule
Apply the statistics profile to your virtual server
The statistics profile doesn't do much until you apply it to your virtual server properties. Here's how:
- Select Virtual Servers from the Local Traffic menu.
- Click on your Virtual Server to enter it's properties
- Make sure the Advanced Configuration option is selected, and scroll down to the Statistics Profile option
- Select the previously created profile maintenance_window
- Click the Update Button
- Select the Resources Menu
- Click the Manage Rules button
- Make sure the maintenance_window iRule is in the Enabled list box and click the Finished button.
The iRule
Now for the fun, here's the iRule that will give you peace, tranquility (and a good nights sleep). This iRule will look for commands in the URI to control the maintenance window settings (see the "/usage" section for details).
when HTTP_REQUEST { set TITLE "iRule Maintenance Window Control" set PROFILE_NAME "maintenance_window" # Look for embedded commands to control the maintenance window. switch -glob [string tolower [HTTP::uri]] { "/enable*" { # Extract the maintenance window day, start time, and end time from the URI. # If not there, return an error message. set params [split [HTTP::uri] "/"] if { [llength $params] < 5 } { HTTP::respond 200 content " <head><center><title>$TITLE</title> <body><h1>Usage: http://[HTTP::host]/enable/day_number/start_hour/end_hour</h1> </body></html>" } else { # Update the maintenance window values in the statistics profile. STATS::set $PROFILE_NAME "day" [lindex $params 2] STATS::set $PROFILE_NAME "start_time" [lindex $params 3] STATS::set $PROFILE_NAME "end_time" [lindex $params 4] HTTP::respond 200 content " <head><center><title>$TITLE</title> <body><h1>Maintenance Window enabled</h1> </body></html>" } } "/disable" { # By zero'ing out the values in the statistics profile, the maintenance window is not enforced. STATS::set $PROFILE_NAME "day" 0 STATS::set $PROFILE_NAME "start_time" 0 STATS::set $PROFILE_NAME "end_time" 0 HTTP::respond 200 content " <head><center><title>$TITLE</title> <body><h1>Maintenance Window disabled</h1> </body></html>" } "/check" { # Return the current time and the current settings for the maintenance window set day [STATS::get $PROFILE_NAME "day"] set start_time [STATS::get $PROFILE_NAME "start_time"] set end_time [STATS::get $PROFILE_NAME "end_time"] set l [split [clock format [clock seconds] -format {%u %H %M}] " "] set cur_day [lindex $l 0] set cur_time [expr [expr [lindex $l 1]*100] + [lindex $l 2]] HTTP::respond 200 content " <head><center><title>$TITLE</title> <body><h1>Current Date and time: $cur_day, $cur_time</h1> <h1>Maintenance Window: $day, $start_time - $end_time</h1> </body></html>" } "/usage" { # Usage instructions for the commands HTTP::respond 200 content " <head><center><title>$TITLE</title> <body><h1>Usage: http://[HTTP::host]/\[command\]</h1> <table><tr><td><ul> <li>/enable/day_of_week_number(1:mon-7:sun)/start_time(hhmm)/end_time(hhmm) - enable maintenance window.</li> <li>/disable - disable maintenance window.</li> <li>/check - check if in maintenance window.</li> <li>/usage - display this usage message</li> </ul></td></tr></table> </body></html>" } default { # no secret command entered, so check the maintenance window # Get the values from the statistics profile. set day [STATS::get $PROFILE_NAME "day"] set start_time [STATS::get $PROFILE_NAME "start_time"] set end_time [STATS::get $PROFILE_NAME "end_time"] if { ($day ne 0) and ($start_time ne 0) and ($end_time ne 0) } { # Use the TCL "clock" command to get the current time settings. set l [split [clock format [clock seconds] -format {%u %H %M}] " "] set cur_day [lindex $l 0] # Math right here is a bit overkill. I've changed it to string concatenation. #set cur_time [expr [expr [lindex $l 1]*100] + [lindex $l 2]] set cur_time "[lindex $l 1][lindex $l 2]" if { ($cur_day eq $day) && ($cur_time >= $start_time) && ($cur_time <= $end_time) } { # The current time is in the maintenance window, so either issue a HTTP::redirect here to a # prettied up page, or you can do as I've done here, and return a HTML response direction # to the client. HTTP::respond 200 content " <head><center><title>$TITLE</title> <body><h1>This site is down for maintenance, please come back at $end_time ...</h1> </body></html>" } else { # Not in maintenance window, so allow connection to continue to application. } } } } }
Conclusion:
So, now you should be all set. While your dev team now has control over their site's availability and the 3:00am calls are a thing of the past...
- DeVon_JarvisNimbostratusHere is what I found out. The octal concern is not an issue any more, but comparing anything to 0800 (leading zero) seems to do a string compare, which leads to incorrect results.
- jdspring_42045NimbostratusWould love to see the final product. Ours also does validation against the keepalives so that once keepalives < 1 it goes to maintenance too. The final version could be used to give user maintenance control and also prevent the "ugly" page cannot be found when all keepalives are down.
- How is it that string comparisons will lead to incorrect results. a string compare for 0800 will be less than 0900 and 1430 is greater then 0600. Can you provide an example of two times in HHMM format that will not work for a string comparison as opposed to a math comparison?
- DeVon_JarvisNimbostratusIn my "modified" version, I had dropped the leading zeros to force numeric comparisons, thinking these would be faster. I have added back the leading zeros and you are right, string comparisons work just fine.
- Jatt_Dill_40963NimbostratusIs there any way we can disable this rule by default and enable when required? Looking for some line code in irule
- DM_5174NimbostratusI have tried this and it does not work. Is there a way to remotely enable and disable an IRULE from the browser or via a script? This way, the normal IRULE can be replaced by the one that will be used during a maintenance window without having to login to the LTM. Other team member not part of the network group can do this. I think It's kinda dangerous to have members logging into a network device when they have no clue as to how it works.
- You could pretty easily write an iControl application to add/remove an iRule from a virtual server's list of resources. Name your language of choice and I can whip something up for you. Perl (script), PowerShell (Script), Java (bytecode), .Net (compiled .exe). Pick your poison and I can get you some code.
- Looks like I already wrote a perl app to do just that. It will list, add, and remove iRules from a specified Virtual Server. Check out PerlVirtualServerRule in the iControl CodeShare:
- Brian_69413NimbostratusI am having trouble with your code in that the STATS profile fields do not seem to hold the values set in them or rather are not consistent. I have added two log statements, one for when /disable is called and I dump the STATS fields, and the second when I run /check. You will see that multiple hits to /check show different STATS field values:
- ASAT_6562NimbostratusI have had trouble getting this to work correctly. Even when the Octal number issue is resolved, the evaluation of current_time fails when the time real current time is between ));24 and 00:59. Using set cur_time [clock format [clock scan now] -format %H:%M] solved the issue for us and this is now happy in our production environment and works as expected.