Quick and Dirty Cisco Hardware Inventory to CSV

I wanted to quickly grab all part numbers and serial numbers of chassis, cards and modules in some Cisco kit but the standard output wasn’t really nice enough to work with very easily.

To this end I knocked up the following script that will give a nice fixed field or comma-separated output that can be dragged into an Excel spreadsheet. Useful for doing support renewals and the like.

This is the first version so it’s a bit rough with error checking and doesn’t like incorrect credentials so type carefully. Tested with a variety of Cisco switches and routers, including Nexus 5K. Requires expect/tcl/tk

If you have ancient hardware that only supports prehistoric ssh key exchange algorithms, this may fail under expect. Good luck fixing it :)

$ ./hwaudit -d switch1
Tacacs user:me
Tacacs pass:
Using command line specified hosts: switch1
Device        Name                  Desc                  PID                 VID      Serial
switch1     "1"                     "WS-C3560-24TS"       WS-C3560-24TS-E     V02      CAT0XXXXXXX
switch1     "GigabitEthernet0/1"    "1000BaseSX SFP"      Unspecified                  AGMXXXXXX3P

$ ./hwaudit -cd switch2
CSV Mode
Tacacs user:me
Tacacs pass:
Using command line specified hosts: switch1
Device,Name,Desc,PID,VID,Serial
switch2,"1","ME-C3750-24TE",ME-C3750-24TE-M,V05,CATXXXXXXXX
switch2,"GigabitEthernet1/0/1","1000BaseSX SFP",Unspecified,,AGMXXXXXXX
switch2,"GigabitEthernet1/0/2","1000BaseSX SFP",Unspecified,,FNSXXXXXXX

Output is in combined_audit.txt

BASH Script:

#!/bin/bash
# hwaudit v1.1
# Mark M (sol@subnetzero.org)
#
# Script to go and get part numbers/serial numbers
# from Cisco devices.
#
# To do: write additional methods for telnet_acs and telnet_pwonly
#
# Examples:
# ./hwaudit -cd device1    <- CSV delimited output for device1
# ./hwaudit -f list.txt    <- Fixed field output for all in list.txt
#
# Expect functions to log on to Cisco Devices
ssh_acs() {
/usr/bin/expect << EOF
set timeout $PROMPT_TIMEOUT
spawn ssh $USNAME@$DEVICE
expect {
        "continue connecting (yes/no)?" {send "yes\r" ; exp_continue}
        "assword:" { send "$PASS\r" }
}
expect -re "(>|#)" {send "term len 0\r"}
expect -re "(>|#)" {send "show inventory\r"}
expect -re "(>$|#$)" {send "exit\r\n"}
close $spawn_id
EOF
}

PROMPT_TIMEOUT=5
USNAME=""
PASS=""
DEVICE=""
METHOD=ssh_acs
USAGE="$0 { -f filename } { -c = csv format } { -d devicename }"
FMT="%-13.13s %-32.32s %-38.38s %-22.22s %-8.8s %-12.12s\n"
CSV=0
cat /dev/null > combined_audit.txt

case $1 in
 [a-zA-Z0-9]*) echo "$USAGE\n"
               exit 1;;
esac

optstring=cd:f: #tT
while getopts $optstring opt
do
   case $opt in
      c) echo "CSV Mode";FMT="%s,%s,%s,%s,%s,%s\n";CSV=1;;
      d) DEVICES=$OPTARG;;
      f) INPUTFILE=$OPTARG;;
      # t) METHOD=telnet_acs;;
      # T) METHOD=telnet_pwonly;;
      *) echo "$USAGE"
   esac
done

case $METHOD in
   ssh_acs)   printf "Tacacs user:" ; read USNAME; stty -echo
              printf "Tacacs pass:" ; read PASS ; printf "\n"; stty echo
              ;;
   *)        printf "No connection method set\n"
             exit 1
             ;;
esac

if [ ! -r $INPUTFILE ]; then
   echo "Input file: $INPUTFILE not readable"
   exit 1
fi

