Michael McNamara https://blog.michaelfmcnamara.com technology, networking, virtualization and IP telephony Sat, 30 Oct 2021 18:30:39 +0000 en-US hourly 1 https://wordpress.org/?v=6.7.2 VMware VeloCloud SD-WAN Orchestrator API and Python – Part 3 https://blog.michaelfmcnamara.com/2021/03/vmware-velocloud-sd-wan-orchestrator-api-and-python-part-3/ Tue, 02 Mar 2021 03:31:11 +0000 https://blog.michaelfmcnamara.com/?p=6886 It looks like this project is going to be moving forward again… time to dust off the Python code and finish out the last few pieces to the puzzle.

Interestingly enough I ran into a quick problem testing my original code. It looks like something had changed with the “Profile” that we’re using for each Edge. When I run my original Python script I’m getting a HTTP/400 returned along with the following response code, Interface “CELL1” present in Profile but not in Edge. Looking through some of the JSON data it would appear that something has changed with the Profile that I’m using in the configuration. The error I’m getting when calling rest/configuration/updateConfigurationModule likely means that I’m missing some required data in my Jinja templates that the VMware VeloCloud Orchestrator is now expecting.

There is a Chrome extension called VeloCloud Developer Assistant, that can help you break down the JSON data and make it a little easier to visually consume and troubleshoot. I personally prefer just going into the Chrome developer tools and copying out the entire JSON data block that’s being posted and then running that through some JSON formatting tool to help clean it up for human consumption. If you go through the steps in the web UI with the Chrome developer tools open, can you go back and extract all the JSON data that is being sent to the VeloCloud Orchestrator, and in short you can easily reverse engineering the calls and the JSON data.

In the end I was able to find the missing CELL1 interface under the routedInterfaces element. I added the missing data elements to the Jinja template and everything started working again. I ended up writing a few other supporting scripts to help with the overall project goal. I wrote a Perl script to poll the existing hardware to gather up all the IP configuration details from each VLAN and interface which then can be fed into the Python script to build the configuration within the VeloCloud Orchestrator. There’s also a management IP required, so I used a snippet of Perl code that I wrote back in 2016 to call the Infoblox API to assign the next available IP address in the management subnet.

With the Jinja templates it’s relatively easy to put this code onto a web server and build a simple WebUI around some Python or PHP code to generate new configurations when needed.

Cheers!

]]>
Infoblox REST API with Perl https://blog.michaelfmcnamara.com/2016/05/infoblox-rest-api-with-perl/ Sun, 01 May 2016 13:19:08 +0000 https://blog.michaelfmcnamara.com/?p=5689 I recently had the need to add a couple of thousand host objects to our Infoblox IPAM solution since we were missing almost all our store routers, switches, wireless switches, APC UPS, printers, etc. With over 500+ stores and not having any interns available I wasn’t looking forward to the challenge of navigating the WEB UI within Infoblox to create over 2,500+ objects. Instead I decided to let the computer do the work and I wrote a quick and dirty little Perl script that could essentially loop over the subnets in a /16 network and create the host objects for each device one at a time. This isn’t particularly pretty but it got the job done for me and I’m posting it here in hopes that it will help someone else traveling the same path.

I started with the following cURL request since I needed to know what format the data had to be in;

curl -k1 -u username:password -H "Content-Type: application/json" -X POST https://infoblox.acme.com/wapi/v2.1/record:host -d '{"ipv4addrs":[{"ipv4addr":"10.1.1.250"}],"name":"store1-printer.acme.com","comment":"HP LaserJet Printer"}'

That cURL request would create a host object at 10.1.1.250 with the name of store1-printer.acme.com. I took that structure, and built some Perl code around it and got the following script;

#!/usr/bin/perl
#
# Filename: /usr/local/etc/infoblox-rest.pl
#
# Purpose:  Perl code to create Infoblox host objects utilizing REST API 
#           interface. Used to create DNS objects across multiple subnets.
#           This code will create host objects for all the routers, switches,
#           APC UPS, servers, PC, etc. Since the IP address of those devices
#           utilize the same last octect in each IP subnet and there is a 
#           standard naming convention to easily follow programatically.
#
# Notes:    This code is really just an example of what's possible using 
#           the Infoblox REST API interface. I don't expect anyone to actually
#           use this code directly, but hopefully they will learn how to 
#           use the REST API interface in Perl through this example.
#           
# Author:   Michael McNamara (mfm@michaelfmcnamara.com)
#
# Date:     April 15, 2016
#
# Version:  1.0
#
# License:
#           Copyright (C) 2016 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 3 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/>
#
# Changes:
#
#  April 30, 2016 (M.McNamara)
#    clean up the code, add license
#

# Load Modules
use strict;
use warnings;
use utf8;
use REST::Client;
use JSON;
use MIME::Base64;
use Data::Dumper;

# Infoblox username and password
my $username = 'InfobloxUsername';
my $password = 'InfobloxPassword';

# Declare Variables
my $IP;
my $HOST;
my $NUM;

# Declare Infoblox URL
# replace infoblox.acme.com with your Infoblox URL or IP address
my $host = 'https://infoblox.acme.com/wapi/v2.1';

# Declare the HTML headers needed
my $headers = {Content_Type => 'application/json', Accept => 'application/json', Authorization => 'Basic ' . encode_base64($username . ':' . $password)};

# Declare the REST object
my $client = REST::Client->new();

# Link the REST object to the Infoblox URL
$client->setHost($host);

# Loop across all the subnets in the /16 network
for (my $i=1; $i<=100; $i++) { 

# The IP is based on which subnet we're loop through 
$IP = "10.246.$i.250"; 

# In some cases it's necessary to perform some arithmetic on the IP 
$NUM = $i - 0; 

# Let's determine what the DNS name should be for this object 
$HOST = sprintf("store46%02d-printer.acme.com",$NUM); 

# Let's echo to the screen the IP address and DNS name 
print "IP = $IP \t HOST = $HOST\n"; 

# Let's set the JSON data set we're going to send to the REST API 
my %json_data = ( ipv4addrs => [{
                        ipv4addr => $IP }],
                        name => $HOST,
                        comment => 'HP LasterJet Printer'

                );

	# Let's encode the JSON data before we send it
	my $data = encode_json(\%json_data);

	# Let's call the REST API interface and create the host object
	$client->POST('/record:host',($data,$headers));
	
	# Let's decode the result so we can see what happened (if debugging)
	#my $response = decode_json ($client->responseContent());

	# Let's print the respone
	print Dumper( $client->responseContent() );

} #end for

exit;

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

Cheers!

]]>
Response: Scripting Does Not Scale For Network Automation https://blog.michaelfmcnamara.com/2014/07/response-scripting-does-not-scale-for-network-automation/ https://blog.michaelfmcnamara.com/2014/07/response-scripting-does-not-scale-for-network-automation/#comments Mon, 21 Jul 2014 21:06:32 +0000 http://blog.michaelfmcnamara.com/?p=4446 About three weeks ago Greg Ferro from Etherealmind posted an article entitled “Scripting Does Not Scale For Network Automation“. It’s quite clear from reading the article that Greg really is “bitter and jaded“.  While I agree that there are challenges in scripting they also come with some large rewards for those that are able to master the skill.

In a subsequent comment Greg really hits on his point.. “We need APIs for device consistency, frameworks for validation and common actions. But above that we need platforms that solve big problems – scripting can only solve little problems.

