Question

I have an interest in using node-suppose instead of expect. I use expect sometimes but i get frustrated trying to deal with different conditions. For example, I use an expect script to change my passwords on a group of servers before my passwords expire.

Some servers might prompt me to change my password automatically, some might require me to run the passwd command, some might disconnect the session remotely immediately following the password change, some don't.

The way I deal with this in expect is ugly and doesn't work correctly 100% of the time.

So basically I am trying to find a way to properly use 'if' statements with suppose

The readme page (https://github.com/jprichardson/node-suppose/blob/master/README.md) doesn't really show how I would do this and I can't find any other examples. Here is the example in the README.md on github.

var suppose = require('suppose')
  , fs = require('fs')
  , assert = require('assert')

process.chdir('/tmp/awesome');
fs.writeFileSync('/tmp/awesome/README.md', 'READ IT')
suppose('npm', ['init'])
  .debug(fs.createWriteStream('/tmp/debug.txt')) //optional writeable output stream
  .on(/name\: \([\w|\-]+\)[\s]*/).respond('awesome_package\n')
  .on('version: (0.0.0) ').respond('0.0.1\n')
  .on('description: ').respond("It's an awesome package man!\n")
  .on('entry point: (index.js) ').respond("\n")
  .on('test command: ').respond('npm test\n')
  .on('git repository: ').respond("\n")
  .on('keywords: ').respond('awesome, cool\n')
  .on('author: ').respond('JP Richardson\n')
  .on('license: (BSD) ').respond('MIT\n')
  .on('ok? (yes) ' ).respond('yes\n')
.error(function(err){
  console.log(err.message);
})
.end(function(code){
  var packageFile = '/tmp/awesome/package.json';
  fs.readFile(packageFile, function(err, data){
    var packageObj = JSON.parse(data.toString());
    console.log(packageObj.name); //'awesome_package'
  })
})

EDIT: I'm including an expect script I use. This only works on AIX servers. I tried to make a script that did both AIX and Linux servers but I got frustrated because I couldn't figure out how to handle when the server closes the connection. It would completely stop the script instead of moving on to the next server in the list. Apparently I was so frustrated I deleted it because I can't find it anymore.

But in this other script, you can see how I am trying to make case like scenarios but I end up having to repeat code over and over because I can't make functions, even a goto would make this easier. And I have to store passwords in environment variables. Like I said, its ugly. I would much rather spend time working on more modern stuff than TCL.

#!/usr/bin/expect -f
#

# set some variables
set timeout 5
set ::logfile [open ./AIX_password_update.log a]
set stamp [clock format [clock seconds] -format {%Y-%m-%d-%T}]


#start task
puts "\n\n\n"
puts "$stamp\nStarting!\nLog file AIX_password_update.log"
puts "\n\n"

#prepare host list for processing
set fd [open $env(HOSTLIST) r]
set hosts [read -nonewline $fd]
close $fd

foreach host [split $hosts "\n" ] {

#connect to host and start sending commands
    spawn /usr/bin/ssh $env(LOGINID)@$host

    set stamp [clock format [clock seconds] -format {%Y-%m-%d-%T}]
    puts "\n############################"
    puts "## Connecting to $host ##"
    puts "############################"

#deal with SSH client and log in if needed
    expect {
        "yes/no" {
            send "yes\r"
            expect {
                "password:" {
                    send "$env(PASSWDU)\r"
                    expect {
                        "ermission denied" {
                            puts "Permission Denied!!!\nmoving on to the next host in the list\n"
                            puts $::logfile "$stamp - $host - Permission denied, wrong password for $env(LOGINID)"
                            continue
                        }
                    }
                }
            }
        }
    }
#deal with SSH/DNS/Network problems and log in if needed
    expect {
        "ssh: Could not resolve hostname" {
        puts "Could not resolve hostname!!!\nmoving to the next host in the list\n"
        puts $::logfile "$stamp - $host - Could not resolve hostname"
        continue
        }
        timeout {
        puts "Request timed out!!!\nmoving to the next host in the list\n"
        puts $::logfile "$stamp - $host - Request timed out"
        continue
        }
        "password:" {
            send "$env(PASSWDU)\r"
                expect {
                    "ermission denied" {
                        puts "Permission Denied!!!\nmoving on to the next host in the list\n"
                        puts $::logfile "$stamp - $host - Permission denied, wrong password for $env(LOGINID)"
                        continue
                    }
                }
        }
        "\\$" {
        }

    }

#make sure this is an AIX server, skip if it is not
    send "uname\r"
    expect {
        "Linux" {
                        set uname Linux
        }
        "AIX" {
                        set uname AIX
        }
    }
    if { $uname != "AIX" } {
        puts "This server is not AIX!!!\nmoving on to the next host in the list"
        puts $::logfile "$stamp - $host - Not AIX"
        continue
    }
#su to root
    expect {
      "\\$" {
        send "su -\r"
        expect {
            "assword:" {
                send "$env(ROOTPW)\r"
                expect eof
                puts $::logfile "$stamp - $host - su to root success"
            }
        }
      }
    }
#detect root prompt and change the password for the user
    expect {
        "#" {
            send "\r"
            send "passwd $env(USERID)\r"
            expect {
                "New password:" {
                    send "$env(PASSWDN)\r"
                    expect {
                        "Re-enter" {
                            send "$env(PASSWDN)\r"
                            expect eof
                            send "pwdadm -c $env(USERID)\r"
                            expect eof
                            puts $::logfile "$stamp - $host - Password changed for $env(USERID)"
                            puts "$stamp - $host - Password changed for $env(USERID)"
                        }
                        "again" {
                            send "$env(PASSWDN)\r"
                            expect eof
                            send "pwdadm -c $env(USERID)\r"
                            expect eof
                            puts $::logfile "$stamp - $host - Password changed for $env(USERID)"
                            puts "$stamp - $host - Password changed for $env(USERID)"
                        }
                    }
                }
            }
        }
        "root" {
            send "\r"
            send "passwd $env(USERID)\r"
            expect {
                "New password:" {
                    send "$env(PASSWDN)\r"
                    expect {
                        "Re-enter" {
                            send "$env(PASSWDN)\r"
                            expect eof
                            send "pwdadm -c $env(USERID)\r"
                            expect eof
                            puts $::logfile "$stamp - $host - Password changed for $env(USERID)"
                            puts "$stamp - $host - Password changed for $env(USERID)"
                        }
                        "again" {
                            send "$env(PASSWDN)\r"
                            expect eof
                            send "pwdadm -c $env(USERID)\r"
                            expect eof
                            puts $::logfile "$stamp - $host - Password changed for $env(USERID)"
                            puts "$stamp - $host - Password changed for $env(USERID)"
                        }
                    }
                }
            }
        }
    }
expect eof
}
expect eof
Was it helpful?

Solution

Author of node-suppose here...

Unfortunately, node-suppose doesn't support conditions. My comparison to expect may have been a poor comparison since it doesn't support a lot of expect features. However, in the future it may support conditions. I did post an issue here in to reference this: https://github.com/jprichardson/node-suppose/issues/7

OTHER TIPS

Here is a simplified script for the simple case where you log in as the LOGINID user and call "passwd" for the USERID user. From your script, I could not grok the situations where you would have to use "su -" or "pwdadm"

The use of exp_continue reduces much code repitition. exp_continue essentially loops within the expect command so that you can wait for another pattern to appear.

#!/user/bin/expect -f

set timeout 5
set fd [open $env(HOSTLIST) r]
while {[gets $fd host] != -1} {
    puts ""
    puts "################################################"
    puts "# CONNECTING TO $host #"
    puts "################################################"

    spawn /usr/bin/ssh $env(LOGINID)@$host

    expect {
        "yes/no" {
            send "yes\r"
            exp_continue
        }
        "assword:" {
            send "$env(PASSWDU)\r"
            exp_continue
        }
        "ssh: Could not resolve hostname" {
            puts "Could not resolve hostname!!!\nmoving to the next host in the list\n"
            continue
        }
        timeout {
            puts "Request timed out!!!\nmoving to the next host in the list\n"
            continue
        }
        -re {\$\s*$}
    }

    send "passwd $env(USERID)\r"

    expect {
        "Old password:" {
            send "$env(PASSWDU)\r"
            exp_continue
        }
        -re "New password:|again:|Re-enter" {
            send "$env(PASSWDTEMP)\r"
            exp_continue
        }
        -re {\$\s*$}
    }

    send "exit\r"
    expect eof
}

If you're already using node.js, you should look into using ssh2 instead of using an expect-like script for connecting to an ssh server.

EDIT: Also, you should consider using public key authentication instead of passwords, whether that's using a key directly or through an agent like ssh-agent.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top