Expect Automation Examples

O'Reilly Exploring Expect

I’ve been working for my new employer for just under 90 days now, carefully studying the network topology slowly pealing back the layers mindful not to break anything. There have been some exciting moments not including the Cisco Catalyst 6509 VSS member that decided to go into recovery mode one evening – revealing a cabling problem with a pair of Cisco ASA 5585-X running in an HA configuration.

A few weeks back I had the opportunity to show off some of my scripting skills by automating the configuration change of some 450+ Motorola RFS4000 and Symbol WS2000 Wireless LAN Switches. We were migrating from Microsoft’s Internet Authentication Service (IAS) to Microsoft’s Network Policy Service (NPS).

The first problem, nobody had an inventory of the wireless LAN switches that we needed to reconfigure. No problem – a quick dump of the logs on the IAS servers provided a nice lengthy list of IP addresses which had authenticated with IAS over the past 6 months. I wrote a quick Perl script to interrogate each IP address, first via ICMP, then via SNMP, and lastly via WGET/CURL.  The result was a list of each model switch we had to contend with along with the software versions; WS2000 v1.x, RFS4000 v4.x and RFS4000 v5.x . There weren’t too many RFS4000 v5.x switches so we decided to handled those changes manually, although on retrospect it would have been far easier to also code that solution as opposed to manually logging into all those WiNG 5.x devices at 4AM in the morning.

I did have a challenge with the passwords. There were multiple administrator passwords in use across all the different models so I had to add some logic to deal with the three different possible passwords. It was good that there were only three passwords because a fourth failed password attempt would cause the wireless LAN switch to disconnect the session and would have made the task a lot harder .

I ended up writing two scripts, one for the WS2000 v1.x and one for the RFS4000 v4.x due to the time constraints. I could have combined the two scripts and detected the version of software but there were some anxious managers waiting eagerly on this change. I added a bash shell script to kick off the Expect script for each switch model and then loop through all the IP addresses or FQDNs.

This wasn’t a sexy solution by any means, it required a bit of testing to determine the commands that were needed for each model and software release but it was a much better solution than manually making the changes on some 450+ devices. We did have to-do a bit of error checking to make sure that the configurations went down to all 450+ devices. We had one or two instances where the WAN connection to that specific office just happen to go offline while the script was running. Thankfully we were able to use the logfile size (logs generated by the Expect script) as a quick determination if there had been a problem or discrepancy that required additional investigation.

If you have a similar challenge hopefully you’ll find the code below helpful.

Symbol WS2000 v1.x

run-ws2000.sh

#!/bin/bash
#
# Language: Bash Shell Script
#
# Filename: /usr/local/etc/run-ws2000.sh
#
# Purpose:  This script will kickoff the Expect scripts that will re-configure 
#           the RADIUS configuration on the Motorola (formerly Symbol) WS-2000
#           Wireless LAN Switches.
#
# Author:   Michael McNamara
# Date:     February 21, 2014
# Version:  1.0
#
# Changes:
#

# Variables
PATH_TO=/usr/local/etc/
EXPECT=/usr/local/etc/symbolws2000radius.exp
SWITCHES='10.1.1.1 10.1.1.2 10.1.1.3 10.1.1.4'

##########################################################################
# M  A I N   S C R I P T  B O D Y
##########################################################################

for SWITCH in $SWITCHES
do
	$EXPECT $SWITCH
done

exit

symbolws2000radius.exp

#!/usr/bin/expect -f
#
# Language: Expect
#
# Filename: /usr/local/etc/symbolws2000radius.exp
#
# Purpose:  This is an Expect script that will login to a Motorola (formerly
#           Symbol) WS2000 Wireless LAN Switch v1.x and modify the RADIUS servers
#           used for 802.1x EAP authentication of the corporate ESSID/WLAN.
#
# Author:   Michael McNamara
#
# Date:     February 21, 2014
#
# Version:  1.0
#
# Changes:
#           February 24, 2014 (M.McNamara) v1.1 - issue with enable prompt changing,
#               abstract prompt in a varaible to account for all possibilities.
#
# License:
#           Copyright (C) 2010 Michael McNamara (mfm@michaelfmcnamara.com)
#
#           This program is free software: you can redistribute it and/or modify
#           it under the terms of the GNU General Public License as published by
#           the Free Software Foundation, either version 2 of the License, or
#           (at your option) any later version.
#
#           This program is distributed in the hope that it will be useful,
#           but WITHOUT ANY WARRANTY; without even the implied warranty of
#           MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#           GNU General Public License for more details.
#
#           You should have received a copy of the GNU General Public License
#           along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Variables