I agree but for now we need to work with what we have available, and that’s no reason to stop scripting today. That said scripting is not a tool that’s going to solve every problem in IT. It might helpful for initial deployments, provisioning, backups, monitoring, testing, etc. but it’s rare that scripting will solve every problem. I personally employ a combination of commercial management solutions with scripting to achieve my goals. I’ve worked with the following methods and technologies: EXPECT/TCL, SNMP, PHP, PERL, XML, NETCONF. These all have their individual challenges but each can be used in their own fashion to help automate a task or process depending on the task or the vendor in question. If you need to-do something once or twice there’s no need for a script or automation, but if you are going to-do something daily or weekly across dozens or hundreds of assets then a script can be extremely helpful.

The point of writing a script is really two fold in my opinion, first to automate the task but more importantly to remove the human error element. I do a lot of my work in the wee morning hours when the eyes are bloodshot and the mind isn’t always as rested as it should be. It’s easy to make simple stupid mistakes repeating monotonous commands on dozens even hundreds of switches or routers. A script helps to actually do the work and it makes sure that I won’t accidentally blow something up, I’m really there just to monitor for problems or issues.

It should be no surprise that there’s effort required to maintain a script, it’s just like a commercial vendor maintaining a product. Here’s the changelog for a Perl script I maintained between 2003 and 2014  that utilized SNMP and TFTP against Avaya/Nortel, Cisco, Motorola/Symbol and HP gear. You can see some of the challenges that Greg referred to in his article;

# Changes:
#
#     May 04, 2011 (M.McNamara) added support for HP C-Class GbE2c and legacy P-Class GbE2
#                               thanks to Karol Perkowski for his code addition
#     Dec 28, 2010 (M.McNamara) added additional code to support ERS4500 being slow TFTP transfer
#     Dec 27, 2010 (M.McNamara) updated CISCO-PRODUCTS-MIB to cover ciscoCBS3120 blade
#     Dec 20, 2010 (M.McNamara) updated ASCII routine with OID s5AgSysAsciiConfigManualUpload
#     Aug 31, 2010 (M.McNamara) added routines to handle binary and ASCII data for Avaya ERS switches
#				also added code to keep 4 archive copies per device
#     Dec 02, 2009 (M.McNamara) cleaned up code added additional debug routines
#     Oct 23, 2008 (M.McNamara) added support for Motorola RFS7000 Wireless LAN Switch
#     Oct 22, 2008 (M.McNamara) added support for ASCII configuration files for Avaya ERS switches
#     Oct 10, 2008 (M.McNamara) added support for Cisco switches
#     Jan 22, 2008 (M.McNamara) added support for HP GbE2c (C-Class) switch
#     Apr 24, 2007 (M.McNamara) added support for WS5100 3.x software
#     Oct 24, 2006 (M.McNamara) added support for ERS1600 v2.1 release
#     Sep 29, 2006 (M.McNamara) added support for BayStack 470 PwR 48T
#     Oct 20, 2005 (M.McNamara) added support for Baystack 5510 24 port also added 
#				Ethernet Routing Switch (formerly Passport) 8600 code
#     Mar 01, 2005 (M.McNamara) incorporated a sub to check for the presence of the
#				proper filename on the TFTP server (/tftpboot) thereby 
#				eliminating the first script "readytftpbackup.pl"
#     Feb 25, 2005 (M.McNamara) added the ability to retry a failed backup
#     Jan 13, 2004 (M.McNamara) some minor bugs throughout code base
#     Jan 06, 2004 (M.McNamara) implemented a workaround for the Passport RAPID-CITY MIB 
#				> 3.2 problem, copied OIDs for Passport 1600 into 
#				 existing MIB along with required MIBS and added sub 
#				to handle 1600s
#     Jan 05, 2004 (M.McNamara) issues with SNMP MIB for Passport 8600 v3.3.4 is presenting
#				problems with the Net-SNMP perl modules and the old MIB 
#				cannot identify the newly added Passport 1600 switches.
#     Dec 11, 2003 (M.McNamara) resolved issue with Passport 8600 not backing up properly
#     Sep 17, 2003 (M.McNamara) added code to incorporate all BayStack switches into backup
#     Oct  1, 2003 (M.McNamara) added code to email status report to notify@acme.org
#				also added Perl script to weekly crontab

Will the scripts I write today be useless in two years, possibly but that’s pretty much the case with anything these days including your phone, your laptop, etc. While we wait for something else to come along the the scripts I write and maintain will be very helpful in making my job easier and making me more efficient.

Cheers!

PS: I’ve finally cleaned up the Scripting section of my blog, fixing all the broken links and updating all the code.

]]>
https://blog.michaelfmcnamara.com/2014/07/response-scripting-does-not-scale-for-network-automation/feed/ 1
Expect Automation Examples https://blog.michaelfmcnamara.com/2014/03/expect-automation-examples/ https://blog.michaelfmcnamara.com/2014/03/expect-automation-examples/#comments Wed, 26 Mar 2014 00:49:02 +0000 http://blog.michaelfmcnamara.com/?p=4304 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!

]]>
https://blog.michaelfmcnamara.com/2014/03/expect-automation-examples/feed/ 1
Automation – Poor Mans Style https://blog.michaelfmcnamara.com/2013/08/automation-poor-mans-style/ https://blog.michaelfmcnamara.com/2013/08/automation-poor-mans-style/#comments Fri, 16 Aug 2013 13:00:17 +0000 http://blog.michaelfmcnamara.com/?p=3221 There has been a lot of discussion recently in networking circles surrounding automation especially in discussions about Software Defined Networking (SDN). While automation means different things to different people I would define it as any tool or solution that automates repetitive tasks (making the job easier) while making the output more consistent and ultimately the network more reliable. I’m a huge proponent of having the computer do the work, I guess that could be defined as automation.

The purpose of this post is to provide some simple examples of how you can start automating today. These are not glamorous solutions hence the poor man slogan but they should help provide some idea of what’s possible. There are plenty of open-source and commercial solutions out there, one that’s been receiving some extra press these past few months is Puppet.

In my current organization we deploy a lot of equipment and we usually do so on a very tight timetable where we have hours, not days or weeks to turn up a closet or a remote site. So our time is extremely precious but more so we can’t afford to be troubleshooting erroneous configuration errors that could easily be avoided with some simple automation. Like numerous organizations before us we too had Microsoft Word Templates and Excel macros and formulas but we almost always ran into problems with the human element of the equation.

I took a small 1Gbps CentOS Linux guest with a LAMP (Linux, Apache, MySQL, PHP) stack and started throwing together some Perl, PHP and JavaScript code. The outcome was a pretty powerful example of what’s possible without a big capital investment or some consulting company reaching their quarterly sales goal on your dime.

Here are three simple examples which are adoptions of each other, adding additional features as time allowed and the solutions matured.

Juniper SRX – VPN Branch Offices

While we were migrating our remote branch offices (31+ locations in all) to Juniper SRX Service Gateways we quickly realized we needed a more reliable solution than building the configuration by hand.  We had a Microsoft Word template that had various fields marked {RED}, the field engineer would perform a search-n-replace to ultimately build the configuration. In our first few conversions we had a number of typos in the configuration that caused use to overrun our scheduled maintenance window. How can we make configuring the Juniper SRX easier for our field engineers? What about a web based portal that takes in the assorted variables and outputs a working configuration?

Juniper SRX Configuration Generator

