Cover V13, i02

Article

feb2004.tar

Python in Systems Administration: Part III -- Pexpect Automates Hard-to-Solve Problems

Cameron Laird

Unix systems administrators are familiar with Expect as a nearly indispensable tool for handling difficult problems involving passwords and other unusual forms of data entry. Few know, though, that Python can easily handle most of the same problems: Python has its own Expect.

Before demonstrating Python's Expect solutions, I want to tie up a few loose ends. This is the third in a series on what Python offers to systems administrators. I previously presented Python as an alternative to the shell and Perl programming languages that sys admins commonly use -- one that's both easy to learn and more adept than shell in such domains as networking and graphical user interfaces (GUIs).

Elegance and Balance

Several readers have raised questions based on those earlier installments, so my first order of business this month is to address those.

Greg Wyman correctly spotted a missing close parenthesis in my_restore.py. The os.popen() call printed in the December issue should have been:

print os.popen('tar xf %s %s' % (device, files)).read()
Mr. Wyman also astutely wondered why that same example program didn't take advantages of new features available with Python's 2.3 release to simplify the list comprehension. He's right -- iteration over a file object and substring-testing allow simplification to:

list_of_qualified_names = [file[:-1] for \
    file in os.popen('tar tf %s' % device) \
    if pattern in file]
That is a more elegant formulation and helps make the case for Python's readability. Moreover, the plans for 2.3 were clear long before both its release and the deadline for December's Sys Admin magazine.

I decided to focus on functionality available in 2.0 and, occasionally, 2.2, to help ensure a "good experience" for readers. Nearly all of the hosts for which I do systems administration are currently at 2.2 and before, and I expect the same is true for most readers, in early 2004. I want the attention of such readers to be on working programs, rather than on locating and installing updates.

It's true, though, that Python continues to improve, and almost certainly will do so in the future. For a snapshot of what a new release provides, see the online article "Python 2.3" that I co-authored:

http://www.byte.com/documents/s=8880/byt1062182129207/0901_laird.html
Paddy McCarthy makes several points that are difficult to summarize briefly. Most starkly, he wonders why I promote allegiance to the Unix model of "many smaller utilities doing one thing well, connected via pipes", yet script the Tkinter example as a standalone executable. He's right to wonder, and his letter, which you can read in full at:

http://mail.python.org/pipermail/python-list/2003-December/199071.html
presents significant alternatives. The real problem is simply the difficulty of systems administration. In a follow-up, Donn Cave observes that it "isn't really a computer programming domain like numeric, hardware control, rendering, [or Web publishing]", but more like the institutionalization of "Miscellaneous Little Tasks". In this series, I've generally aimed to provide examples that stand on their own, but are otherwise as minimal as possible. My concern is more to make ideas clear, so you can apply the ideas to your own situation, rather than to supply finished solutions.

The second installment mentioned IPython as an alternative shell. This mailing list thread:

http://mail.python.org/pipermail/python-list/2003-December/198584.html
details the latest news about what it's like to use IPython for daily work.

Scripting the Unscriptable

Let's return now to this month's main subject, and start by asking, "What's the point of Expect?" Suppose you need to reset a password. That's a common occurrence, one that /usr/bin/passwd addresses. passwd does everything necessary for the most common situations systems administrators face.

Suppose, though, you want to automate a passwd solution -- do a bulk re-assignment of the passwords for everyone in a particular college course, say, or set up a simple CGI-driven page that allows remote users to change their passwords. For a long-term solution, you shouldn't be using conventional Unix /etc/passwd accounts, or CGI, of course. There are too many security vulnerabilities to make those safe except with very careful analysis and hardening.

That sort of safety isn't always available to us. One of the great challenges of systems administration is its reactivity. There is no "long term" for many tasks; the idea is just to fix things up and get them running with as little delay as possible. The point of Expect isn't to encourage "hacking" or a short-term approach. Instead, Expect simply offers to automate jobs you'd be doing by hand otherwise. In this example, passwd does the job fine, as long as you're willing to type when it is ready. Redirecting a script into passwd doesn't work -- as with many programs that receive sensitive information, it disables keystroke echoing in a way that makes input redirection infeasible.

Pexpect, available through:

http://sourceforge.net/projects/pexpect/
changes all that. It's a tiny download compatible with any recent installation of Python. Once you've unpacked it, you can write Python programs such as:

#!/usr/bin/env python
'''Change password for one user.

Save this source as 'set_password.py'.

A production version would handle exceptions, explain that it can 
only be run as 'root', and so on.
'''
  
import pexpect, sys
  
if len(sys.argv) != 3:
    print "Usage:  set_password.py user new_password."
    print "%d and %s." % (len(sys.argv), sys.argv)
    sys.exit(1)
(user, new_password) = (sys.argv[1], sys.argv[2])
child = pexpect.spawn("passwd %s" % user)
for repeat in (1, 2):
    child.expect("password: ")
    child.sendline(new_password)
With this little program in hand, password management suddenly becomes scriptable. If you like, you can run this utility from a shell script, on the model of:

#!/bin/sh

INITIAL_PASSWORD=abcd1234
for USER in "user1 user2 user3"
do

    set_password.py $USER $INITIAL_PASSWORD
done
Different Kinds of Expect

This capability is monumental -- at least, it has been to me. With Expect, I frequently solve in half an hour problems that would take all day by hand. I am very grateful for the confidence it gives me that, if I can just understand how to solve a specific instance of a problem in systems administration or data entry, I'll be able to generalize it to an automation that runs on its own. In my work, Expect has been particularly valuable for automating such "embedded applications" as are common in routers, switches, and other network hardware, along with lab equipment.

Don Libes, a computer scientist with what's now known as the National Institute of Standards and Technology, wrote the original for Expect more than a decade ago. He based his work on the Tcl programming language. His version, and the influential book he wrote about it, Exploring Expect, have been so successful that many programmers and administrators believe Expect is available only in its Tcl manifestation. Libes anticipated from the beginning, though, that other languages would take up the idea.

Most important is to use some flavor of Expect. This makes for a big boost in productivity beside which the differences between the Expects for different languages pale. The original Tcl-based Expect has several advantages:

  • The book is so well written and full of useful ideas that I recommend it even to those running Expect based on languages other than Tcl.
  • Libes' version is more mature, with careful workarounds for numerous operating-system-specific faults and blemishes.
  • It has advanced facilities such as interact not implemented for all other languages.

Don't let those discourage you, however; Pexpect is more trouble-free to install than the original Expect, and its functionality is a close match. It has its own interact(), and works well on such common Unixes as Linux, *BSD, and Solaris.

If you have begun to experiment with Python in your systems administration work, Pexpect makes a natural complement. Be sure to pay particular attention to Pexpect's examples/ directory; it includes sample Pexpect-code programs to play chess, run commands remotely through ssh, remove binary files improperly added to a CVS repository, and more.

Next month, I'll investigate a bit deeper Python's abilities to build graphical user interfaces for systems administration tasks.

Cameron Laird, a vice president at consultancy Phaseit, Inc. (http://phaseit.net/), has written occasionally for Sys Admin in previous years.