set PATH "/usr/local/etc"
set TELNET "/usr/bin/telnet"

set SWITCH [lindex $argv 0]
set USERNAME admin
set PASSWORDS {password1 password2 password3}
set PINDEX 0

# Which Wireless LAN are we going to change?
set WLAN_IDX 2

# RADIUS/NPS Servers
set RADIUS1 10.1.1.1
set RADIUS2 10.1.1.2

set PROMPTS "(%|#|cli\>|admin\>|\$|\-\-\-\>)"

set TODAY [timestamp -format %y%m%d ]
set WEEKDAY [timestamp -format %a ]
set DATE [timestamp -format %c ]

stty -echo

log_file $PATH/logs/$SWITCH.radius.log
log_user 0	# Disable logging to STDOUT
#log_user 1	# Enable logging to STDOUT

# Useful information out to logfile
send_log "*********************************************************************\r\n"
send_log "Starting logfile for $SWITCH on $DATE\r\n"
send_log "*********************************************************************\r\n"

set timeout 30
spawn $TELNET $SWITCH

expect {
	"Connected to" {

		expect "login:"
		send -- "$USERNAME\r"
		expect -exact "assword:"
		send -- "[lindex $PASSWORDS $PINDEX]\r"

		expect {
			"Login incorrect" {
				send_user "\nDEBUG: Login failed with $USERNAME [lindex $PASSWORDS $PINDEX] on $SWITCH\n"
				send_log "\nDEBUG: Login failed with $USERNAME [lindex $PASSWORDS $PINDEX] on $SWITCH\n"

				incr PINDEX
				if {$PINDEX == [llength $PASSWORDS]} {
					send_user "ERROR: PASSWORD ISSUE WITH $SWITCH - UNABLE TO LOGIN!\n" 
					send_log "*********************************************************************\r\n"
					send_log "End of logfile for $SWITCH on $DATE \r\n"
					send_log "*********************************************************************\r\n"
					exit	
				}

				expect "login:"
				send -- "$USERNAME\r"
				expect -exact "assword:"
				send -- "[lindex $PASSWORDS $PINDEX]\r"

				exp_continue
			}
			"admin>" {
				send -- "network\r"
				expect -re $PROMPTS
				send -- "wlan\r"
				expect -re $PROMPTS
				send -- "show eap $WLAN_IDX\r"
				expect -re $PROMPTS

				####################################################################
				# REMOVE THE FOLLOWING # FROM THE FILE TO ACTUALLY MAKE THE CHANGES
				####################################################################
				# REMOVE THE FOLLOWING # FROM THE FILE TO ACTUALLY MAKE THE CHANGES
				####################################################################

				send -- "set eap server $WLAN_IDX 1 $RADIUS1\r"
				expect -re $PROMPTS
				send -- "set eap server $WLAN_IDX 2 $RADIUS2\r"
				expect -re $PROMPTS
				send -- "show eap $WLAN_IDX\r"
				expect -re $PROMPTS
				send -- "save\r"
				expect -re $PROMPTS

				####################################################################

				send -- "quit\r"
				expect eof
			}
		}

	}

	"No route to host" {
		send_log "ERROR: Unable to connect to $SWITCH via telnet!\n"
		send_user "ERORR: Unable to connect to $SWITCH via telnet!\n"
	}

}

send_log "*********************************************************************\r\n"
send_log "End of logfile for $SWITCH on $DATE \r\n"
send_log "*********************************************************************\r\n"

exit 0

Motorola RFS4000 v4.x

run-rfs4000.sh

#!/bin/bash
#
# Language: Bash Shell Script
#
# Filename: /usr/local/etc/run-rfs4000.sh
#
# Purpose:  This script will kickoff the Expect scripts that will re-configure 
#           the RADIUS configuration on the Motorola RFS 4000 v4.x
#           Wireless LAN Switches.
#
# Author:   Michael McNamara
# Date:     February 21, 2014
# Version:  1.0
#
# Changes:
#
#