The solution was really quite easy and has been done by others before. The field engineer plugs in a few values and the Perl/PHP application spits back a complete configuration for both the branch office Juniper SRX 210H and the main office Juniper SRX 650. The initial version of the application required the field engineer to enter a random 128 character shared key, later versions of the application automatically generated a random shared key for use in the configuration. This approach completely eliminated any other configuration issues during the migration project and is now part of our standard process for a new greenfield site.

Avaya Ethernet Routing Switch 4850GTS-PWR+

On the heals of that migration we had a very large expansion project underway at our largest facility. The physical construction called for the installation of about 63+ Avaya Ethernet Routing Switch 4850GTS-PWR+ switches. In order to help streamline the configuration process and help eliminate configuration errors I built an adaption of the earlier application above to fit the requirements for this project. In this project I expanded the functionality of the original application by adding JavaScript code to perform client side data validation. If the field called for an IP address, then the JavaScript code would only submit the data to the server if the field passed validation. It was pretty straight forward and simple but we took the original solution and improved on it.

Avaya Ethernet Routing Switch Configuration Generator

APC UPS/PDU Management Cards

In that same expansion project we also identified the need to streamline the configuration of the American Power Conversion (APC) UPS’s and PDUs that we were deploying throughout the infrastructure. If you’ve ever worked with them you know they can be somewhat difficult to quickly and easily configure. Our field engineers were spending on average 1 hour to configure each device and often there were inconsistencies in the configuration depending on which field engineer had performed the configuration. So we came up with a new streamlined process which allows the engineer to complete the task in about 15 minutes. The field engineer manually configures a DHCP reservation (manual DHCP) utilizing the MAC address of the management card within our Infoblox IP address management solution. Once the UPS or PDU is online and communicating with the network the field engineer plugs in a number of variables into the web browser and the Perl application will output the configuration. In this case we decided to take this solution one step further by having the Perl application actually program the configuration into the device. The Perl application will generate the configuration and then will make a FTP call to the actual asset and upload the configuration. The only thing left for the field engineer was to perform some simple tests once the task was complete, to verify that the asset was reporting, sending SNMP traps, to our management platform. And even that last step could have probably been easily automated.

APC UPS PDU Configuration GeneratorMy Thoughts

There are a number of frameworks that I could have used in writing these applications but I decided to keep it simple (this time around). The point here is to just provide an example of what’s possible. There are quite a few tools and solutions in the market place that already leverage SNMP, NET-CONF, XML, SOAP APIs, etc to help provide integration between systems as well as management and automation.

Wouldn’t it be great if the last application accepted the MAC address of the APC UPS/PDU and made an automated call to Infoblox and automatically created a DHCP reservation for that asset? Thereby streamlining the process even further? There’s nothing stopping me from doing that other than the time and energy it takes to code the solution and then test it appropriately.

I’m not ready right now to release the actual code but if enough people request I will work to creating sanitized copies and release the code under a GPL license.

Let me know what your doing around automation.

I recall a number of interesting posts a few years back where some folks had completely automated how they inventory and on-board their IP phones. They were using bar code scanners to collect the information from the outside of the box and then had an automated process for taking that information and creating the necessary configuration files for a zero-touch installation, including the actual node and TN information for the Avaya Communication Server 1000. That was a pretty neat example of automation in my opinion and obviously saved them a lot of time and effort.

 Cheers!

]]>
https://blog.michaelfmcnamara.com/2013/08/automation-poor-mans-style/feed/ 8
Automation with Perl SOAP XML https://blog.michaelfmcnamara.com/2013/01/automation-with-perl-soap-xml/ https://blog.michaelfmcnamara.com/2013/01/automation-with-perl-soap-xml/#comments Wed, 02 Jan 2013 16:15:30 +0000 http://blog.michaelfmcnamara.com/?p=3242 perlxmlI’m always looking for ways to help automate the environment around me. I was recently asked to investigate how we could integrate our internally developed applications into our installation of CA Service Desk. I looked at the integration offered by Maileater but I wanted the ability to record/track the ticket numbers and handles for any tickets, incidents, requests or changes we were opening within Service Desk. I discovered that I could make SOAP/XML calls directly against the application, as long as I ordered the data properly and provided the correct bits and bytes. The value in tracking the ticket number and handle comes when you want to automatically acknowledge/close previously open tickets or if you might want to re-open previously closed tickets or requests when some condition is met.

I should note that the Service Desk Users forum was a big help in my initial research and already has a number of code snippets available on their site.

Here’s a quick Perl script that demonstrates how to interface with CA Service Desk via SOAP/XML calls. This specific script will create an Incident (type = crt:182) assigned to the group Help Desk ( group = 9C4E34BDF796B04DBAD365034FC92288) with the problem category of Alarms (category = pcat:400978) and set the reporting method to Other ( zreporting_method = i:7304).

The values above are specific to my installation of CA Service Desk, you may need to adjust the values for your specific environment and installation. You will also need to substitute a valid username and password that has access to Service Desk in the variables $USERNAME and $PASSWORD. You will also need to update the $wsdl variable to reference the hostname or IP address of the CA Service Desk application server.

You’ll note that I’m using a number of Perl modules to make life easy. You’ll need to have these Perl modules installed in order for the script to run properly. You can install the modules with the following commands assuming you are running CentOS/RedHat Linux;

yum install perl-SOAP-Lite perl-XML-Simple perl-XML-Dumper

Yum will automatically add any other per-requisite packages for you.

Here’s the actual Perl script;

#!/usr/bin/perl

#use SOAP::Lite trace=>'debug';
use SOAP::Lite;
use Data::Dumper;
use XML::Simple;
use strict;
use warnings;

# Declare Constants
use constant DEBUG      => 0;           # DEBUG settings
use constant CONSOLE    => 0;           # CONSOLE DEBUG settings

################################################################
# UPDATE WITH YOUR SPECIFIC USERNAME AND PASSWORD
################################################################
my $username = 'USERNAME';
my $password = 'PASSWORD';

# Declare Variables
my $xml = new XML::Simple;
my $ticket_number;
my $ticket_handle;
my $wsdl = 'http://hostname:8080/axis/services/USD_R11_WebService?wsdl';
my $service = SOAP::Lite->service($wsdl);

my $sid = $service->login($username, $password);

my $userHandle = $service->getHandleForUserid($sid,$user);
my $method = SOAP::Data->name('createRequest');

my @params = (SOAP::Data->name("sid" => $sid),
SOAP::Data->name("userHandle" => $userHandle),
SOAP::Data->name("attrVals" =>
  \SOAP::Data->value(
   SOAP::Data->name("string" => "description"),
   SOAP::Data->name("string" => "This is a test indicent created from a Perl script using a SOAP/XML interface"),
   SOAP::Data->name("string" => "customer"),
   SOAP::Data->name("string" => $userHandle),
   SOAP::Data->name("string" => "type"),
   SOAP::Data->name("string" => "crt:182"),
   SOAP::Data->name("string" => "category"),
   SOAP::Data->name("string" => "pcat:400978"),
   SOAP::Data->name("string" => "group"),
   SOAP::Data->name("string" => "9C4E34BDF796B04DBAD365034FC92288"),
   SOAP::Data->name("string" => "zreporting_method"),
   SOAP::Data->name("string" => "i:7304"),
   SOAP::Data->name("string" => "summary"),
   SOAP::Data->name("string" => "This is a test incident created via Perl SOAP/XML.")
  )
),
SOAP::Data->name("propertyValues" =>
  \SOAP::Data->value(
   SOAP::Data->name("string" => '')
  )
),
SOAP::Data->name("template" => ''),
SOAP::Data->name("attributes" =>
  \SOAP::Data->value(
   SOAP::Data->name("string" => 'ref_num'),
   SOAP::Data->name("string" => 'persistent_id')
  )
),
SOAP::Data->name("newRequestHandle"),
SOAP::Data->name("newRequestNumber")
);

