#!/usr/local/bin/expect
# Written By  : Kynan Dent
# Written On  : 23/07/2004
# Function    : Loop through all cisco devices in the environment and 
#               retrieve config information (wri t).
# Version     : 1.0

###############################################################################
# proc        : msg
# Function    : Print a message to the user if the quiet option is off
proc msg { message } {
  global quiet_mode
  if { quiet_mode == false }  {
    send_user "$message\r\n"
  }
}
###############################################################################
# proc        : cyclades login
# Function    : perform initial login to cyclades
# Returns     : true if login was successful
#               false if login failed
 
proc cyclades_login { device-cycladesIP } {
  global SPAWN_ID SSH_BIN
  
  # set a longish timeout to deal with network lag
  set timeout 10
  # each device is accessed via a cyclades so pull the string apart
  set device [lindex [split ${device-cycladesIP} "-" ] 0]
  set IP [lindex [split ${device-cycladesIP} "-" ] 1]
  send_user "Contacting cylades to log in to ${device}...\r\n"
  # log on to the cyclades
  if [catch "spawn $SSH_BIN admin:$device@10.0.0.$IP" spawn_err ] {
    send_user "spawn error : $spawn_err\r\n"
    return false
  } else {
    set SPAWN_ID $spawn_id
    # there are four possibilities to deal with here:
    # 1. ssh key needs to be accepted
    # 2. a password prompt
    # 3. timeout - nothing happens
    # 4. ssh quits because of problems
    
    expect {
      eof {
        send_user "process died\r\n"
        return false
      }
      "connecting (yes/no)?" {
        send "yes\r"
        exp_continue
      }
      "assword:" {
        send "LOGIN_PASSWORD_GOES_HERE\r"
      }
      timeout {
        send_user "Timeout occurred trying to connect to 10.0.0.$IP!\r\n"
        return false
      }
    }
  }
  # once we're in, the cyclades will try to contact the port of the unit we 
  # requested.  This can cause two possible responses
  # 1. The port is already in use
  # 2. It connects.  If this is the case then NOTHING happens.  A newline
  #    is required to "wake" the port.  To deal with this we wait for 3 seconds
  #    which should be ample, and assume a connection was made.
  set timeout 3
  expect {
    -re "(ttyS\[0-9]*) is being used by" {
      send_user "Port $expect_out(1,string) is already in use!\r\n"
      close
      return false
    }
    timeout {
      send "\r"
      return true
    }
  }
}

###############################################################################
# proc        : cyclades_logout
# Function    : clean logout from cyclades
# Returns     : true if logout was successful
#               false if clean logout failed

proc cyclades_logout {} {
  global SPAWN_ID
  set spawn_id $SPAWN_ID
  set timeout 3
  
  # send the cyclades kill sequence (~.) and WAIT for the connection to 
  # actaully close before killing the process otherwise the port stays locked
  send "~."
  expect {
    -re "Connection.*closed" {
      close
      return true
    }
    timeout {
      return false
    }
  }
}

###############################################################################
# proc        : switch_login
# Function    : This function logs on to a switch and enables it
# Returns     : true if login was successful
#               false if login failed

proc switch_login {} {
  global SPAWN_ID
  set spawn_id $SPAWN_ID

  # login - there should be a "Username:" prompt waiting for us but if someone
  # left the switch logged in then it could be anything from the device_name> prompt
  # to device_name# to any command that was running.  If it is not the username 
  # prompt then best just report it and leave it alone.
  expect {
    "Username:" {
      send "USERNAME_GOES_HERE\r"
      expect "Password:"
      send "SWITCH_PASSWORD_GOES_HERE\r"
      # wait for the prompt and then enable
      expect ">"
      send "en\r"
      expect "Password:"
      send "ENABLE_PASSWORD_GOES_HERE\r"
      expect "#"
      return true
    }
    timeout {
      return false
    }
  }
}
###############################################################################
# proc        : switch_logout
# Function    : This function logs out of a switch
# Returns     : true if logout was successful
#               false if login failed

proc switch_logout {} {
  global SPAWN_ID
  set spawn_id $SPAWN_ID

  # logout - quit out of IOS
  send "quit\r"
  expect "Press RETURN to get started."
}
###############################################################################
# proc        : fetch_config
# Function    : This function runs the IOS "wri t" command and logs all output
#               to a file named after the device we are logged into
# Returns     : true if logout was successful
#               false if login failed

proc fetch_config { device-cycladesIP } {
  global SPAWN_ID
  set spawn_id $SPAWN_ID
  
  set device [lindex [split ${device-cycladesIP} "-" ] 0]
  
  # fetch the config
  send_user "Fetching config...\r\n"
  send "wri t\r"
  #send "sho int\r"
  set config ""
  expect {
    -re "(\[^\r]*)\r\n" {
      # the line can now be collected and stored, we'll write it to file later
      append config "$expect_out(1,string)\n"
      exp_continue
    }
    "More" {
      # this is the page break, hit space for another page
      send " "
      # after the space the switch sends a bunch of backspaces to clean out
      # the -- More -- prompt.  The next expect cleans them out of the output
      expect -re "^.*\x008"
      exp_continue
    }
    -re "^${device}#" {
      # now save the config to file
      set FILE [ open "${device}.config" "w" ]
      puts $FILE $config
      close $FILE      
    }
    timeout {
        send_user "timeout waiting for ^$device#\r\n"
        send_user "$expect_out(0,string)"
      }
  }
}

###############################################################################
#                          __  __    _    ___ _   _ 
#                         |  \/  |  / \  |_ _| \ | |
#                         | |\/| | / _ \  | ||  \| |
#                         | |  | |/ ___ \ | || |\  |
#                         |_|  |_/_/   \_\___|_| \_|
# init
# hide program interaction from user
log_user 0
# check the command line
set argc [ llength $argv ]
if { $argc != 0 } {
  # find out what came in
}


# setup a few localisation things
set SSH_BIN "/opt/local/bin/ssh"
#set SSH_BIN "/usr/bin/ssh"

# setup switch list.  The list is the device number and the last octet of the 
# cyclades IP.  For instance, device 203 is on cyclades 10.0.0.2 so its' list
# entry is 203-2.

set cisco_devices [ list 203-2 \
                         204-1 \
                         308-2 \
                         309-1 \
                         311-2 \
                         312-2 \
                         314-1 \
                         403-2 \
                         404-1 \
                         409-1 \
                         412-1 \
                         500-2 \
                         502-1 \
                         504-1 \
                         505-1 \
                         600-1 \
                         602-2 \
                         604-1 \
                         605-1 ]


# make SPAWN_ID global, we'll only ever deal with a single process at a time
global SPAWN_ID

# loop through each device and try to login
foreach thing $cisco_devices {
  if [ cyclades_login $thing ] {
    # logged in!   
    switch_login
    fetch_config $thing
    switch_logout
    if [ cyclades_logout ] {
        send_user "Logged out clean!\r\n"
      } else {
        send_user "Log out failed!!\r\n"
    }
  } else {
    # cyclades_login failed - user has already been notified    
  }
}