Friday, February 21, 2014

A Mini-Adventure Using Expect to Query Voice Gateway Configurations

Many moons ago, I started a series on the NetCraftsmen blog site covering various "tools" in my UC toolkit . I never did finish that series out and I may pick it back up and carry it forward. I still get asked about it from time to time and a recent query got me thinking about some of the more useful tools in my toolkit: scripting/programming languages.

I don't want to get into the pros/cons of specific languages here. There are just too many options available and I am not fool enough to consider myself an expert on the nuances between programming languages or development environments. I'll leave that to those who live and breath this stuff.

What I want to do with this entry is emphasize the fact that the greatest tool you can add to your toolkit is "ingenuity". Sometimes you need to go outside of the box and create a solution to your problem rather than waiting for Cisco or some other vendor to solve your problem with a bit of software. 

Personally, if I can't find a way to automate a task using existing tools I wonder if I can build it myself. That doesn't always work (trust me) but it works more often than not and I find the process to be a lot of fun. Then again, I am a bit of a nerd and what I find fun usually isn't by most standards. That's cool, too.

Background

The best way to illustrate when and how I decide to script something is to use an example. 
Recently, I  needed a solution for gathering data from 30+ voice gateways in an environment where we were planning a migration to a new UC system release. This migration would involve a parallel build out and a flash cut. To prepare for the flash cut, I needed to modify configurations on the various voice gateways. 

The idea of gathering the data, massaging the config, and then adding the config changes by hand didn't appeal to me. I felt that there was enough work there to warrant an attempt to automate at least some portion of the process. Of course, I was just looking for an excuse to do something more exciting than spending a few hours with my favorite text editor.

So, what did I decide to do? I decided to tinker with Expect and a bit of shell scripting. I am familiar with Expect and I have done minor tweaks to Expect scripts in the past. I have never really built something from scratch. So, it sounded like fun (relatively speaking, of course).

The Environment

The script I am going to lay out here is an Expect script and I am using a bash shell script to start the process. I use OS X as my work machine and one of the benefits is that OS X comes loaded with everything I need to do perl, expect, tcl, etc.. I used to be a Windows user and on that platform, I liked to use the various ActiveState products. I think if you use the Tcl environment, you should be able to get the expect script I discuss below to work. However, you'll probably need to tweak a few things. I used ActiveState's perl environment in the past with great success and found it easy to port perl scripts from *NIX environments to Windows platforms.

The Scripts

So, the scripts I put together aren't going to blow your mind or anything. I am also not going to pump it up with a bunch of intro/supporting text. I figure the best approach is to lay out what I used and let those that are interested play with it. By no means is this revolutionary. You may find it useful or you may not. 

What I am presenting here is just for gathering information. I used the following to create a bunch of text files with information needed to do some manual edits. Then we copied/pasted the new configs into the devices. That approach just fit better with how our customer wanted to do things.

Anyway, the first script is getvoxinfo.exp. This is an expect script. It uses information passed to it to SSH to a voice gateway and execute a series of commands. The commands are determined by the type of gateway we are dealing with.

getvoxinfo.exp

#!/usr/bin/expect -f
# getvoxinfo.exp
# Variables
# $myvgw $myvgip $myvgmodel $myuid $mypwd
# myvgw: used for the output file name and stdout messages
# myvgip: used to establish ssh session
# myvgmodel: used to identify what type of gw and the commands we should sue
# myuid: The user ID to authenticate to the vgw
# mypwd: The user password

set myvgw [lindex $argv 0]
set myvgip [lindex $argv 1]
set myvgmodel [lindex $argv 2]
set myuid [lindex $argv 3]
set mypwd [lindex $argv 4]
set myout [lindex $argv 5]
set outfh [open "$myout/$myvgw.txt" "w+" 0666 ]
set prompt ">|#"

# Create the logfile file
log_file -a ./getvoxinfo.log
# This command tells expect not to echo the output to the console.
# comment the following line if you want to see stdout messages
log_user 0

# Begin processing
send_user "\n"
send_user "Gathering configuration information for $myvgw!"
send_user "\n"

# spawn ssh
spawn ssh -o StrictHostKeyChecking=no $myuid\@$myvgip

