The iRules 101 series has existed on DevCentral for years, and has long been one of the more popular sets of articles that we have had on the site. As such, we have been hesitant to do anything to “rock the boat”, as it were. However, given that that series is now several years old, and the technology driving iRules and in fact F5 gear in general has evolved greatly in that time, we’re now re-visiting this series to get new and experienced iRules users on track from the basics all the way through some of the core concepts. We’ll cover topics ranging from this first installment, which includes some programming basics and concepts, up through F5 terminology and concepts, iRules basics and usage, and eventually some more advanced concepts. This primer is low level, intended for those that might be looking to connect some core concepts of programming and how they interact, as well as a refresher for those that haven't spent much time in the trenches scripting.
In this first installment of the series the idea is to discuss programming at more of a general, basic level. Think of it as a sort of “What you need to know” style introduction. For many this will be extremely low level review, so feel free to skip ahead in the series. For those that may be truly new to programming as a whole or feel that you’re rusty enough that you could use a “from the beginning” refresher, the concepts detailed here are important and hopefully will be useful information. They’re certainly concepts that are important to understand if you hope to begin your journey into iRules Fu greatness. So what do you need to know? Let’s get started.
A Programmer’s Glossary
First let’s talk about some very core programming concepts. It’s important that as the series goes on the basic terms and concepts are understood, so think of this as sort of a getting started glossary. The basic things that you need to know at least a bit about to describe “programming” as a concept are:
We’ll take each of these in turn and give a very brief overview.
The concept of commands is an exceedingly simple one, for the most part. Each programming language has an array of many different commands, which can be executed to achieve different desired results. Host name lookups, spawning child processes, accessing the shell to run a command, storing information in memory, directing traffic, or any number of other things are achieved by a command, or a series of commands called from within the code in question. At the core the commands in any code are the part of the code that actually “does something”. The rest is there to manipulate information, create logical support and flow, etc. Without the commands to actually perform an action, there would be no effect when any script was run, despite how pretty the logic and code was.
For a basic look at how you'd call some commands programatically in a few different languages, see below:
Variables are integral parts of any programming language as they are used to store information in memory. By storing information in a variable you are then able to later recall it later, manipulate that information, pass it to a given command, etc. For instance: If I have code that performs a host lookup to retrieve an IP address, I could execute commands on the result of that and things would work just fine. What happens the next time I want to execute commands against the resulting IP address? My code has to call the operation that did the lookup again. What if, on top of multiple iterations, there were also multiple actions involved? Perhaps I want to compare the IP destination of a given host to value, then based on the result execute a command using that host’s IP. Without storing the result from the host lookup I’m now looking at two lookups every time I execute my snippet of code. By storing the resulting IP in a variable I would be able to perform a single lookup and use the variable for as many iterations and operations as I want without having to perform another lookup every time. Variables are also essential for many things such as conditional testing, string manipulation and more, but more on that later.
You can see a basic example of a few different languages and the way they handle simple variables below:
Conditional statements are a type of control structure in programming that allows you to create a logical flow within your code. For instance if you want to perform a given action every time your script is run, you’d just execute the relevant command. If, however, you only want to run a specific command under particular circumstances, such as only redirecting traffic to a particular server if the primary server is down for instance, you need a way to logically state that. The most basic conditional statement, the if conditional, allows you to do things like dictate that a command or set of code only be executed if a given condition is met, hence the term “conditional”. For instance “look up the IP of this hostname but only if no one has requested it before”.
Again, to see what basic conditional constructs look like, we go to a simple chart of a few relatively standard languages:
Operators - There are two main types of operators that we’ll cover here, logical and comparison.
With commands, variables and conditionals you’ve got a pretty strong skeleton of basic programming functionality, but a very important piece is missing. Comparison operators are an integral part of any language as they are what allow you to evaluate and compare values against one another. What good is a conditional statement saying “if x = 1 do y” without the equals? Standard comparison operators are things we’ve all seen since early math classes. =, >=, <=, != are among the commonly used. There are, of course, others however. Things like regular expression based matching, substring matching and more are all available in most languages. The general concept of a comparison operator is simple though, at its core it is designed to compare one value against another. Whether that is the result of a command being compared against a static value to see if they are equal or two variables being compared to see which one has a greater numeric value, it’s all thanks to comparison operators.
The other major type of operator that we’re concerned about in this series is the logical operator. Logical operators are used to build logical expressions which can be useful in conjunction with conditionals to create a slightly more complex logical flow in your code. For instance a standard conditional might say “if x = y … do something”. But what if you want to do that same thing if x is equal to either y or z? Well, you could either write the same code block twice, once for the case of “x=y” and again for “x=z”, but not only would that be wildly inefficient, it would cause all sorts of problems down the road. A simple logical or can simplify this immensely. Simply changing the conditional statement to “if x = y or if x = z … do something” will get the result you’re looking for without the need to repeat your logic and without some of the major pitfalls that making such blatant repetitions can cause if you’re not careful.
That should serve as a very rudimentary glossary of terms for just about any programming language out there. Every language has its own nuances and intricacies of course. The language on which iRules is based is Tcl (Tool Command Language). With it Tcl brings its own unique traits in syntax as well as functionality.
Tcl Specifics – The What and Why
There were many choices when building iRules as to which language would be best suited for the purpose. There are many reasons that Tcl was chosen, but chief among them are performance, customization and ease of use. While it may not be the most robust language when comparing raw, base functionality to others, Tcl serves the purposes of iRules quite well.
Performance was a massive factor when considering a language on which to base iRules. Tcl is lightweight, compact and highly performant. In an environment where each “script” may be executed hundreds of thousands of times per second, performance is absolutely paramount. The ability to be compiled ahead of run time is a massive win in the battle for performance. Unlike many interpreted languages Tcl does not have to be interpreted at run time, but rather it can be pre-compiled and run in an extremely efficient manner, which allows for exceptionally high processing rates compared to many similar languages. The efficient and easy to use C API within Tcl is also a major boon as most of the commands built into iRules are custom built. Many of the calls made from iRules make use of this API.
Because of this, the customization capabilities of a language had to be taken into account as well when considering languages. Tcl is easily modified and amended to fit our purposes. As the majority of commands that make it into iRules are in fact custom, this is a necessity that Tcl provides well. No language was as network aware as required for our purposes and adding custom commands is the way in which we were able to shape a language to fit the needs of the ADC role, blending network and application functionality, that iRules plays so well.
To that end, any language also needed to be easy to use to properly fit the bill for iRules. Tcl is simple to read, quick to learn and easy to troubleshoot even when passing from person to person, at least compared to some of the more complex options. This is especially important given the nature of the code often written in iRules (somewhat simple, quickly produced code when compared to a product release cycle).
For the complete story on the why of Tcl you can read more in depth here. For now though, this should hopefully serve as a brief introduction into what benefits Tcl itself brings into the iRules picture.