Wednesday, September 29, 2010

TCL Scripting in IOS. Easy to understand Tutorial !

Hi all. Here i am with yet another tutorial. Programming in IOS. Dont confuse it with programming IOS because you cant do it. IOS is not open source so you cant make extensions/modifications to it, But IOS allows you to use TCL (pronounced "tickle") scripting language to write scripts to get tasks done in a different and more feasible way, that was either not possible before or too complicated. One example is the recent query posted by our fellow member superr about how to monitor snmp OID to trigger events. Although the solution was in EEM, but the same could be done using tcl.

Those who are totally new to this stuff will be getting confused as to what i am talking about. So lets see a few examples, that are very practical and you may encounter tasks similar to these more often.

Example 1)

When router cpu utilization reaches 30%, send an email to me, plus give only the first five top processes in "sh process cpu sorted".

Example 2)
(this is a case of ARP flooding/looping which causes the router to nearly crash)

If router cpu utilization reaches above 80%, check the current processes to see if the top 2 processes contains ARP-Input, if this is the case, check the show int to see which interface is getting lots of broadcast. Find the interface and shut it down, Wait for 3 mins and recheck the process level to see if its ok (below 80%), if yes then check the WAN link to reach the email server, if the wan link is reachable, send an email to notify the support engineer that this event has occurred !!

(dont get your head spinning, there's a lot more to follow ;-))

Example 3)

You have 200+ routers in your access. You want to make sure that every WAN interface is configured with IPsec VPN. Considering 2 WAN links per router means checking of 400 interfaces, can you do that by
1) manually opening the router and viewing the running config ?
2) manually opening the backup config stored on your pc and viewing it ?
3) Or simply open the router, paste the tcl script, that will do all the checking, in the end telling you which interfaces are not vpn enabled and optionally enabling the IPSEC vpn on them !!

Depending on your views, these example might be appealing and might not. But if you are network administrator of 100+ routers, then i consider scripting/EEM to be a must-know knowledge, SIMPLE !!. There is no option, you must learn it. It gives you more control and chances of your completing complicated tasks increase dramatically.

I would recommend the following books to learn more about tcl and how to program.

Tcl and Tk Programming for the Absolute Beginner

Hello World!: Computer Programming for Kids and Other Beginners by Warren Sande and Carter Sande (its for python)

But remember, i have spend 4 years in my BS doing programming, and no book can teach you how to program. They will at best simply teach the syntax of the programming language but wont teach you, how to solve your problems using programming language. So you need to do a lot of practice in order to become a good programmer. Remember, scriping in IOS doesnt require or expects that you start building robotic arms, or robots that speak by themselves, no automatic cars are required, NOTHING !!!!!.

Q) So what the hell i will be doing ?
A) Trust me or not, most of the time, you will be required to do string parsing, extracting your desired result, comparing it with some predefined value and doing some action. Thats it !!

Q) Is it really that simple, or you think i am a dumbo ?
A) It is !!

Q) I dont trust you, na na....
A) Then just read on, i hope i will convince you on the way :-)

Q) What if i am not convinced ?
A) You will, trust me on that ;-)

NOTE: this entire tutorial will be very very specific to scripting in IOS. Remember that.

Some of the programming essentials !

Programming is same as scripting. I may use the word program and script interchangebly, so dont get confused, they are the same thing.

Variables.

Variables are fundamental in every programming language. You will never see a language without variables, and perhaps not a single proper program written without using variables. NEVER !!

Variables are simply places to hold values. Consider this

myfirstvar = 4

over here, the name of variable is "myfirstvar" and the value of this variable is 4. simple :-).

Q) Hey hey, what simple ? whats simple about this ? when and why i will be using variables ?
A) You will use variables under two conditions

1) When you are taking input from the user
2) When you need to use a same value more then "1" time in your program.

User input must be kept somewhere, so for that purpose its necassary to use variables or more properly storage areas.

Now before going into detail, lets see tcl in your router. login to your router and type tclsh from exec mode.

Printing on screen.