# Allow this script to handle ssh connection issues
 expect {
  timeout { send_user "\nConnection timed out. Check host $myvgw\n"; exit 1 }
  eof { send_user "\nSSH Connection To $myvgw Failed\n"; exit 1 }
  "*#" {}
  "*assword:" { send "$mypwd\n" }
 }

# determine gateway type and set command list
if {$myvgmodel == "MGCP"} {
 set commands {
  "show mgcp"
  "show mgcp endpoint"
  "show ccm-manager"
  "show controllers T1 | include T1"
  "show isdn status"
  "show sccp"
  "show dspfarm all"
 }
}
if {$myvgmodel == "H323"} {
 set commands {
  "show run | s dial-peer voice.*voip"
  "show controllers T1 | include T1"
  "show isdn status"
  "show sccp"
  "show dspfarm all"
 }
}
if {$myvgmodel == "Analog"} {
 set commands {
  "show run | s sccp"
  "show stcapp device summary"
  }
}
#Process Commands on device
expect -re $prompt
exp_send "term len 0\r"
expect -re $prompt

foreach cmd $commands {
 exp_send "$cmd\r"
 sleep 2
 expect -re $prompt {
  set mytmpbuff $expect_out(buffer)
  foreach line [split $mytmpbuff "\r"] {
   if {$line == $cmd || $line == "\n$myvgw#"} {
    # ignore the line
    puts $outfh "!\r\n"
   } else {
    puts $outfh [string trim $line "\r\n"]
   }
  }
 }
}

# wrap it up
# send carriage return to kick out a prompt
exp_send "\r"
expect -re $prompt {
 send_user "Execution complete. Closing file..."
 close $outfh
 send_user "Done!\r\nSending exit command to router..."
 exp_send "exit\r"
 expect ":~\$" {
  send_user "Done!\r\n"
  exit
 }
}

The expect script runs as a single instance per voice gateway. To automate the task of getting configuration information from multiple gateways I created a text file that was a CSV listing my gateway names, IP addresses, and gateway types. I actually built the CSV by running SQL queries on CUCM to pull out the information. The IP addresses for H323 devices are usually used for the device name (bonus). MGCP and SCCP devices require you to either have the IP address handy or (in large environments where you don't have the info) you can use the RISPort API to get IP addresses. I had the info handy in a customer provided spreadsheet (thanks Mr. Customer).

Now, I used a bash shell script to collect some basic environment information and to loop through the CSV file. Launching the expect script on every iteration. The shell script follows:

launch-vgwcheck.sh


#!/bin/bash

# Gather basic cred info

echo -n "Enter source file: "
read -s -e mysrcfile
echo -ne '\n'
echo -n "Enter output directory: "
read -s -e myoutdir
echo -ne '\n'
echo -n "Enter User ID: "
read -s -e myuid
echo -ne '\n'
echo -n "Enter password for ($myuid): "
read -s -e mypwd
echo -ne '\n'


# Loop through source file and execute expect script
while read myrec
do
 myvgw=$(echo $myrec | cut -f1 -d',')
 myvgip=$(echo $myrec | cut -f2 -d',')
 myvgmodel=$(echo $myrec | cut -f3 -d',')
 ./getvoxinfo.exp $myvgw $myvgip $myvgmodel $myuid $mypwd $myoutdir
done < $mysrcfile


The above scripts should run "as is". At least, they should on Mac OS X. I have used them twice since their creation in November/December. The only things I had to tweak were the "set commands" declarations. 

Conclusion

I guess I am trying to get two things across with this blog. First, maybe the little junk script I put together will help someone. If it does, that we be most righteous. It certainly isn't doing harm. Second, and most important, the best tool you have is your ingenuity and willingness to experiment. 

Maybe another point that I should put out there is "choose your battles wisely". Sometimes it is just more efficient to manually power through a task. Developing some script, particularly if it is using a language you are unfamiliar with, will be a huge time sink. Fortunately, I have used this script a couple of times since I originally put it together and I have expanded on its functionality a bit. So, it was worth the time investment. For me, anyway. 




Thanks for reading. If you have time, post a comment!

1 comment:

  1. This is Way Cool!! Speaking as an Expect, and TCL script writer myself.

    ReplyDelete