print "dumper: ".Dumper(\@params)."\n" if (DEBUG);

my $soap = $service->call($method => @params);

if ($soap->fault){
        print "Error\n";
        print "[".$soap->faultcode."] [".$soap->faultstring."] [".$soap->faultdetail."]";
} else {
        print $soap->result if (DEBUG);
}

print "SOAP dump: ".Dumper($soap->result)."\n" if (DEBUG);

$xml = XMLin($soap->result);

$ticket_number = $xml->{Attributes}->{Attribute}->[0]->{AttrValue};
$ticket_handle = $xml->{Attributes}->{Attribute}->[1]->{AttrValue};

print "Ticket Number = $ticket_number\n";
print "Ticket Handle = $ticket_handle\n";

$service->logout($sid);

exit;

Cheers!

]]>
https://blog.michaelfmcnamara.com/2013/01/automation-with-perl-soap-xml/feed/ 1
Infoblox Perl CGI Application https://blog.michaelfmcnamara.com/2011/11/infoblox-perl-cgi-application/ https://blog.michaelfmcnamara.com/2011/11/infoblox-perl-cgi-application/#comments Fri, 11 Nov 2011 15:17:30 +0000 http://blog.michaelfmcnamara.com/?p=2510 I received quite a few emails from people asking me if I could share my Infoblox Perl CGI application. I’m most certainly happy to do so, this should provide a good basic starting point for anyone looking to build their own Perl CGI interface to the Infoblox appliances. I’ve posted a screenshot of the CGI application to the left of this article. It’s nothing fancy but it should demonstrate how to you can incorporate the functionality into your own management system or corporate Intranet. The purpose of this interface it to allow users to add MAC addresses into the MAC address filter employed by Infoblox without having to actually login to the Infoblox appliance.

I’m currently restricting access to this CGI application via Apache authentication in my organization, leveraging Apache against our Windows Active Directory.

Later versions of the application will hopefully include it’s own application level authentication against an LDAP source such as Microsoft’s Windows Active Directory along with some ability to log any submitted changes and issue email notification regarding changes.

You’ll find the Perl script along with the cascading style sheets and Javascript files in the archive infoblox-cgi.zip.

You’ll need the CGI and Infoblox Perl modules installed on your server. You should update the default values with your Infoblox IP address, username, and password, MAC filter name, etc. along with the URL of the server your going to use to host the CSS and Javascript files. I won’t post the entire script but here’s the first few lines…

#!/usr/bin/perl
#
# infoblox.pl
#
# Copyright (C) 2011 Michael McNamara
#
# 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 3 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/>.
#
# Filename: /var/www/cgi-bin/infoblox.pl
#
# Purpose:  add/change/remove MAC addresses from MAC filtering on Infoblox
#
# Author:   Michael McNamara
#
# Date:     October 24, 2011
#
# Changes:
#
#    October 24, 2011 (M.McNamara)
#       o  the beast is born had issues getting the Infoblox Perl libraries
#        to install on CentOS 5.7 due to a bug in perl-libwww-perl so
#        I built a CentOS 6.0 box that had a newer version of
#        perl-libwww-perl so I didn't get bogged down. Once that was
#        complete installing the Infoblox Perl libraries was pretty
#        easy. I took some of the sample code from there API refrence
#        manually and started carving it up with a Perl CGI interface.
#    October 25, 2011 (M.McNamara)
#    o  we have a working product the basics work now we need to start cleaning
#        up the interface, adding error checking, adding logging, etc
#
#    October 27, 2011 (M.McNamara)
#    o  added basic form validation for the MAC address using a JavaScript call
#    o  added logic to search for the MAC address first before trying to add it.
#    o  added some CSS to help cleanup the look of the interface
#
#
# Program Flow:
#

# Load Modules
use strict;
use warnings;
use CGI;
use CGI::Carp qw(fatalsToBrowser);
use Infoblox;

# Global Varibles
our $session;        # Infoblox Object
our $web;        # CGI Object
our $mac;        # Infoblox Object
our $fullurl;        # URL of script
our $macaddress;        # CGI Variable
our $description;       # CGI Variable
our $username;          # CGI Variable
our $DEBUG = 0;         # DEBUG FLAG for troubleshooting
our $action;        # What will we be doing
our ($sdate, $date, $time, $currentTime, $iTime);       # Time and Date

#
# THE VALUES BELOW SHOULD BE UPDATED FROM THE DEFAULTS
#
our $SERVER = "10.1.1.1";            # Infoblox Grid Master
our $USERID = "TestUser";            # Infoblox Username
our $PASSWD = "Test12345\$";            # Infoblox Password
our $MAC_ADDRESS = "99:88:77:66:55:44";        # USED FOR TESTING ONLY
our $MAC_FILTER = "IBFILTER";            # Filter used in Infoblox
our $TENYEARS = 31556926*10;            # Expiration time in seconds
our $OK_CHARS = 'a-zA-Z0-9 ,-:';        # Filter to sanitze input
our $COMPANY = "Acme Hospital";            # Company Name
our $CONTACT_NAME = 'Michael McNamara';        # Contact Name
our $CONTACT_EMAIL = 'user@somewhere.com';    # Contact Email
our $SCRIPT_URL = "http://web.acme.org";     # URL for CSS and Javascript

#########################################################################
#########################################################################
##  M  A  I  N     P  R  O  G  R  A  M
#########################################################################
#########################################################################

# Let's intiailize our program
&startup;

# Let's output the HTML headers
&html_header;

if ( $action eq "add") {

# If the script is being called with parameters let's process them
&runjob;

} elsif ($action eq "list" ) {

# Let's retrieve and output the entire list of MAC addresses
&listjob;

} else {

# If the script isn't being called with parameters let's display the form
&html_form();

}

# Let's output the HTML footers
&html_footer;

exit 0;

Cheers!

]]>
https://blog.michaelfmcnamara.com/2011/11/infoblox-perl-cgi-application/feed/ 10
Infoblox API Perl Modules https://blog.michaelfmcnamara.com/2011/11/infoblox-api-perl-modules/ https://blog.michaelfmcnamara.com/2011/11/infoblox-api-perl-modules/#comments Wed, 09 Nov 2011 23:58:33 +0000 http://blog.michaelfmcnamara.com/?p=2470 We recently migrated from Alcatel-Lucent’s VitalQIP to Infoblox for our IPAM (IP Address Management) solution. I hope to make a more detailed post reviewing Infoblox in the future, for now I’ll stick with the issue of integrating with the API interface. One of our goals for the past few years has been to enable MAC address registration essentially turning off the dynamic nature of DHCP. This would prevent someone from connecting any device to our internal network and getting a DHCP issued IP address. It certainly not a complete solution to the security dilemmas but it would be a good first step.

I do most of my work with CentOS and RedHat Linux because those are the distributions that my organization supports internally (even if I’m one of two people that support Linux across the entire organization). In this case I was working with a CentOS 5.7 server but I was having an issue compiling and installing the Infoblox Perl modules.