R1# tclsh
R1(tcl)# puts "hi, i am in tcl :-)"
hi, i am in tcl :-)

R1(tcl)#

"puts" keyword simply displays what ever is given under inverted commas on screen.

Variables.

R1(tcl)# set myfirstvar 10
10

R1(tcl)#

Over here, "set" is use to assigned a variable "myfirstvar" some value, which in this case is number "4". Now to print the variable do this,

R1(tcl)# puts myfirstvar
myfirstvar

R1(tcl)#

Now this was not i expected. the variable "myfirstvar" is simply a place to store the values. So if you have a locker in bank, then the stuff in that locker is important and not the locker. So variable is the locker, the value is the stuff you place in that locker. So to get the value inside myfirstvar, you have to put the dollar sign in front of it..

R1(tcl)# puts $myfirstvar
10

R1(tcl)#

Now lets create 2 variables, assign them numbers and add them. To do mathematical calculations, tcl uses keyword "expr"

R1(tcl)#set var1 100
100
R1(tcl)#set var2 200
200
R1(tcl)#set var3 [expr $var1 + $var2]
300
R1(tcl)#puts $var3
300

R1(tcl)#

over here "[expr $var1 + $var2]", $var1 says to tcl, that use the value kept in var1 which is 100. $var2 tells to use the value kept in var2 which is 200. expr tells tcl to perform the addition (or whatever like subtraction, multiplication, division, exponentiation). "[]" square brackets will be replaced with whatever value is calculated inside them, so the above expression is more like this

R1(tcl) set var3 300

[expr $var1 + $var2] gives value 300, which will be replaced with it and appears like above statement. We will be using square brackets alot so dont be confused.

In Tcl, you can execute IOS commands as well using following 2 keywords

1) exec -> to run privilege mode commands
2) ios_config -> to run all config mode commands.

Using Exec command

R1(tcl)#exec sh ip int brief
Interface IP-Address OK? Method Status Prot
ocol
Serial1/0 unassigned YES unset administratively down down

Serial1/1 unassigned YES unset administratively down down

Serial1/2 unassigned YES unset administratively down down

Serial1/3 unassigned YES unset administratively down down

R1(tcl)#

Using ios_config command

R1(tcl)# ios_config "int serial 1/0" "ip address 11.0.0.1 255.255.255.252" "no shut"

R1(tcl)#exec show ip int brief
Interface IP-Address OK? Method Status Prot
ocol
Serial1/0 11.0.0.1 YES unset up up

Serial1/1 unassigned YES unset administratively down down

Serial1/2 unassigned YES unset administratively down down

Serial1/3 unassigned YES unset administratively down down

R1(tcl)#

Note: While in tcl mode, you can run IOS commands directly without these keywords, but as we go deeper, you will understand why these keywords make a difference.

Now lets create a simple tcl program.

Program 1)

Create a tcl program to calculate the DRAM on your router.

Now lets see how to write a program.

1) First solve it on paper and follow the steps carefully, observe deeply how exactly did you solved this issue.

I would solve it like this

First i will do "show version"

R1#sh ver
Cisco IOS Software, 3600 Software (C3640-JS-M), Version 12.4(12), RELEASE SOFTWA
RE (fc1)
Technical Support: http://www.cisco.com/techsupport
Copyright © 1986-2006 by Cisco Systems, Inc.
Compiled Fri 17-Nov-06 13:59 by prod_rel_team

ROM: ROMMON Emulation Microcode
ROM: 3600 Software (C3640-JS-M), Version 12.4(12), RELEASE SOFTWARE (fc1)

R1 uptime is 1 hour, 21 minutes
System returned to ROM by unknown reload cause - suspect boot_data[BOOT_COUNT] 0
x0, BOOT_COUNT 0, BOOTDATA 19
System image file is "tftp://255.255.255.255/unknown"

Cisco 3640 (R4700) processor (revision 0xFF) with 116736K/6144K bytes of memory.
Processor board ID 00000000
R4700 CPU at 100MHz, Implementation 33, Rev 1.2
4 Serial interfaces
DRAM configuration is 64 bits wide with parity enabled.
125K bytes of NVRAM.
8192K bytes of processor board System flash (Read/Write)