# Variables
PATH_TO=/usr/local/etc
EXPECT=/usr/local/etc/motorolarfs4000radius.exp
SWITCHES='10.1.1.1 10.1.1.2 10.1.1.3 10.1.1.4'

##########################################################################
# M  A I N   S C R I P T  B O D Y
##########################################################################

for SWITCH in $SWITCHES
do
	$EXPECT $SWITCH
done

exit

motorolarfs4000radius.exp

#!/usr/bin/expect -f
#
# Language: Expect
#
# Filename: /usr/local/etc/motorolarfs4000radius.exp
#
# Purpose:  This is an Expect script that will login to a Motorola (formerly
#           Symbol) RFS4000 Wireless LAN Switch v4.x and modify the RADIUS servers
#           used for 802.1x EAP authentication of the corporate ESSID/WLAN.
#
# Author:   Michael McNamara (mfm@michaelfmcnamara.com)
#
# Date:     February 21, 2014
#
# Version:  1.1
#
# Changes:
#	    February 25, 2014 (M.McNamara) v1.1 - disable StrictHostKeyChecking so the
#		the initial SSH connection doesn't generate a yes/no dialog which
#		could hang up the Expect script.
#
#           February 24, 2014 (M.McNamara) v1.0 - issue with enable prompt changing, 
#		abstract prompt in a varaible to account for all possibilities.
#
# License:
#           Copyright (C) 2014 Michael McNamara (mfm@michaelfmcnamara.com)
#
#           This program is free software: you can redistribute it and/or modify
#           it under the terms of the GNU General Public License as published by
#           the Free Software Foundation, either version 2 of the License, or
#           (at your option) any later version.
#
#           This program is distributed in the hope that it will be useful,
#           but WITHOUT ANY WARRANTY; without even the implied warranty of
#           MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#           GNU General Public License for more details.
#
#           You should have received a copy of the GNU General Public License
#           along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Variables

set PATH "/usr/local/etc"
set TELNET "/usr/bin/telnet"
set SSH "/usr/bin/ssh"

set SSH_OPTIONS "-o StrictHostKeyChecking=no"

set SWITCH [lindex $argv 0]
set USERNAME admin
set PASSWORDS {password1 password2 password3}
set PINDEX 0

# Which Wireless LAN are we going to change?
set WLAN_IDX 2

# RADIUS/NPS Servers
set RADIUS1 10.1.1.1
set RADIUS2 10.1.1.2

set PROMPTS "(%|#|cli\>|admin\>|\$|\-\-\-\>)"

set TODAY [timestamp -format %y%m%d ]
set WEEKDAY [timestamp -format %a ]
set DATE [timestamp -format %c ]

stty -echo

# Setup the logging
log_file $PATH/logs/$SWITCH.radius.log
log_user 0	# Disable logging to STDOUT
#log_user 1	# Enable logging to STDOUT

# Useful information out to logfile
send_log "*********************************************************************\r\n"
send_log "Starting logfile for $SWITCH on $DATE\r\n"
send_log "*********************************************************************\r\n"

# Set the timeout to 30 seconds for the following commands
set timeout 30

# Spawn an SSH connection to the switch
spawn $SSH $SSH_OPTIONS $USERNAME@$SWITCH

