Search code examples
sshtclexpectcredentialscisco

Cycle through multiple possible credentials TCL Expect


I wrote a script to login via ssh and run some commands. Half of the devices on list my device list have a different acceptable username and password combination.

I want expect to be able to interpret the failed login, start a new login attempt with the same device, but use different login credentials on the second attempt (there are two possible username and password combinations)

If anyone can point me in the right direction I'd really appreciate it. This is my first time using TCL or expect.

A condensed version of what I have so far is below.

set username "someusername"
set password "somepassword"

set devices "X.X.X.X"

foreach device $devices {
    puts "Processing device: $device";
    spawn plink  -ssh $devices 

    expect "Store key in cache"
    send "y\r"

    expect "login as:"
    send "$username\r"

    expect "password:"
    send "$password\r"
    expect "#"

send "conf t\r"
expect "(config)#"

send "some commands\r"
expect "(config)#"

send "end\r"
expect "#"

send "copy run startup-config\r"
expect "#"

Solution

  • You probably need something like

    set usernames [list name1 name2]
    set passwords [list pw1   pw2  ]
    
    set devices [list X.X.X.X ...]
    
    foreach device $devices {
        puts "Processing device: $device";
        spawn plink  -ssh $devices 
    
        set auth_idx -1
    
        expect {
            "Store key in cache" {send "y\r"; exp_continue}
    
            "login as:" {
                incr auth_idx
                if {$auth_idx == [llength $usernames]} {
                    puts "login failed for $device"
    
                    # close the plink connection. I'm guessing here
                    send \003       ;# Ctrl-C
                    expect eof
    
                    # and on to the next device
                    continue
                }
    
                send "[lindex $usernames $auth_idx]\r"
                expect "password:"
                send "[lindex $passwords $auth_idx]\r"
    
                # and wait for either the command prompt or another login prompt
                exp_continue
            }
    
            "#"
        }
    
        # ... whatever you do when you're logged in
    
        # and logout. something like
        send "quit\r"
        expect eof
    }
    

    This uses exp_continue to "loop" within the same expect statement. I assume that you may not always have to store the key in the cache. I also assume that you see "login as" if the first login attempt fails


    UPDATE

    set usernames [list name1 name2]
    set passwords [list pw1   pw2  ]
    
    set devices [list X.X.X.X ...]
    
    set device_idx 0
    set auth_idx 0
    
    while {$device_idx < [llength $devices]} {
        set device [lindex $devices $device_idx]
    
        puts "Processing device: $device";
        spawn plink  -ssh $devices 
    
        expect {
            "Store key in cache" {send "y\r"; exp_continue}
    
            "login as:" {
                send "[lindex $usernames $auth_idx]\r"
                expect "password:"
                send "[lindex $passwords $auth_idx]\r"
                exp_continue
            }
    
            "Access denied" {
                incr auth_idx
                if {$auth_idx == [llength $usernames]} {
                    puts "login failed for $device"
                    # next device
                    set auth_idx 0
                    incr device_idx
                } else {
                    # close the plink connection. I'm guessing here
                    send \003       ;# Ctrl-C
                    expect eof
                    # re-do with current device
                }
                continue
            }
    
            "#"
        }
    
        # ... whatever you do when you're logged in
    
        # and logout. something like
        send "quit\r"
        expect eof
    
        # and on to the next device
        set auth_idx 0
        incr device_idx
    }