Configuration register is 0x2142
--More--

I will locate the line in bold. then i will add both values i.e. 116736 and 6144 and to convert it to MB, divide it by 1024, which gives me the value of 120.

(i am using dynamips and have given exactly 120 ram, so this calculation producess perfect result :-) ).

Now remember, machines are simply dumb, you need to tell them each and everything to solve the issue, because whether you realize it or not, you also require all the pieces together in order to solve the issue. You can sure make some assumptions, but that assumption is usually not completely random, its always based on some prior knowledge. But in case of computers, you have to tell them everything. Now lets break the above solution to MOST SIMPLISTIC STEPS.

1) Run the command show version
2) locate the line which contains the value i.e. 116737k/6144
3) Add both the values before and after "/"
4) Divide them by 1024
5) Display the result

Now as you can see, these steps cant be further broken down. Now lets solve them 1 by 1,

1) set version [exec show version]


Now for second step, we have to go a bit more DEEPER. Here starts the most important topic

PARSING THE GIVEN INPUT FOR A SPECIFIC STRING.

Q) whats a string ?
A) String is the collection of characters. Everything you type whether a number or a letter is essentially a character. So what ever we type, if it contains more then 1 character, it will become string. So this whole tutorial can be thought of a very large string.

Q) what do you mean by parsing the string ?
A) Parsing the string means, going through the string looking for something. Like in our case, i will parse (look through) show version output to locate the line containing the DRAM values.

So how to search in a given string ?. There are usually 2 methods that i use

1) Taking the input, splitting it till i get to the value i want.
2) Using regular expression.

I wont be using method 1 till i have no other way. In start i used it alot but you will realize as i did, its better to get comfortable with regular
expressions. They are fast and easy way to search for a given string.

CCNP guys must have played with BGP and surely regexp. Those who havent, you must do it. I will be giving a brief tutorial on how to use regexp.

Q) What are regular expression ?
A) They are more general/dynamic way of respresenting a given string.

Q) Oh yeah !!! i know i am being dumb but it actually went over my head you know !
A) Ok so just read on.

Lets consider the essentials of regular expression

. means anything. Including space, newline, anything
* means zero or more occurence of the character preceding this.
+ means one or more occurence of the character preceding it.
- defines the range
[] encloses the range

Q) ..... WHAT ??? i have seen regexp and they are lot more, you are cheating !!!!!!
A) Explanining regexp is not a part of this tutorial. I will be using a simplified approach to use regular expressions.

Lets practice it a bit

suppose i am giving you the following string

Hi, my height is 100 foot 11 inches. My color is fair, i have bright blue eyes and shiny hairs. Yesterday i had fever measuring 100. WOW !!

Q) Are you talking about yourself ?
A) Ofcourse not...

Now using regular expression, EXTRACT the fever value.

Now, focus the surrounding of the value you need to extract. "Yesterday i had fever measuring 100. WOW !!" and ALSO "Hi, my height is 100 foot 11 inches"

We need the second 100 value thats after word "measuring". So in my opinion, i will tell the regexp to get me the value that is after "measuring ". Rest is all garbage right ?

So in regexp, you must define what is garbage for you and what part is important. In our case, all what is before "measuring" is garbage, and all after 100 is garbage too. Garbage in regexp is denoted by ".*"

. means anything and * means zero or more times. So it becomes

anything (zero or more)times
. *

so our partial regular exp becomes

.* measuring

keep in mind, that we want to write a general expression so that it will give us any value that comes after measuring. Since this value (fever) is expected to change (since i am telling you !!), you have to generalize this using regexp.

Fever value will always be a number. its length would be 2 - 3 digits ( am i right ?) )

so regular expression for fever value might be

[0-9][0-9][0-9] TADAAAAAAAAAAAAAAAAAAAAA!!!!

Nope its wrong. This may evaluate too

First[0-9] = 1
Second[0-9] = 0
Third[0-9] = 0

