Search code examples
pythonpython-3.xapache-karafpexpectkaraf

Python pexpect - spawn takes time to establish


Does pexpect.spawn take time to return to its calling Python script?

I'm writing a script to manage a Karaf container, using pexpect to inject commands. If Karaf isn't running, I've noticed that I cannot always reliably use isalive() unless I insert a pause in the script.

For example:-

# open a Karaf SSH session
karaf_session = pexpect.spawn("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p " +
                      karaf_port + " " +
                      karaf_id + "@" +
                      karaf_host)

log_file = open(log_file_name, "wb")
karaf_session.logfile_read = log_file

time.sleep(1)

if karaf_session.isalive():
    print("Connected to Karaf")
else:
    print("Failed to connect to Karaf")

If I comment out time.sleep(1) then isalive()returns true, even though Karaf is not running.

Depending on whether the logfile_read is before or after the sleep, it either catches nothing or the following:-

ssh: connect to host localhost port 8101: Connection refused

I'd rather have something more reliable than an arbitrary sleep!


Solution

  • Well, regardless of the connection status, the ssh process is going to be running for some period of time, during which isalive() would (correctly) return True - after all, it is running. A running ssh, however, does not imply that a successful connection has been established because it takes some amount of time for that to happen.

    Adding the sleep provides enough time for the spawned ssh process to terminate because of the failed connection when Karaf is not running. Without the delay, isalive() is evaluated while ssh is still running and therefore returns True.

    So, you can't rely on the fact that the ssh process is running to mean that you have a valid connection to your Karaf server. You need to interact with it by waiting to see whether a Password: prompt is issued by Karaf, for example:

    import time
    import getpass
    import pexpect
    
    karaf_port = 8101
    karaf_id = 'user'
    karaf_host = 'localhost'
    log_file_name = 'karaf.log'
    
    ssh_command = 'ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p {} {}@{}'.format(karaf_port, karaf_id, karaf_host)
    
    karaf_session = pexpect.spawn(ssh_command)
    
    log_file = open(log_file_name, "wb")
    karaf_session.logfile_read = log_file
    
    try:
        karaf_session.expect('Password:', timeout=5)
        print('Got "Password:" prompt, sending password')
        karaf_session.sendline(getpass.getpass("Enter your Karaf password: "))
        # etc., etc.
    except pexpect.EOF as e:
        print('Connection failed. Got EOF exception waiting for "Password:" prompt')
    except pexpect.TIMEOUT as e:
        print('Timed out waiting for "Password:" prompt')