Forum Discussion
Chris_Day_10331
Nimbostratus
Dec 27, 2005iRules for Individual Testing of Farm Servers
Hello wonderful people at DevCentral!
I have a follow-up to an earlier post I had made “iRule for Server Testing” (http://devcentral.f5.com/default.aspx/Default.aspx?tabid=28&forumid=5&postid=4603&view=topic) regarding the re-writing of URLs to facilitate testing of individual pool members for a particular application.
I was not successful in getting this to work, primarily because I believe I may have approached the problem from the wrong angle. I wanted to start this from the “top down” to see what all you smart people think is the best approach to achieve the end result.
THE PROBLEM:
We now have 16 web servers (among 16 app servers, db servers, etc) each of which services a number of applications (currently 6 but not likely to exceed 50 to 100). The applications are distinguished from one another only by HTTP/1.1 host header. As a new server is brought into production it is very difficult to ensure that it is responding properly for each of the 6 applications (and some other reasons beyond the scope of this post). As this number grows linearly, it is increasingly difficult and time consuming to test and more importantly locate issues with the deployment of the app to each of the boxes. A sample application served by each of the 16 boxes would be:
http://application1.company.com/
THE GOAL:
I was hoping to make it such that we could provision an additional VS (with either an internal or external IP) and enable testers to test a particular application on a particular server as follows:
http://application1.test.company.com/web001/ OR
http://application1.test.company.com/10.10.20.54/
This would require minimal DNS maintenance as new applications were added since it could be achieved by creating a single wildcard sub-domain (*.test.company.com pointing to VS IP Address). My goal was then to have the BIG-IP parse the “/web001/” portion and determine from a lookup table of some kind that I, the tester, was trying to load “application1.company.com” from server “web001”. In the latter example, it would obviously determine that I wanted to have the specified application served by the server who's IP address I provided.
The critical part was that the server be unaware of all the fancy rewriting going on and believe that the client was actually requesting http://application1.company.com/ (without the “/web001/”). I wanted the URLs back to the client to be re-written with the “/web001/” to enable the site to best tested/navigated seamlessly (i.e. not just the initial request).
As I am not a developer or particularly strong with iRules, I was hoping that someone could pick this up and help. There is no real rush on this, but needless to say the current solution which involves the continuous editing of hosts files is quite ugly.
I really appreciate all the help you guys have provided in the past (I still owe some beers) and thank you in advance for reading this post.
Cheers,
Chris
17 Replies
- JRahm
Admin
if using the server IP address, you could do something like this. Note that this is untested, but should get you started.when HTTP_REQUEST { if { [HTTP::uri] > 1 } { set uri_index [ split [HTTP::uri] "/" ] set myserver [ string trimleft [ lindex $uri_index 0 ] "/" ] set myuri [ lindex $uri_index 1 ] HTTP::uri "/$myuri" use node $myserver } } when HTTP_RESPONSE { if { $myserver != "" } { HTTP::uri "/$myserver[HTTP::uri]" } } - JRahm
Admin
You might add some error checking to make sure the server provided in the URL is actually a server known by the big ip:when HTTP_REQUEST { if { [HTTP::uri] > 1 } { set uri_index [ split [HTTP::uri] "/" ] set myserver [ string trimleft [ lindex $uri_index 0 ] "/" ] set myuri [ lindex $uri_index 1 ] HTTP::uri "/$myuri" if { [catch { use node $myserver } ] } { log "Invalid node $myserver - does not exist" reject return } else { use node $myserver } } } when HTTP_RESPONSE { if { $myserver != "" } { HTTP::uri "/$myserver[HTTP::uri]" } } - Chris_Day_10331
Nimbostratus
Hey, this looks great but how does it deal with the re-writing of the host headers. Previously, I had a chunk that looked something like this:when HTTP_REQUEST { Search for host mapping in the lookup list set new_host [findclass [HTTP::host] $::HostMap_External " "] if { "" ne $new_host } { if mapping is found, replace the Host header log local0. "Replacing host from [HTTP::host] to $new_host" HTTP::header replace "Host" $new_host Pull out the node name as the first element of the uri ie. /web001/foo/bar -> web001 set node_name [lindex [split [HTTP::uri] "/"] 1] if { "" ne $node_name } { Now look for the node address in the lookup list log local0. "Searching for node: $node_name" set node_addr [findclass $node_name $::NodeMap_External " "] if { "" ne $node_addr } { log local0. "Routing to node: $node_addr " node $node_addr } else { log local0. "Didn't find node '$node_name' in lookup class" } } else { log local0. "No node name passed in URI" } } else { log local0. "Didn't find [HTTP::host] in lookup class" } }
It looks to me like your example will help with the FQDN/IP/ example but not the FQDN.TEST/IP/ or FQDN.TEST/NAME/. I appreciate anything you can do - and let me know if there is anything I can get for you as a late xmas gift!
CD - Simon_Knox_1115
Nimbostratus
Hi, we do a similar thing for testing web apps on specific servers but use TCP port numbers to differentiate them:
i.e. http://URL:8001 uses the node for server 1 on port 80
http://URL:8002 uses the node for server 2 on port 80
and so on.
It means that you don't have t do any URL rewriting
See theis thread:
http://devcentral.f5.com/Default.aspx?tabid=28&view=topic&forumid=5&postid=3313
Cheers
Simon - Chris_Day_10331
Nimbostratus
First off -- thanks to everyone whoh participates for the ongoing support...
After implementing hybrids of both of these suggested solutions, I think we have come up with an approach that will work great with our environment. Here is what we would like to do:
- evaluate incoming http/https request and look for a query of "&node=$NODEIP" or just "?node=$NODEIP" as the case may be
- select node that matches $NODEIP from default pool and maintain persistence to that node
- have this rule be applicable to multiple sites - a standard rule that will allow us to evaluate the performance/availability of independent nodes from an external monitoring service
Can anyone help me put this together? I know enough to be able to read the rules but lack the originality/skill to actually create!
Thank you again.
Chris Day, Purveyor of Fine F5 Products - Deb_Allen_18Historic F5 AccountAssuming you want the rest of the traffic to fall into myPool, the following should work for you:
when HTTP_REQUEST { set myNode [findstr [HTTP::uri] "node=" 5 "&"] if {[catch {node $myNode}]}{ pool myPool } else { node $myNode } }
HTH
/deb - Chris_Day_10331
Nimbostratus
Thanks Deb -when HTTP_REQUEST { set myNode [findstr [HTTP::uri] "node=" 5 "&"] if {[catch {use node $myNode}]}{ log "Node not found." pool http-pool-mypool } else { log "Node found." node $myNode } }
I set the iRule up this way, however, it seems that I am not able to get the "Node not found." log event to fire per /var/log/ltm. When I use:
http://vip/?node=10.10.20.51 it works as expected! Any thoughts? - Deb_Allen_18Historic F5 AccountI was assuming that command worked for you in the earlier example. Try this instead, then, adjusting port on the pool member if necessary:
when HTTP_REQUEST { set myNode [findstr [HTTP::uri] "node=" 5 "&"] if {($myNode ne "") \ and ([LB::status pool http-pool-mypool member $myNode:80 eq "up")}{ log "Node found." pool http-pool-mypool member $myNode:80 } else { log "Node not found." pool http-pool-mypool } }
/deb - unRuleY_95363Historic F5 AccountI'd like to re-iterate some notes about the use of the "catch" command:
First, of all, catch actually evaluates the command, so there is no need to evaluate it again in the if-else body. All catch really does, is catch the return code of what it evaluated.
Second, you should check for a return of != 1 which means not an error, instead of just doing a boolean check as there are other possible non-zero values that don't mean an error occured.
Also, the "node" command won't return an error if the node does not exist. It will only return an error if the node provided is not a properly formatted IP address. Instead, if the node does not exist, you will likely get an LB_FAILED event when the connection fails to connect.
Anyway, using Deb's latest "check if the node is a member of the pool" logic provided in the above post is probably much better way to go anyway. Just thought I'd mention these notes about using the "catch" command. - This fixes a few compile errors:
when HTTP_REQUEST { set myNode [findstr [HTTP::uri] "node=" 5 "&"] if {($myNode ne "") \ and ([LB::status pool xpbert-http member $myNode 80] eq "up")}{ log "Node found." pool http-pool-mypool member $myNode:80 } else { log "Node not found." pool http-pool-mypool } }
-Joe
Help guide the future of your DevCentral Community!
What tools do you use to collaborate? (1min - anonymous)Recent Discussions
Related Content
DevCentral Quicklinks
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com
Discover DevCentral Connects