Telnet connection to an AS400 system via telnetlib (Python)

IBM has made a habit of changing names. Chances are that the machine you are talking about is not truly an AS/400, which ran OS/400. IBM has not shipped one since 2000. Product line was replaced (or renamed, depending upon your point of view) with the iSeries systems, which later became System i, then i5, but the OS was still OS/400. The hardware product line was merged with the pSeries systems, and became Power Systems. The OS became i5/OS, and now under the latest release 7.1, it is officially "IBM i". The next hardware generation, Pure Systems, can also run IBM i OS. So chances are your server is actually a System i or a Power System, but IT staff often continues to call them AS/400's, even thinking of them as AS/400's. As a group, this family tree of product lines is often referred to as IBM's midrange systems.

IBM midrange systems originally employed IBM model 5250 terminals. Successive models still used what is referred to as 5250 protocol, which in some ways is similar to the 3270 protocol used with IBM mainframes. It is a block oriented protocol, generally transmitting an entire screen at a time, and often colloquially called "green screen" based on the original color of the characters. While your server might possibly be configured to use VTxxx telnet, that is probably a long-shot. But TN5250 support is almost certain. Try connecting with that instead.


You can't treat the AS/400 connection like you would a *nix telnet connection. Rather than using read_until(), try using read_very_eager() to get the entire screen (including ANSI escape sequences which position the cursor at the beginning of the "User...." field), then write() exactly what you would type if you were to log in manually. I've also found that in order to get the log in screen to show up after connecting, you need to first write a newline character. I have no idea why this is, but that's what worked for me.

import getpass
import sys
import telnetlib

HOST = raw_input("HOST : ")
user = raw_input("Enter your remote account: ")
password = getpass.getpass()


try:
    tn = telnetlib.Telnet(HOST)
    tn.write('\n')
    tn.read_very_eager()
    tn.write(user)
    tn.write('\t')  # tab into the next field
    tn.write(password)
    tn.write('\r')  # 'enter' key
except:
    print "Error"

One last thing: Keep in mind that the AS/400 won't send an EOF character at the end of the screen, so read_all() will just hang and never return. You could try using read_until(), but since the data returned from the server may be broken up and full of ANSI escape sequences, there's no guarantee of finding what you want. The obvious solution seems to put read_very_eager() in a while loop terminating when read_very_eager() returns an empty string. However, this has its own problems, as the server may return various status messages or other partial screens before moving on to the next full screen. The only full solution is likely to continuously parse the data returned by server and keep track of what the current screen looks like (i.e. terminal emulation and screen scraping) or simply add some time.sleep() commands between writing and reading.


Below are the questions I was going to ask in a comment attached to your question. Then I noticed that you cross-posted the question to Stack Overflow. Please don't cross post.

There I found that you had posted a paste bin in a comment. That lead me to change my comment to an answer.

In your paste bin, the AS/400's prompts show spaces between the periods. In the code in your question, there are no spaces between the dots. It is likely that putting spaces there will cause your code to work.


For posterity (supplying this kind of information up front will make it more likely you'll receive a good answer and sooner):

Are you sure you're sending the correct line endings to the AS/400?

What OS is the AS/400 running?

Do the AS/400 telnet login prompts really have all those dots followed by a space?

Are you expecting to see a welcome banner or similar?

What happens if you send a command before the read_all?

What do the logs on the AS/400 say?


Try the Python Expect (pexpect) module. Follow the passmass.py script as an example. It should be included with the pexpect module too.

Here's an untested revision to your script:

import getpass
import pexpect
import sys

HOST = raw_input("HOST : ")
user = raw_input("Enter your remote account: ")
password = getpass.getpass()


try:
    tn = pexpect.spawn('telnet %s'%(HOST))

    tn.logfile = file ("LOG.TXT","wb")

    i = tn.expect([pexpect.TIMEOUT, '[Uu]ser..... '])
    if i == 0: # Timeout
        print 'ERROR!'
        print 'telnet could not login. Here is what telnet said:'
        print tn.before, tn.after
        sys.exit (1)
    tn.sendline(user)
    tn.expect (['[Pp]assword..... '])
    tn.sendline(password)

except:
    print "Error"