LWP::UserAgent version 5.813 required–this is only version 2.033

When I attempted to compile the Infoblox Perl modules I received the following errors;

LWP::UserAgent version 5.813 required--this is only version 2.033 at /usr/lib/perl5/site_perl/5.8.8/Infoblox/Agent.pm line 3.
BEGIN failed--compilation aborted at /usr/lib/perl5/site_perl/5.8.8/Infoblox/Agent.pm line 3.
Compilation failed in require at /usr/lib/perl5/site_perl/5.8.8/Infoblox/Session.pm line 19.
BEGIN failed--compilation aborted at /usr/lib/perl5/site_perl/5.8.8/Infoblox/Session.pm line 19.
Compilation failed in require at /usr/lib/perl5/site_perl/5.8.8/Infoblox.pm line 8.
BEGIN failed--compilation aborted at /usr/lib/perl5/site_perl/5.8.8/Infoblox.pm line 8.
Compilation failed in require at ./ibcli.pl line 78.
BEGIN failed--compilation aborted at ./ibcli.pl line 78.

This was with Perl 5.8.8 on CentOS 5.7 x64, unfortunately it seems this is a known issue with the version of LWP::UserAgent that is currently being distributed via the CentOS repository.

I was able to spin up a new CentOS 6.0 x86 server which was running Perl 5.10.1 and didn’t experience this problem.

The installation was pretty straight forward (except for the issue above) but the API reference manual does a very thorough job of detailing all the possible installation methods on both Windows and Unix/Linux. I just opened a browser to one of the Infoblox appliances and downloaded the Perl modules.

https://10.1.1.1/api/dist/CPAN/authors/id/INFOBLOX/

Just replace the IP address of 10.1.1. with the IP address of your Infoblox appliance. I’m not sure why Infoblox hides their manuals behind their support portal, I just don’t understand why companies do that.  You can find the manual right here, Infoblox_API_Documentation_6.1.0.pdf.

Cheers!

]]>
https://blog.michaelfmcnamara.com/2011/11/infoblox-api-perl-modules/feed/ 7
Cisco Nexus Switch Backups with Perl SNMP https://blog.michaelfmcnamara.com/2010/09/cisco-nexus-switch-backups-perl-snmp/ https://blog.michaelfmcnamara.com/2010/09/cisco-nexus-switch-backups-perl-snmp/#comments Wed, 01 Sep 2010 14:00:22 +0000 http://blog.michaelfmcnamara.com/?p=1602 I’ve spent some time over the past few days trying to get our home grown Perl script designed to backup all our network switches to work with the Cisco Nexus 7010 and 5010 switches.

With previous Cisco switches such as the 6509, 3750, 2960, etc we know that the following commands (when sent via a Perl script using the Net-SNMP Perl module) would instruct the switch to copy it’s running-config to a TFTP server.

snmpset -v1 -c$COMMUNITY $HOST ccCopyProtocol.$RANDOM i 1
snmpset -v1 -c$COMMUNITY $HOST ccCopySourceFileType.$RANDOM i 4
snmpset -v1 -c$COMMUNITY $HOST ccCopyDestFileType.$RANDOM i 1
snmpset -v1 -c$COMMUNITY $HOST ccCopyServerAddress.$RANDOM a "10.1.1.50"
snmpset -v1 -c$COMMUNITY $HOST ccCopyFileName.$RANDOM s "sw-train-acme.cfg"
snmpset -v1 -c$COMMUNITY $HOST ccCopyEntryRowStatus.$RANDOM i 1
sleep 5
snmpget -v1 -c$COMMUNITY $HOST ccCopyState.$RANDOM
#if not successful sleep 3 and re-check ccCopyState else continue and destroy table entry
snmpset -v1 -c$COMMUNITY $HOST ccCopyEntryRowStatus.$RANDOM i 6

I know that the both the Cisco Nexus 7010 and 5010 both balk at the SNMP OIDS/MIBS used above. So I’m searching for a set of equivalent SNMP OIDS/MIBS as those in CISCO-CONFIG-COPY-MIB for NX-OS. I’m not sure that such a OID/MIB even exists for NX-OS but it doesn’t hurt to search and ask.

I’m curious if anyone else has come across this issue? I know that there is an XML interface available but I would prefer to keep using the PERL/SNMP script that I’ve already developed. In the interim I’ll probably write an Expect script (or add some Expect code to my existing Perl script) to remotely connect to the switches and issue the appropriate copy commands.

Cheers!

Updated: Monday June 27, 2011

I’ve finally found the issue and now I’m able to backup the Cisco Nexus switches as expected.
[ad name=”ad-articlefooter”]

]]>
https://blog.michaelfmcnamara.com/2010/09/cisco-nexus-switch-backups-perl-snmp/feed/ 29
ASCII Configuration Generator (ACG) for Nortel Switches https://blog.michaelfmcnamara.com/2008/10/ascii-configuration-generator-acg-for-nortel-switches/ https://blog.michaelfmcnamara.com/2008/10/ascii-configuration-generator-acg-for-nortel-switches/#comments Tue, 28 Oct 2008 02:00:28 +0000 http://blog.michaelfmcnamara.com/?p=478 I wrote a Perl script a long time ago to backup the binary configuration files for all our Nortel Ethernet and Ethernet Routing Switches (including BayStack 350 and 450s, Ethernet Switch 460 and 470s, Ethernet Routing Switch 4500s and Ethernet Routing Switch 5500s, Ethernet Routing Switch 1600 and 8600s along with Motoroal WS5100 and RFS7000s and HP GbE2s). The Perl script was very simple and straightforward. The problem was that the Nortel configuraiton files were binary files that we as engineers were unable to review or analyze. There were no tools (at least not that I’m aware of) that could allow us to review those configurations. If we had a question about the configuration stored in the binary file we had to restore the configuration to a mock up switch(s) in our testlab in order to be able to review the actually configuration. It seems that Nortel finally heard our cries for help and added a new feature in v3.7.x (ES460/ES470) and v5.1.x (ERS5500) software that would allow us to TFTP upload the ASCII configuration from the ACG.

There was one problem though… the SNMP OID has yet to be documented in the Nortel SNMP MIBS. I had to run a packet trace against Nortel’s Device Manager to determine the OID that Device Manager was using to initiate the manual config upload. I found that the OID was ”
1.3.6.1.4.1.45.1.6.4.4.19.0″

I took my existing script and created a new subroutine and had everything working within about 30 minutes.

Here’s some of the code I wrote;

