views:

594

answers:

5

Hello,

I am trying to test if a file exists over SSH using pexpect. I have got most of the code working but I need to catch the value so I can assert whether the file exists. The code I have done is below:

def VersionID():

        ssh_newkey = 'Are you sure you want to continue connecting'
        # my ssh command line
        p=pexpect.spawn('ssh [email protected]')

        i=p.expect([ssh_newkey,'password:',pexpect.EOF])
        if i==0:
            p.sendline('yes')
            i=p.expect([ssh_newkey,'password:',pexpect.EOF])
        if i==1:
            p.sendline("word")
            i=p.expect('service@main-:')
            p.sendline("cd /opt/ad/bin")
            i=p.expect('service@main-:')
            p.sendline('[ -f email_tidyup.sh ] && echo "File exists" || echo "File does not exists"')
            i=p.expect('File Exists')
            i=p.expect('service@main-:')
            assert True
        elif i==2:
            print "I either got key or connection timeout"
            assert False

        results = p.before # print out the result

VersionID()

Thanks for any help.

A: 

I don't have any pexpect experience but looking at their web page it looks like you can call the expect method with multiple values and it returns the index of the one that it matches (this is based purely on me just looking at this example).

child.expect('password:')
child.sendline (my_secret_password)
# We expect any of these three patterns...
i = child.expect (['Permission denied', 'Terminal type', '[#\$] '])
if i==0:
    print 'Permission denied on host. Can't login'
    child.kill(0)
elif i==2:
    print 'Login OK... need to send terminal type.'
    child.sendline('vt100')
    child.expect ('[#\$] ')
elif i==3:
    print 'Login OK.'
    print 'Shell command prompt', child.after

Actually, you're already using that functionality at the top.

So you want to catch whether the file exists or not?...

Try this...

        p.sendline('[ -f email_tidyup.sh ] && echo "File exists" || echo "File does not exists"')
        file_exists = {0: True, 1: False}[p.expect(('File Exists', 'File does not exists'))]
eric.frederich
A: 

When you as user type something in ssh, the shell will echo the characters back. That is happening now as well.

So, doing:

p.sendline('test -f email_tidyup.sh && echo "File exists" || echo "File does not exist"')

Will result in an input of:

service@main-: test -f email_tidy.sh && echo "File exists" || echo "File does not exists"
File does not exist

Doing:

i = p.expect(['File exists', 'File does not exist'])

Will then always result in i==0, because "File exists" is present in the first line that is received back.

An alternative where the original send line, does not have the expected sentence:

p.sendline('test -f email_tidyup.sh; echo result: $?')
i = p.expect('result: 0', 'result: 1')

Or more like the original:

p.sendline('[ -f email_tidyup.sh ] && echo "File exists" || echo "File does not exist"')
i = p.expect(['\nFile exists', '\nFile does not exist'])
Johan
@Johan I tried the solution you provided. When File Exists i==0 but when file does not exits i==0 as well.
chrissygormley
The square bracket version is **exactly** the same as the `test` version.
Dennis Williamson
@Dennis You are right. I've remove that part from the answer.
Johan
+3  A: 

Why not take advantage of the fact that the return code of the command is passed back over SSH?

$ ssh victory 'test -f .bash_history'
$ echo $?
0
$ ssh victory 'test -f .csh_history'
$ echo $?
1
$ ssh hostdoesntexist 'test -f .csh_history'
ssh: Could not resolve hostname hostdoesntexist: Name or service not known
$ echo $?
255

This way, you can just check the return code without needing to capture output.

MikeyB
(yeah yeah, I wrote in shell script... writing in Python is left as an exercise to the reader :) )
MikeyB
A: 

I have worked the solution that will do me. The code is below:

def VersionID(): 

    ssh_newkey = 'Are you sure you want to continue connecting' 
    # my ssh command line 
    p=pexpect.spawn('ssh [email protected]') 

    i=p.expect([ssh_newkey,'password:',pexpect.EOF]) 
    if i==0: 
        p.sendline('yes') 
        i=p.expect([ssh_newkey,'password:',pexpect.EOF]) 
    if i==1: 
        p.sendline("word") 
        i=p.expect('service@main-:') 
        p.sendline("cd /opt/ad/bin") 
        i=p.expect('service@main-:') 
        p.sendline('[ -f email_tidyup.sh ] && echo "File exists" || echo "File does not exists"') 
        i=p.expect('service@main-:') 
        assert True 
    elif i==2: 
        print "I either got key or connection timeout" 
        assert False 


        results = p.before # print out the result
        print results
        value = results.split('"')[8]
        split_value = value.split('\r\n')[1:-1]
        self.assertEquals(split_value, ['File exists'])

This extracts the value from 'p' in a string format. I then split the string up to get the string 'File Exists' into a list and compare it to the response I am looking for. If the File does not exists the test will fail.

Thanks for all the help.

chrissygormley
+1  A: 

If the server accepts sftp sessions, I wouldn't bother with pexpect, but instead use the paramiko SSH2 module for Python:

import paramiko
transport=paramiko.Transport("10.10.0.0")
transport.connect(username="service",password="word")
sftp=paramiko.SFTPClient.from_transport(transport)
filestat=sftp.stat("/opt/ad/bin/email_tidyup.sh")

The code opens an SFTPClient connection to the server, on which you can use stat() to check for the existance of files and directories.

sftp.stat will raise an IOError ('No such file') when the file doesn't exist.

If the server doesn't support sftp, this would work:

import paramiko
client=paramiko.SSHClient()
client.load_system_host_keys()
client.connect("10.10.0.0",username="service",password="word")
_,stdout,_=client.exec_command("[ -f /opt/ad/bin/email_tidyup.sh ] && echo OK")
assert stdout.read()

SSHClient.exec_command returns a triple (stdin,stdout,stderr). Here we just check for the presence of any output. You might instead vary the command or check stderr for any error messages instead.

flight
@flight -- I get an error: ----paramiko.SSHException: Channel closed.---- All the details are entered correctly, would you know why? Thanks
chrissygormley
@chrissygormley: Does "sftp [email protected]" work for you (i.e. in a shell)?
flight
@flight -- The sftp does work for other machines, but for the camera I'm logging into it is getting rejected. I will look at how to solve this problem and try the above code again. Thanks
chrissygormley
It's likely that the sftp binary doesn't exist on the camera. Try using ssh or sftp with `-v`
MikeyB
@chrissygormley: I would be interested to hear whether the second solution (without SFTP, see above) worked for you.
flight
@flight -- I tried the second solution but recieved an error which was: --- DeprecationWarning: the md5 module is deprecated; use hashlib instead --- I have installed the paramiko libraries.
chrissygormley
@chrissygormley: Sounds like an older paramiko version on a more recent Python version. You might try to change the "import md5" in paramiko into "import hashlib", or create a md5.py in your PYTHONPATH with a single line "import hashlib". Or perhaps try to update paramiko.
flight
@flight -- I havn't manged to get the paramiko working as it has something to do with the versions. I tried this code on another machine and it is useful. Thanks
chrissygormley