This is right but not 100%. Fever values are usually like 98,99,100,102,104 etc.

So the more appropriate regexp would be

[0-9]+

Now, this says that number from 0-9 can appear more then once. Strictly speaking, it satisfies our need. But what if a value is given with 4 digits ?, or extremely high fever like 300 which is not possible, so let me give you a relief that, we wont be going into that much complication. AS long as we are able to extract our needed value out of given input, we are fine.

So the complete regular expression becomes

".*measuring [0-9]+\."

Q) Why have you written "\." ?
A) As you know that "." is the regexp itself. I wanted to write period as it was appearing in statement, so for this purpose i add "\" telling the regexp that period is the literal value and not a regexp. Same is true for other regexp such as +,* etc. When you need to specify them as literals, just place a "\" before them.

Now the syntax for tcl is as follows

first lets assign this whole statement to a variable.

R1(tcl)#set var1 "Hi, my height is 100 foot 11 inches. My color is fair, i hav$
Hi, my height is 100 foot 11 inches. My color is fair, i have bright blue eyes a
nd shiny hairs. Yesterday i had fever measuring 100. WOW !!
R1(tcl)#
R1(tcl)# regexp {.*measuring ([0-9]+)\.} $var1 complete_string fever_value
1 <- 1 means match was found. 0 means match wasnt found

R1(tcl)# puts $fever_value
100

R1(tcl)#

Now lets see the syntax. Regular expression is written after regexp keyword and in curly braces. The value you need to actually extract is placed with round brackets within those curly braces. Then you place the variable containing the string that will be evaluated by the regexp followed by yet 2 more variables. The first variable will contain the complete string (which is useless to us) and second variable contains the value that will be extracted by the round brackets, which in our case will be value 100 as show in above example.

Now lets get back to our example of extracting DRAM values from the entire show version string.

First just pick up the line contaning the values.

"Cisco 3640 (R4700) processor (revision 0xFF) with 116736K/6144K bytes of memory."

Now if you have multiple platform, then the first value "Cisco 3640" is likely to change. Just run sh version on couple of routers and see which part of this line always remain constant. In my view it was the last 3 words " bytes of memory". You can verify this by looking at the entire ouput and making sure that these 3 words dont appear anywhere else, thus making this line unique.

Now first try to write your own regexp for this.

.
.
.
.
.
.
.
.
.

Well, mine is

".*([0-9]+)K/([0-9]+)K bytes of memory."

R1(tcl)#regexp {.*with ([0-9]+)K/([0-9]+)K bytes of memory.} $version complete_string first_val second_val
1
R1(tcl)#puts $first_val
116736

R1(tcl)#puts $second_val
6144

R1(tcl)#

Now add them and divide them with 1024 to get the final result

R1(tcl)#puts "the DRAM is [expr ($first_val + $second_val) / 1024]"
the DRAM is 120

R1(tcl)#

If you want to test this before hand, paste the following lines once you are in privilege mode

---------------------------------------------------
tclsh

set version [exec show version]

regexp {.*with ([0-9]+)K/([0-9])+K bytes of memory.} $version complete_string first_val second_val

puts "the DRAM is [expr ($first_val + $second_val) / 1024]"

Check to see, if you get the correct output or not :-).


---------------------------------------------------

Now, instead of going any further, just practice the above, by extracting different strings from different inputs. Like for example, extract the following from the show version output

1) IOS name
2) Model name
3) How much Flash
4) Configuration register value.


Trust me, it just needs a practice of 1 or 2 days. We will move on once you are bit comfortable with regexp, since we will be using them alot. There would be rarely a script that will be written without them.

This is just the start. These are not TCL based EEM scripts but they use similar logic. I will be writing more if you guys are getting what i am explaining here. Just give it a little effort and you will never regret it.

Let me know of your feedback so i may write more. Plus, in your feedback, provide the regexp (complete scripts) for extracting all the 4 values i mentioned so i will also tell me that you actually learned something from this tutorial :-)

No comments:

Post a Comment