############################################################################
# Subroutine baystack_tftp_config_ascii
#
# Purpose: use SNMP to instruct BayStack switches to TFTP upload their
# ASCII configuration file to the central TFTP server
############################################################################
sub baystack_tftp_config_ascii {

#s5AgSysTftpServerAddress
#s5AgSysAsciiConfigFilename
#s5AgSysAsciiConfigManualUpload (NOT IN THE MIBS) USE 1.3.6.1.4.1.45.1.6.4.4.19.0
# snmpset -v2c -cprivate 10.1.1.100 1.3.6.1.4.1.45.1.6.4.4.19.0 i 4

   # Declare Local Variables
   my $setresult;

   $filename = "ascii/".$filename;

   my $sess = new SNMP::Session (  DestHost  => $snmphost,
                                   Community => $community,
                                   Version   => SNMPVER );

   my $vars = new SNMP::VarList(
                        ['s5AgSysTftpServerAddress', 0, "10.1.1.20",],
                        ['s5AgSysAsciiConfigFilename', 0, $filename,] );

   my $go = new SNMP::VarList(
                        ['.1.3.6.1.4.1.45.1.6.4.4.19', 0, 4, 'INTEGER'] );

   &check_filename($filename);

   # Set TFTP source and destination strings
   $setresult = $sess->set($vars);
   if ( $sess->{ErrorStr} ) {
      print "ERROR: {BayStack} problem setting the TFTP parameters (TFTP IP, FILENAME) for $snmphost\n";
      print "ERROR: {BayStack} sess->{ErrorStr} = $sess->{ErrorStr}\n";
   }

   # Start TFTP copy
   $setresult = $sess->set($go);
   if ( $sess->{ErrorStr} ) {
      print "ERROR: {BayStack} problem setting the TFTP action bit for $snmphost\n";
      print "ERROR: {BayStack} sess->{ErrorStr} = $sess->{ErrorStr}\n";
   }

   # Pause while the TFTP copy completes
   sleep $PAUSE;

   # Check to see if the TFTP copy completed
   $setresult = $sess->get('.1.3.6.1.4.1.45.1.6.4.4.19.0');
   if ( $sess->{ErrorStr} ) {
      print "ERROR: problem checking the TFTP result for $snmphost\n";
      print "ERROR: sess->{ErrorStr} = $sess->{ErrorStr}\n";
   }

   # If TFTP failed output error message
   if ($setresult != 1) {
        while ($setresult == 2) {
           print "DEBUG: config upload status = $setresult (waiting)\n" if (DEBUG);
           sleep $PAUSE;
           $setresult = $sess->get('.1.3.6.1.4.1.45.1.6.4.4.19.0');
        } #end while
   } #end if $test ne "success"

   # If the upload command failed let's try again
   if ($setresult == 3) {

      print "DEBUG: initial command returned $setresult\n" if (DEBUG);
      print "DEBUG: lets try the upload command again\n" if (DEBUG);

      # Let's pause here for a few seconds since the previous command failed
      sleep $PAUSE;

      # Start TFTP copy
      $setresult = $sess->set($go);
      if ( $sess->{ErrorStr} ) {
         print "ERROR: problem setting the TFTP action bit for $snmphost\n";
         print "ERROR: sess->{ErrorStr} = $sess->{ErrorStr}\n";
      }

      # Pause while the TFTP copy completes
      sleep $PAUSE;

      # Check to see if the TFTP copy completed
      $setresult = $sess->get('.1.3.6.1.4.1.45.1.6.4.4.19.0');
         if ( $sess->{ErrorStr} ) {
            print "ERROR: problem checking the TFTP result for $snmphost\n";
            print "ERROR: sess->{ErrorStr} = $sess->{ErrorStr}\n";
      }

      # If TFTP failed output error message
      if ($setresult != 1) {
         while ($setresult == 2) {
            print "DEBUG: config upload status = $setresult (waiting)\n" if (DEBUG);
            sleep $PAUSE;
            $setresult = $sess->get('.1.3.6.1.4.1.45.1.6.4.4.19.0');
         } #end while
      } #end if
   } #end if

   if ($setresult != 1) {
      print "DEBUG: $snmphost config upload *FAILED*!\n";
      print SENDMAIL "ERROR:$snmphost ($sysObjectID) config (ASCII) upload *FAILED*!
\n";
   } elsif ($setresult == 1) {
      print SENDMAIL "$snmphost ($sysObjectID) was successful (ASCII)
\n";
      print "DEBUG: $snmphost ($sysObjectID) was successful (ASCII)\n";
   } else {
      print "DEBUG: unknown error return = $setresult (ASCII)" if (DEBUG);
   } #end if

   print "DEBUG: upload config file results = $setresult (ASCII)\n" if (DEBUG);

   return 1;

} #end sub baystack_tftp_config_ascii

Cheers!
Update: Friday April 10, 2009

I’ve discovered a small issue using the MIB “s5AgSysAsciiConfigFilename”. While the MIB says the value can be between 0..128 characters long I’ve actually found that the value can’t be an longer than 30 characters. If you try to use a filename that is greater than 30 characters you’ll get an SNMP set error. I actually had quite a few switches failing to backup properly because I was prepending “ascii/” to the filename. I tested this with software v3.7.2.x and v3.7.3.x on a Ethernet Switch 470.

s5AgSysAsciiConfigFilename OBJECT-TYPE
        SYNTAX  DisplayString (SIZE (0..128))
        MAX-ACCESS      read-write
        STATUS  current
        DESCRIPTION
                "Name of the ascii configuration file that will be
             downloaded/uploaded either at boot time when the
             s5AgSysAsciiConfigAutoDownload object is set to
             useConfig(3), or when the s5AgSysAsciiConfigDownloadStatus
             object is set to startDownload(4).  When not used, the
             value is a zero length string."
        ::= { s5AgentSystem 6 }

Cheers!

]]>
https://blog.michaelfmcnamara.com/2008/10/ascii-configuration-generator-acg-for-nortel-switches/feed/ 12
Nortel ERS 8600 10GB and MRTG https://blog.michaelfmcnamara.com/2008/07/nortel-ers-8600-10gb-and-mrtg/ Thu, 17 Jul 2008 02:00:33 +0000 http://blog.michaelfmcnamara.com/?p=142 MRTG logoI recently stumbled across a problem in MRTG when I was attempting to graph the utilization of a 10Gb interface on a 8683XLR card within a ERS 8600 switch. I noticed that the MaxBytes value that was listed in the MRTG configuration file was 176258176 (176.3 MBytes/s). While this was obviously incorrect I wondered how that value came to be.

I quickly fired up the trusty snmp-net toolset (snmpget) and confirmed from a CentOS Linux host that the ifSpeed OID was returning 1410065408, which is of course is not the correct value for a 10Gb interface. The problem here is that a 32 bit integer can only hold a maximum value of 2^32 – 1 (4,294,967,295). While I believe other manufacturers will just return the maximum value (4,294,967,295) the Nortel switch appears to return a very odd value. I’m not exactly sure why it’s returning that value, perhaps someone from Nortel can help answer that question.

How to fix it?

The Perl script that builds the configuration files for MRTG, cfgmaker, requires a little editing so that it knows how to react if it receives the odd value of 1410065408 when it polls the ifSpeed OID. Here’s the necessary edit;