if [ -z "$DEVICES" ]; then
   DEVICES=$( grep -iv "^$|^#" "$INPUTFILE" )
   if [ -z "$DEVICES" ]; then
      echo "No list generated. $INPUTFILE"
      exit 1
   fi
else echo "Using command line specified hosts: $DEVICES"

fi

if [ $(tput cols) -lt 128 ] && [ "$CSV" -eq "0" ]; then
   printf "\033[31;1mWARNING - \033[37mTerminal may not be wide enough for clean output in non-csv mode.\033[0m\n"
   sleep 2
fi

for DEVICE in ${DEVICES[@]}; do
   $METHOD | tr -d '\r' > output.$DEVICE.tmp
   sed 's/  \+,/,/g' output.$DEVICE.tmp | \
   awk -F "," 'BEGIN {start=0;printf("'"$FMT"'","Device","Name","Desc","PID","VID","Serial")}
               /show inventory/ { start=1 }
               start==0 { next }
               start==1 && $1 ~ /^NAME:/ {name=substr($1,7)}
               start==1 && $2 ~ /DESCR:/ {desc=substr($2,9)}
               start==1 && $1 ~ /^PID:/  {pid=substr($1,6)}
               start==1 && $2 ~ /VID:/   {vid=substr($2,7)}
               start==1 && $3 ~ /SN:/    {ser=substr($3,6)}
               $1 ~ /^$/ {printf ("'"$FMT"'","'$DEVICE'",name,desc,pid,vid,ser)
               name=NULL;desc=NULL;pid=NULL;vid=NULL;ser=NULL} ' |\
               egrep -v ",,,,," | tee -a combined_audit.txt
done

echo "Output is in combined_audit.txt"
rm output.*.tmp

How to do static lookups with Splunk and a CSV file

The documentation for the lookup function on Splunk’s website proved to be immensely confusing and did me no favours at all. A few hours of trial and error later, I figured it out. This example will use a csv file that translates IP Protocol to Names intended for use with an SRX app I’m working on.

Procedure

Create a csv file with headers. Bear in mind that the key you want to look up against (the first field) MUST be an existing field name in Splunk. At the time I had a field extraction called srx_protocol_id so the first field is labelled as such. Be careful of calling your lookups and primary keys by the same name, otherwise it gets confusing.

ip-protocol-numbers.csv (truncated)

srx_protocol_id,protocol_name
0,HOPOPT
1,ICMP
2,IGMP
3,GGP
4,IPv4
5,ST
6,TCP
7,CBT
8,EGP
9,IGP
10,BBN-RCC-MON
11,NVP-II
12,PUP
13,ARGUS
14,EMCON
15,XNET
16,CHAOS
17,UDP

First of all, select App > Your App from the top menu.

Go to Manager > Lookups > Lookup Table Files > New

Select the relevant App, select the file with [ Choose File ] and give the same filename, eg: ip-protocol-numbers.csv

Once done, your path may be an admin path, so edit the permissions of the file you uploaded to be “This app only” and select Read for Everyone and write for admin and power. The path should change to

home/splunk/opt/splunk/etc/apps/[APPNAME]/lookups/ip-protocol-numbers.csv

Create a protocol definition.

Destination App: Your App
Name: ip-protocols
Type: File based
Lookup file: ip-protocol-numbers.csv
[X] Advanced options

Minimum matches: 1
Maximum matches: 1
Default matches: NA

The above settings are chosen because we only expect 1 name per protocol ID, and if it doesn’t match, we want NA to be returned. If the Lookup file you want isn’t showing up and your permissions are OK, you may need to restart splunk.

Searching
Now I can look up the protocol name in the following search:

RT_FLOW_SESSION | dedup 1 host,srx_sess_id keepempty=true | top limit=0 srx_protocol_id  | lookup ip-protocols srx_protocol_id | fields - srx_protocol_id percent | rename protocol_name AS "Protocol"

By default, a lookup will return all fields that match the primary key. If you have several fields you can modify your search with the following to restrict the fields you want output.

lookup [lookup-name] [search field name] OUTPUT [field name in csv file]