expect {
	"yes/no" {
		send "yes\r" }

	"User Access Verification" {

		expect "*?sername:"
		send -- "$USERNAME\r"
		expect -exact "assword:"
		send -- "[lindex $PASSWORDS $PINDEX]\r"

		expect {
			"Incorrect Login" {
				send_user "\nDEBUG: Login failed with $USERNAME [lindex $PASSWORDS $PINDEX] on $SWITCH\n"
				send_log "\nDEBUG: Login failed with $USERNAME [lindex $PASSWORDS $PINDEX] on $SWITCH\n"

				incr PINDEX
				if {$PINDEX == [llength $PASSWORDS]} {
					send_user "ERROR: PASSWORD ISSUE WITH $SWITCH - UNABLE TO LOGIN!\n" 
					send_log "*********************************************************************\r\n"
					send_log "End of logfile for $SWITCH on $DATE \r\n"
					send_log "*********************************************************************\r\n"
					exit	
				}

				expect "*?sername:"
				send -- "$USERNAME\r"
				expect -exact "assword:"
				send -- "[lindex $PASSWORDS $PINDEX]\r"

				exp_continue
			}
			">" {
				send -- "terminal length 0\r"
				expect -re $PROMPTS
				send -- "enable\r"
				expect -re $PROMPTS
				send -- "show wireless mobile-unit\r"
				expect -re $PROMPTS
				send -- "show wireless wlan config $WLAN_IDX\r"
				expect -re $PROMPTS

				#####################################################################
				# REMOVE THE FOLLOWING # FROM THE FILE TO ACTUALLY MAKE THE CHANGES
				#####################################################################
				# REMOVE THE FOLLOWING # FROM THE FILE TO ACTUALLY MAKE THE CHANGES
				#####################################################################

				send -- "config t\r"
				expect -re $PROMPTS
				send -- "wireless\r"
				expect -re $PROMPTS
				send -- "wlan $WLAN_IDX radius server primary $RADIUS1\r"
				expect -re $PROMPTS
				send -- "wlan $WLAN_IDX radius server secondary $RADIUS2\r"
				expect -re $PROMPTS
				send -- "exit\r"
				expect -re $PROMPTS
				send -- "show wireless wlan config $WLAN_IDX\r"
				expect -re $PROMPTS
				send -- "write mem\r"
				expect -re $PROMPTS

				#####################################################################

				send -- "quit\r"
				expect eof
			}
		}

	}

	"No route to host" {
		send_log "ERROR: Unable to connect to $SWITCH via telnet!\n"
		send_user "ERORR: Unable to connect to $SWITCH via telnet!\n"
	}

}

send_log "*********************************************************************\r\n"
send_log "End of logfile for $SWITCH on $DATE \r\n"
send_log "*********************************************************************\r\n"

exit 0

Cheers!

{ 1 comment }

Happy St. Patrick’s Day 2014 #IrelandInspires

527033_36997563-50

Happy St. Patrick's Day!!! It's been more than 15 years since I was back to Ireland to see my family and friends. I spent a lot of time in Ireland when I was younger working on my Uncle's dairy farm outside Listowel. I can still remember the day trips to Dublin and the weekend outings to Ballybunion.  And who could forget the Rose of Tralee, was always and exciting festival and event. http://www.youtube.com/watch?v=9wmIZEl1nSo Cheers […] Read More

{ 1 comment }

Short Story – switchport trunk allowed vlan

1402507_78786595

I like sharing these stories because they help me document some really simple problems that can sometimes take a few minutes to troubleshoot and ultimately resolve. The moral of this story resolves around the much used command "switchport trunk allowed vlan x,y,z" and the often overlooked commands "switchport trunk allowed vlan add|remove x,y,z". I decided to write about this topic since I recently encountered operational "difficulties" with both my prior employer and my current employer that involved the same near identical mistake of a network engineer accidentally overwriting the list of allowed VLANs. In the most recent case it was […] Read More

{ 2 comments }

Winter Storm Pictures – Part 2

IMG_20140205-1

It's been a wild and crazy week here in southeastern Pennsylvania. In December I made a post extolling the beauty of a recent snow storm. This past week I saw the furry of mother nature as multiple winter storms pummeled the Philadelphia area.  On Wednesday a 1/4 inch of ice fell on the Philadelphia suburbs that brought down power lines and trees leaving many people literally out in the cold. Unfortunately I too was left in the dark, but thankfully I have a portable generator. With temperatures dropping into the low 20s (F) at night I hard wired the house […] Read More

{ 0 comments }

It’s the networks fault #14

network_cable_by_tootall

It's been an incredibly busy January for me as I transitioned from managing a large regional network with 10Gbps WAN links to managing a large global network with 100Mbps WAN links. It's always interesting to see yet another meshed VLAN and IP addressing scheme. An engineer at my last employer would always say, "The best thing about standards? There's always more than one to choose from." I had to actually go shopping for a few more pairs of jeans and t-shirts. Gain 30% Linux Disk Performance with noatime, nodiratime, and relatime by Bob Plankers - Bob shared a number of […] Read More

{ 1 comment }