223c223
<             if ( (not defined $value) || ($value == 2**32-1)) {
---
>             if ( (not defined $value) || ($value == 2**32-1)  || ($value = 1410065408)  ) {

This edit will cause the configuration script to ignore the ifSpeed value and use the ifHighSpeed value which should return a proper value for MaxBytes of 1250000000 (1250.0 MBytes/s).

A quick word of warning here, you MUST use SNMP v2 with MRTG in order to see the ifHighSpeed OID.

Here’s an example of the paramaters I use when calling the cfgmaker script;

/usr/local/mrtg/bin/cfgmaker --global "PathAdd: /usr/local/rrdtool/bin" --global "LibAdd: /usr/local/rrdtool/lib/perl" --global "WorkDir: /var/www/html/mrtg/mdc" --global "IconDir: /mrtg/" -global "WithPeak[_]: wmy" --global "LogFormat: rrdtool" --ifdesc=descr --no-down --zero-speed=100000000 --snmp-options=:::::2 snmpreadstring@sw-ers8600.dc.acme.org -output=sw-ers8600.dc.acme.org.cfg

The parameter “–snmp-options=:::::2” above tells cfgmaker and MRTG to use SNMP v2 when polling the switch so it can retrieve the 64 bit counters such as ifHCInOctets and ifHCOutOctets as well as ifHighSpeed.

Cheers!

Update: Thursday July 17, 2008

I went back and decided to test the 10GB XFP ports on the Nortel Ethernet Routing Switch 5530. Interestingly enough that switch returns a value of 4294967295 when you query the ifSpeed OID. The output produced by cfgmaker correctly identifies that port as MaxBytes 1250000000 (1250.0 MBytes/s). I may need to call Nortel myself just to satisfy my curiousity on this one.

Cheers!

Update: Wednesday July 23, 2008

It seems that this problem was corrected in software v4.1.5.4 and later for the Ethernet Routing Switch 8600. I was able to confirm that an ERS 8600 switch running v4.1.5.4 does return 4294967295 as expected. Here’s the blurb from the release notes;

MIB value for ifSpeed is now displayed correctly for any 10 Gigabit interface.(Q01174158-01)

Cheers!

]]>
Perl Script to poll ARP Table https://blog.michaelfmcnamara.com/2008/05/perl-script-to-poll-arp-table/ https://blog.michaelfmcnamara.com/2008/05/perl-script-to-poll-arp-table/#comments Mon, 05 May 2008 14:00:00 +0000 http://maddog.mlhs.org/blog/2008/05/perl-script-to-poll-arp-table/ I’ve written a lot of Perl scripts to help make managing the network easier and more efficient. One of the scripts I’ve written allows me to dump the IP ARP table of the Nortel Ethernet Routing Switch 8600 to a file for later/additional processing. While the script was original written for the ERS 8600 switch it will also work on just about any router (Layer 3 device) that supports the RFC1213 (ipNetToMediaNetAddress).

The script has been tested and works on Nortel’s BayRS routers (ARN, ASN, BLN, BCN). You just obviously need to be careful of how the script interprets the ipNetToMediaIfIndex value depending on the device you are polling.

The script get8600arp.pl is a very straight forward script. It simply polls various SNMP OIDs and then stores the results in a file. It does this for every switch (FQDN/IP Address) that is listed in the input file.

#!/usr/bin/perl
#
# Filename: /root/get8600arp.pl
#
# Purpose:  Query Nortel Ethernet Routing Switch 8600 for the IP ARP
#           table via SNMP. This script will poll a list of devices
#           (input file) and dump the contents of the IP ARP table to
#           and outputfile.
#
# Author:   Michael McNamara
#
# Date:     December 5, 2002
#
# Support Switches:
#           - Nortel ERS 8600
#           - Nortel ERS 1600
#           - Nortel ERS 5500
#           - Nortel BayRS Routers
#
# Requirements:
#           - Net-SNMP
#           - Net-SNMP Perl Module
#           - SNMP-MIBS
#
# Changes:
#
#           - May  5, 2007 (M.McNamara)
#           clean up code and documentation for release to public
#           - Oct 10, 2006 (M.McNamara)
#           went back to SNMP v1 to support BayRS legacy routers
#           - Sep 04, 2003 (M.McNamara)
#           migrated from vendor specific MIB to RFC1213 (ipNetToMediaNetAddress)
#

# Load Modules
use strict;
use SNMP;
use Net::Ping;

# Declare constants
#use constant DEBUG      => 0;           # DEBUG settings
use constant RETRIES    => 3;           # SNMP retries
use constant TIMEOUT    => 1000000;     # SNMP timeout, in microseconds
use constant SNMPVER    => 1;           # SNMP version

# SNMP Settings
$SNMP::verbose = 0;
$SNMP::use_enums = 1;
$SNMP::use_sprint_value = 0;
&SNMP::initMib();
&SNMP::loadModules('RAPID-CITY');

# Declaration Variables
my ($sess, @vals);
my @devices;
my ($card, $port);
my $snmphost;
my $comm = "public";        # SNMP ReadOnly Community String
my %array;
my $switchfile;
my $datafile;

our $DEBUG;                     # DEBUG flag

undef @devices;

# Program and help information
my $program = "get8600arp.pl";
my $version = "v1.3";
my $author = "Michael McNamara";
my $purpose = "This Perl script is retreieve the IP ARP table from the ERS8600 Layer 3 switch/router and store it in file for later use.";
my $usage = "Usage: $program \[input\] \[output\] \[-help\] \[debug\]\n    <input>  = filename listing each switch to poll\n    <output> = filename where to store output\n";

if (($#ARGV +1) <= 2) {
 print "Program: $program \nVersion: $version \nWritten by: $author \n$purpose\n\n$usage\n";
 print "DEBUG: ARGV =  $#ARGV\n";
 print "DEBUG: ARGV =  $ARGV[0] $ARGV[1] $ARGV[2] $ARGV[3]\n";
 exit;
}

my $arg1 = shift @ARGV;
my $arg2 = shift @ARGV;
my $arg3 = shift @ARGV;

if ($arg1 =~ /help/) {
 print "Program: $program \nVersion: $version \nWritten by: $author \n$purpose\n\n$usage\n";
 print "DEBUG: ARGV =  @ARGV\n";
 print "DEBUG: ARGV =  $ARGV[0] $ARGV[1] $ARGV[2] $ARGV[3]\n";
 exit;
}

$switchfile = $arg1;
$datafile = $arg2;
$DEBUG = $arg3;

# Test to see if inputifle exists
if (!-e $switchfile) {
 die "ERROR: Unable to locate and/or open inputfile $switchfile...";
}

############################################################################
##### B E G I N   M A I N ##################################################
############################################################################

&load_switches;

&collect_arp;

exit 0;

############################################################################
#### E N D   M A I N #######################################################
############################################################################

############################################################################
# Subroutine collect_arp
#
# Purpose: collect ARP information from layer 3 switches/routers
############################################################################
sub collect_arp {

 # Open output datafile for appending
 open(DATAFILE, ">>$datafile");

 # Loop over each Passport 8600 switch
 foreach $snmphost (@devices) {

    my $packet = Net::Ping->new('icmp');

    $snmphost =~ s/\n//g;        # remove CRLF

    if ($packet->ping($snmphost)) {

       $sess = new SNMP::Session (    DestHost   =>  $snmphost,
                              Community  =>  $comm,
                              Retry      =>  RETRIES,
                              Timeout    =>  TIMEOUT,
                              Version    =>  SNMPVER );

       my $vars = new SNMP::VarList(
                              ['ipNetToMediaIfIndex', 0],
                              ['ipNetToMediaPhysAddress', 0],
                              ['ipNetToMediaNetAddress', 0],
                              ['ipNetToMediaType', 0] );

       while (1) {

          @vals = $sess->getnext($vars);  # retreive SNMP information

          last unless ($vars->[0]->tag eq 'ipNetToMediaIfIndex');

          $vals[1] = unpack('H12', $vals[1]);
          $vals[1] =~ tr/a-z/A-Z/;

          $card = (($vals[0] & 62914560) / 4194304);
          $port = (($vals[0] & 4128768) / 65536) + 1;

          print "$snmphost, $vals[0], ($card/$port), $vals[1], $vals[2], $vals[3]\n" if ($DEBUG);
          print DATAFILE "$snmphost, $vals[0], $card, $port, $vals[1], $vals[2]\n";

          $array{$snmphost}[$card][$port] = $vals[2];

       } # end while

    } else {

       print ("ERROR: $snmphost not responding to ICMP ping skipping...\n");

    } #end if $packet

 } #end foreach

 close(DATAFILE);

} #end sub collect_arp

############################################################################
# Subroutine load_switches
#
# Purpose: load list of switches
############################################################################
sub load_switches {

 open(SWITCHLIST, "<$switchfile");

 # Walk through data file
 while (<SWITCHLIST>) {

    # Skip blank lines
    next if (/^\n$/);
    # Skip comments
    next if (/^#/);

    #print "DEBUG: adding $_ to our list of devices \n" if ($DEBUG);

    push (@devices, $_);

 }

 close(SWITCHLIST);

 return 1;

} # end sub load_switches
############################################################################

The real magic that folks have always been searching for is the binary formula to turn the ipNetToMediaIfIndex into a location that denotes the card and port where that specific device is connected to.

$card = (($vals[0] & 62914560) / 4194304);
$port = (($vals[0] & 4128768) / 65536) + 1;

While I still use flat files you could certainly adopt this code to dump the output into a database. I just haven’t had the time although I’ve been playing with MySQL quite a bit lately.

Cheers!

]]>
https://blog.michaelfmcnamara.com/2008/05/perl-script-to-poll-arp-table/feed/ 7
WiFi Hotspot Portal https://blog.michaelfmcnamara.com/2007/11/wifi-hotspot-portal/ https://blog.michaelfmcnamara.com/2007/11/wifi-hotspot-portal/#comments Tue, 20 Nov 2007 01:00:00 +0000 http://maddog.mlhs.org/blog/2007/11/wifi-hotspot-portal/ A few years ago I had a request to design a public WiFi hotspot portal for the patients and visitors within our five major facilities. I did a fair amount of research and found a number of interesting commercial and open-source solutions. Unfortunately none of them really filled our requirements or caught my fancy. So I embarked on building/coding our own solution using a wide array of open-source software that was already available. Since I was most familiar with Perl at the time I chose to code the solution using Perl and Javascript (browser side) using Linux as the operating system of choice.

