OK, looks like I've managed to get a workable solution in place...
Below is the code that I've created. Thought I'd post it here for comment before submitting to Code Share.
Cheers
Gavin
when RULE_INIT {
set static::SUBTABLE "maintenance_window"
set static::debug 1
Global variable to use within other iRules to prevent multiple invocation errors.
set ::MAINTENANCE_MODE 0
}
when HTTP_REQUEST priority 10 {
set VS_NAME [virtual name]
set TITLE "[substr $VS_NAME 8] iRule Maintenance Window Control"
set PROFILE [substr $VS_NAME 8]
set PROFILE_NAME [concat [string map {. _} $PROFILE]_maintenance_window]
if {$static::debug == 1} { log local0.info "VS Name: $VS_NAME Title: $TITLE Profile: $PROFILE Profile Name: $PROFILE_NAME" }
Look for embedded commands to control the maintenance window.
switch -glob [string tolower [HTTP::uri]] {
"/enable*" {
Extract the maintenance window day, start date time, and duration from the URI.
If not there, return an error message.
set params [split [HTTP::uri] "/"]
if { [llength $params] < 4 } {
HTTP::respond 200 content "
$TITLE
Usage: http://[HTTP::host]/enable/start_date_time/duration
Start Date Time must be of the format yyyy-mm-dd hh24:mi:ss
Duration is in Hours
"
} else {
Assign the values to variables.
set START_VALUE [string map {"%20" " "} [lindex $params 2] ]
set DURATION_HOURS [lindex $params 3]
if {$static::debug == 1} { log local0.info "Start Value = $START_VALUE, Duration = $DURATION_HOURS." }
Process input
set START_EPOCH [clock scan $START_VALUE ]
if {$static::debug == 1} { log local0.info "Start EPOCH = $START_EPOCH" }
set DURATION_SECS [expr {$DURATION_HOURS*60*60}]
if {$static::debug == 1} { log local0.info "Duration = $DURATION_SECS" }
Add value to subtables
table set -subtable $static::SUBTABLE $PROFILE_NAME $START_EPOCH 0 $DURATION_SECS
HTTP::respond 200 content "
$TITLE
Maintenance Window enabled
"
}
}
"/disable" {
By removing the session table entries, the maintenance is removed.
if {$static::debug == 1} { log local0.info "Removing set Maintenance window..." }
table delete -subtable $static::SUBTABLE $PROFILE_NAME
HTTP::respond 200 content "
$TITLE
Maintenance Window disabled
"
}
"/check" {
Return the current time and the current settings for the maintenance window
set START_EPOCH [table lookup -subtable $static::SUBTABLE $PROFILE_NAME]
Set Common variables.
set NOW_EPOCH [clock seconds]
set NOW_DTS [clock format $NOW_EPOCH ]
if { $START_EPOCH != "" } {
set START_DTS [clock format $START_EPOCH]
set DURATION [table lifetime -subtable $static::SUBTABLE $PROFILE_NAME]
set DIFFERENCE [expr { $NOW_EPOCH - $START_EPOCH } ]
set RESPONSE "$START_DTS for [expr {$DURATION/60/60}] hours"
Debug Logging
if {$static::debug == 1} {
log local0.info "Maintenance window set..."
log local0.info "Table values: Start EPOCH - $START_EPOCH Duration - $DURATION"
log local0.info "Start DTS = $START_DTS Date Time now = $NOW_EPOCH"
log local0.info "Difference = $DIFFERENCE"
}
} else {
set START_DTS "No Maintenance Window set"
set RESPONSE $START_DTS
Debug Logging
if {$static::debug == 1} { log local0.info "No Maintenance Window set..." }
}
HTTP::respond 200 content "
$TITLE
Current Date and time: $NOW_DTS
Maintenance Window: $RESPONSE
"
}
"/usage" {
Usage instructions for the commands
HTTP::respond 200 content "
$TITLE
Usage: http://[HTTP::host]/\[command\]
/enable/start_date_time(yyyy-mm-dd hh24:mm:ss)/duration(h) - enable maintenance window.
/disable - disable maintenance window.
/check - check if in maintenance window.
/usage - display this usage message
"
}
default {
no secret command entered, so check the maintenance window
Get the values from the statistics profile.
set START_EPOCH [table lookup -subtable $static::SUBTABLE $PROFILE_NAME]
set DURATION [table lifetime -subtable $static::SUBTABLE $PROFILE_NAME]
Debug logging
if {$static::debug == 1} { log local0.info "Table values: Start EPOCH - $START_EPOCH Duration - $DURATION" }
Check if start_epoch is non-null.
if { $START_EPOCH != "" } {
Use the TCL "clock" command to get the current time as epoch.
set NOW_EPOCH [clock seconds]
if {$static::debug == 1} { log local0.info "Current EPOCH Time = $NOW_EPOCH" }
Check NOW against Scheduled start
if { $NOW_EPOCH < $START_EPOCH } {
Not yet in maintenance, therefore return for other processing
return
} else {
Now must be greater than Start Epoch value, therefore in maintenance or after maintenance
set DIFFERENCE [expr { $NOW_EPOCH - $START_EPOCH } ]
if {$static::debug == 1} { log local0.info "Difference between Now and Start Epoch is $DIFFERENCE" }
if { $DIFFERENCE < $DURATION } {
Within maintenance window
if {$static::debug == 1} { log local0.info "In Maintenance window - Returning holding page." }
if { [HTTP::uri] ends_with "logo-act.png" } {
if {$static::debug == 1} { log local0.info "Returning Logo..." }
HTTP::respond 200 content [b64decode [class element -value 0 act_logo_png]] "Content-Type" "image/png"
} else {
if {$static::debug == 1} { log local0.info "Returning html..." }
HTTP::respond 200 content [class element -value 0 offline_page_html] "Content-Type" "text/html"
}
Set Maintenance mode global variable.
set ::MAINTENANCE_MODE 1
} elseif { $DIFFERENCE > $DURATION } {
Outside of Maintenance window
if {$static::debug == 1} { log local0.info "Outside of Maintenance window. Unsetting Maintenance Mode." }
Unset maintenance mode flag for good measure
set ::MAINTENANCE_MODE 0
Remove session table entry.
table delete -subtable $static::SUBTABLE $PROFILE_NAME
}
}
} else {
Not in maintenance window, so allow connection to continue to application.
if {$static::debug == 1} { log local0.info "Not In Maintenance window - Continuing." }
Ensure Maintenance Mode global variable is unset.
set ::MAINTENANCE_MODE 0
}
}
}
}