Yesterday I left when the problem got really interesting: how to transfer code
to another machine and execute it there. I already advanced part of the solution:
pickle to convert something returned by
ast.parse() into something
transferable. Let's see how hard it really is:
import paramiko import ast import pickle p= ast.parse ('print ("yes!")') pick= pickle.dumps (p) c= paramiko.SSHClient() c.load_host_keys ('/home/mdione/.ssh/known_hosts') c.connect ('localhost', allow_agent=False, password='foobarbaz') (i, o, e)= c.exec_command ('''python -c "import pickle from ast import Print, Module, Str import sys c= pickle.load (sys.stdin) code= compile (c, 'remote', 'exec') exec (code)"''') i.write (pick) o.readline ()
This happily prints 'yes!' on the last line.
There are a lot of caveats in this code. First, this doesn't work on Python3,
only because there's no official/working port of
paramiko for that version.
Jan N. Schulze a.k.a.
nischu7 has made a port,
which looks quite active (last commit from around a month ago), but I tried it with
Python 3.3 and didn't work out of the box. Furthermore, even when
says that it automatically detects the format of the stream, which means that
technically I could pickle something in Python2 and unpickle it back in Python3,
the same does not happen with the
ast module. Hence, I'm also using Python2 in
the remote. This implies that I will have to check if the reconstruction works
and if the reconstructed code actually
compile()'s. But I already knew that.
Second, this assumes that you have the remote machine already in the
file. Third, I'm importing things from
ast specifically for reconstructing the
parsed code (
ast.dump (p) returns
"Module(body=[Print(dest=None, values=[Str(s='yes!')], nl=True)])").
I hadn't checked yet, but somehow
from ast import * is not enough. Last, the
transfered code is simple enough, makes no references to local or remote variables
(for whichever definition or local and remote; I will have to be consistent in the
future when using those words), nor references other modules, present or not in
the remote machine (there, remote is the machine mentioned in the parameter of
ssh()). But this is a promising step.
Another thing to notice is that the code is sent via
stdin. This might cause
trouble with script expecting things that way, let's see:
import paramiko import ast import pickle p= ast.parse ('foo= raw_input (); print (foo)') pick= pickle.dumps (p) c= paramiko.SSHClient() c.load_host_keys ('/home/mdione/.ssh/known_hosts') c.connect ('localhost', allow_agent=False, password='foobarbaz') command= '''python -c "import pickle from ast import Print, Module, Str, Assign, Name, Call, Load, Store, dump import sys c= pickle.loads (sys.stdin.read (%d)) code= compile (c, 'remote', 'exec') exec (code)"''' % len (pick) (i, o, e)= c.exec_command (command) i.write (pick) i.write ('bar!\n') o.readline ()
This works, but only after someone tells you that you should use
input(), which triggers the realization that you're reading Python3's
doc but using Python2. Damn you,
So, in conclusion, This technique starts to show promise. The good thing about
it is that it barely requires any setup. Future developments could include a
client cache. The next step is to get the variables in the remote machine and
gluing it with the previous developments.
 Another caveat: that one is definitely not my password :)
 Clearly this sentence was written before this other one.
 There, I just invented bodynotes :)
 I have again been bitten by a post that takes days, if not months, to publish.