1

I'm trying to execute a remote script on an Oracle ZFS array. The way they've designed the CLI is that you can send it a javascript "script" via SSH. This script is supposed to have the word "script" in the first line which will invoke a sub-shell which waits for a period on it's own line before executing

Example:

script
run('shares');
var projects = list();
dump(projects);
.

which if you use it like this:

$ ssh array < above_script

outputs a nice list of the projects in a JSON readable format.

I'd like to get this to work with paramiko ssh in a python script but so far I'm not having much luck. After successfully connecting and getting an SSHClient instance I'm trying this:

try:
    get_projects = """run('shares');
                    var projects = list();
                    dump(projects);"""
    w, r, e = ssh.exec_command('script')

    for line in get_projects.split('\n'):
        w.write(line)
    w.write('.')
    pp.pprint(e.readlines())
    pp.pprint(r.readlines())

except paramiko.SSHException, e:
    logger.exception("Couldn't execute script on array %s: %s" % (array.name, e))

The problem is that it never returns, it just sits there. I can't even ctrl-c stop it, I have to ctrl-z and kill it.

If I remove the attempts to read stderr, and stdout it returns but nothing is captured, so it seems like there's an issue reading from the socket:

INFO:paramiko.transport:Authentication (publickey) successful!
INFO:root:Not none
DEBUG:paramiko.transport:[chan 1] Max packet in: 34816 bytes
DEBUG:paramiko.transport:[chan 1] Max packet out: 32768 bytes
INFO:paramiko.transport:Secsh channel 1 opened.
DEBUG:paramiko.transport:[chan 1] Sesch channel 1 request ok
DEBUG:paramiko.transport:EOF in transport thread

Any insights would be appreciated.


small update (this is going to look ugly): Invoking the Channel with ssh.invoke_shell() allowed me to send the script and then running shell.recv(1024) returned the messy output below:

INFO:paramiko.transport:Authentication (publickey) successful!
>>> shell = ssh.invoke_shell()
DEBUG:paramiko.transport:[chan 1] Max packet in: 34816 bytes
DEBUG:paramiko.transport:[chan 1] Max packet out: 32768 bytes
INFO:paramiko.transport:Secsh channel 1 opened.
DEBUG:paramiko.transport:[chan 1] Sesch channel 1 request ok
DEBUG:paramiko.transport:[chan 1] Sesch channel 1 request ok
>>> get_projects = """script
...                             run('shares');
...                             var projects = list();
...                             dump(projects);
...                             .
...                             """
>>> get_projects
"script\n                            run('shares');\n                            var projects = list();\n                            dump(projects);\n                            .\n                            "
>>> shell.send(get_projects)
203
>>> shell.recv(1024)
'Last login: Fri Mar  1 02:26:58 2013 from 10.91.134.163\r\r\n\r\x1b[1mciczfsa:>\x1b[m\x0f \x1b[m\x0fscript\n\r\r("." to run)> \x1b[m\x0f                            run(\'shares\');\n\r\r("." to run)> \x1b[m\x0f                            var projects = list();\n\r\r("." to run)> \x1b[m\x0f                            dump(projects);\n\r\r("." to run)> \x1b[m\x0f                            .\n\r[\'Project1\', \'Project_PoolA_2\', \'RBR_PROJECT\', \'SAS_501\', \'TestProject\', \'default\', \'reptest\', \'testproj1\', \'testproj2\']\r\n\r\x1b[1mciczfsa:>\x1b[m\x0f \x1b[m\x0f                            '

Not a TON of help here because it's obviously not structured. Ideally I'd like to send the script to the shell, and receive the structured response to I can parse it as JSON.

Kenster
  • 23,465
  • 21
  • 80
  • 106
Chris Matta
  • 3,263
  • 3
  • 35
  • 48
  • I don't know if this will matter, but just in case: assuming that the `w` returned by `ssh.exec_command('script')` behaves like a standard file, `w.write` does not write a newline at the end of the string. Perhaps try adding a newline to the strings written with `w.write`. – Warren Weckesser Mar 01 '13 at 02:48
  • 1
    Warren, that was just the kick I needed... see above for the (stupid) fix. – Chris Matta Mar 01 '13 at 02:54
  • 1
    You can replace `line.lstrip().rstrip()` with `line.strip()`. Also, have you tried sending the entire string `get_projects` in one call to `w.write`, instead of writing each line separately? – Warren Weckesser Mar 01 '13 at 03:30

1 Answers1

0

From the original poster:

The fix was that I needed to send a newline with each "new line" (funnily enough):

try:
    get_projects = """run('shares');
                    var projects = list();
                    dump(projects);
                    ."""
    w, r, e = ssh.exec_command('script')

    for line in get_projects.split('\n'):
        w.write(line.lstrip().rstrip())
        w.write('\n')
    pp.pprint(r.readline())


except paramiko.SSHException, e:
    logger.exception("Couldn't execute script on array %s: %s" % (array.name, e))
Kenster
  • 23,465
  • 21
  • 80
  • 106