I needed to provide a public WiFi hotspot across our existing corporate wireless infrastructure at our five major sites. It obviously needed to be secure from our internal network, it needed to be 100% automated (there were no resources available to support this offering) and it needed to work (there’s a surprise requirement). We also needed to keep internal (corporate) laptops and wireless devices from connecting to the unencrypted network and circumventing current Internet access policies.

Because of security concerns I decided to only allow HTTP (TCP 80) and HTTPS (TCP 443) traffic from the public wireless network. I also tabled any ideas of content/URL filtering from the original design. Instead we would reliable on Blue Coat ProxySG/ProxyAV appliances and Websense to perform content filtering and AV scanning of the traffic in a later upgrade.

How did we do it?
We carved out an ESSID (“public”) from our Motorola Wireless LAN infrastructure at each facility. We setup the wireless network without any encryption or security so as to minimize any end-user difficulties in connecting to the wireless network. We took CentOS and built a WiFi portal server/gateway/firewall/router using an HP Proliant DL360. We essentially turned our Linux server into a cheap and very efficient firewall/gateway for the WiFi Hotspot. We connected one NIC of the Linux server to the wireless WLAN and the other to our internal network. This allowed use to use the Linux server to provide IP addresses to the wireless devices through DHCP. It also allowed use to have the Linux server provide DNS for name resolution. And most importantly it allowed use to use IPtables to provide firewalling between the wireless network and our internal network. This solution also allowed us to implement bandwidth shaping/throttling to prevent the public WiFi Hotspot wireless users from utilizing too much of our Internet link (DS-3 ~ 45Mbps).

Once a device associates with the wireless network the Linux portal server will issue the device a DHCP address from the 192.168.16.0/20 network. When the user opens their web browser they will be redirected to the Linux portal web server and the registration page as it appears below;

Once the user clicks on the “I AGREE” button the Linux server will kick off the “register.pl” script to check the IP/MAC address and decide if they should be granted access. If they are granted access they will be redirected to our Internet homepage after which they’ll be free to surf to any URL. If the user is denied access they will be directed to an error page.

It is also possible that the user may attempt to register multiple times due to their web browser caching the portal page contents as the contents of a legitimate Internet website. Example: A user opens their web browser to www.cnn.com and is greeted with the portal page. User registers that is then re-directed to www.acme.org. The user then types www.cnn.com back into the browser address bar, but instead of getting the legit content for the CNN website the user is greeted again by the portal page. The user not knowing any better clicks the “I AGREE” button for the second time in as many minutes. Previously this problem would have gone on and on over and over, now the system will detect that the user is already registered and will through an error alerting the user to “refresh” their web browser. In order to refresh the browser the user should just type in the URL of the website they are attempting to visit and click “Go” (or hit “enter”). If they are greeted with the portal page they should click the “refresh” button from the browser button bar. That will instruct the web browser to ignore any cached content and attempt to retrieve all the data direct from the source website.

Every night at midnight the firewall rules will be reset to the defaults. Requiring any that wishes to access the WiFi Hotspot to agree to the AUP again. This is done to prevent folks from continually sitting/camping on the WiFi Hotspot.

Initially I thought we might be able to use a VPN or GRE tunnel to connect the five public WLANs to a single Linux server. Unfortunately I was a little ahead of the times and VPN/GRE tunnels were just starting to be supported in the various wireless switches (Motorola in this case). So I decided to take an easier approach and installed five HP Prolaint DL360 servers, one for each site.

I’m very happy to report that the solution works very well and virtually supports itself.

The only issue that we’ve seen is the need to continually update the blacklist file to keep corporate wireless devices from connecting to the public network. Thankfully I’ve written a small Bash Shell script to help with that process.

I hope to write a more detailed account of how to set this up on my website sometime in the future. If your interested in hearing more or have questions please drop me a line.

Cheers!

]]>
https://blog.michaelfmcnamara.com/2007/11/wifi-hotspot-portal/feed/ 1
Perl Scripting https://blog.michaelfmcnamara.com/2007/10/perl-scripting/ https://blog.michaelfmcnamara.com/2007/10/perl-scripting/#comments Sat, 27 Oct 2007 17:50:00 +0000 http://maddog.mlhs.org/blog/2007/10/perl-scripting/ I really like using Perl because of the Net-SNMP Perl libraries that make it really easy to write code to interact with devices that support SNMP.

Hopefully everyone out there is backing up their network switch configurations in the unlikely event that if their hardware dies they only need to worry about replacing the hardware and not about re-configuring the entire switch.

Quite a few years back I wrote a Perl script that would send the proper SNMP commands to instruct a network switch to copy it’s configuration to a TFTP server. This script essentially became known as “switchtftpbackup.pl” It’s nothing fancy or pretty but it gets the job done.

I’ve posted this Perl script on my webiste under the Perl section.

I run this script from Cron one of our CentOS Linux servers at work every week. The same server also acts as a central TFTP server for the entire organization. I also run other scripts that then archive the weekly backups, in the event that I need to go to a backup that’s more than a week old.

I believe both Nortel’s Optivity NMS and Cisco’s Cisco Works both have options to backup switch configurations these days.

What are you using?

Cheers!

]]>
https://blog.michaelfmcnamara.com/2007/10/perl-scripting/feed/ 11