Platinum Solutions Corporate Website


Updating Multiple Systems in One Step

The answer you entered to the math problem is incorrect.

I just got back from a trip where our team had to install a bunch of machines. There were fifteen machines total, but with the Solaris zones, we had twenty-eight total operating systems to work on. And on each of these systems, we had a number of things we had to do as we prepared them for production use -- create user accounts, disable unneeded services, copy some configuration files in place, set up some cron jobs... multiply that by twenty-eight, and it wasn't long before I was very, very tired of typing the same login and password.

It seemed to me that these very repetitive tasks were something that should be automatable. Then I remembered a tool I used years ago that might very well be just the trick.

Expect is an automation tool that automates interactive applications. The concept is really simple -- you tell it to send some data, and it expects a response. Expect has been around for ages: 1990, back when the command line was king.

Setting up a basic Expect script is pretty simple, since you can just copy the sample scripts or code chunks from the man page. If you want to do something like, say, updating multiple systems by reading lists of IP addresses from a file, reading arguments from a command line, and prompting for passwords and what commands to run (all of which I did), you have to know a little of Expect’s underlying language, TCL (Actually, there are variants of Expect now for Python, Perl, Java, and Ruby).

TCL (Tool Control Language) is a very straight-forward language, although a little unusual for someone used to the syntax of Java. For example, here’s a one-liner that sets the value of the variable “a” to the second item in a list:

% set a [ lindex { abc def ghi } 2 ]
ghi

The key parts of the script that I wrote were to read the list of host names from a file and to then iterate over that list. Here’s how I read in the host names, after setting the variable hostInputFile from command line or user input.

# read in the lines of the file into a variable
set fileLines [split "[read [ open $hostInputFile r]]" \n]

# iterate over each line
for {set lineIndex 0} { $lineIndex < [llength $fileLines]} {incr lineIndex} {
# select the next line and trim it
set nextLine [string trim [lindex $fileLines $lineIndex]]

# append if line doesn’t begin with ‘#’ and line isn’t empty
if {[ string first # $nextLine ] != 0
[ string length $nextLine ] > 0} {
lappend hostList $nextLine
}
}

Later, I iterate over the list of hosts in hostList…

for {set hostIndex 0} {$hostIndex < [llength $hostList]} {incr hostIndex} {
# get the next host from the list
set nextHost [lindex $hostList $hostIndex]

# spawn ssh
spawn ssh -l $loginUser $nextHost
expect -timeout 30 -nocase "password: "
send $password
send "\n"
expect "bash"
expect -re " $"

# do whatever it is you need to do on this system
# …

send "exit\n"
expect -re "Connection.*closed\."

close
wait
}

Not too complicated, eh? Running scripts similar to this (which actually DID something once logged in) took perhaps a minute to run on dozens of systems. Compare that with the tedium of typing the same logins and passwords and commands over and over, and you can see the usefulness of Expect.

Expect – a good tool for multi-system updates!

Comments

Mike McKinney Wed, 1969-12-31 20:00

Expect is a great tool, and so is the TCL language (pronounced tickle)...  (go ahead Matt and Adam, I can hear you both snickering :) )  Rick, here is another way (little shorter) to accomplish what you've outlined above:

proc doWork {host} {
    puts stdout "host is $host"
}
set fp [open <path to>/hostsFile.txt r]
while {[gets $fp line] > -1} {
    set host [string trim $line]
    if {![string match "#*" $host] &&
            [string length $host]} {
        doWork $host
    }
}
close $fp

On another note, if this is something you might run quite a bit, and you don't really need the interactive nature of expect, you could update the ssh keys for all hosts and simply call

puts stdout "response from $host:

    [exec ssh $host <path to>/remoteScript.tclsh]"

within your "doWork" proc...  (multi-line strings are not an issue.)  There is a great HOWTO listed here regarding ssh-keygen : http://www.arches.uga.edu/~pkeck/ssh/

If you want to find out if you have expect loaded (it's an extension to TCL so you might not have it with your installation) you can issue the following command within a tclsh shell

info command expect

If nothing comes back, you have to go get the extension (here: http://expect.nist.gov/

Adam Rossi Wed, 1969-12-31 20:00

Rick - you mentioned Mike's favorite three letters in your post: T-C-L. I hope that you are prepared for the consequences!

Rick Witter Wed, 1969-12-31 20:00

Ha, TCL is like Perl in that there always seems to be a way to condense the code down into fewer lines -- ultimately to one really long, hard to read line!

In my script, I actually did something like the doWork proc you show... earlier in the script I had the user enters a command file name (just a list of expect commands), and once the script has the user running as root, it would source that file.  This way I could set up lots of little command scripts, but the bulk of the program (reading host list, logging in, su-ing) was unchanged.

By the way, Mike reminded me that TCL stands for Tool Command Language (not Tool Control Language).  That's what I get for trying to write from memory... 

Post new comment

Please solve the math problem above and type in the result. e.g. for 1+1, type 2.
The content of this field is kept private and will not be shown publicly.
  • Lines and paragraphs break automatically.

More information about formatting options