diff --git a/py/execnet/register.py b/py/execnet/register.py index ceca07113..e8577d544 100644 --- a/py/execnet/register.py +++ b/py/execnet/register.py @@ -2,6 +2,8 @@ import os, inspect, socket import sys from py.magic import autopath ; mypath = autopath() +from py.__.misc.warn import APIWARN + import py if sys.platform == "win32": win32 = True @@ -55,6 +57,7 @@ class InstallableGateway(gateway.Gateway): class PopenCmdGateway(InstallableGateway): def __init__(self, cmd): infile, outfile = os.popen2(cmd) + self._cmd = cmd io = inputoutput.Popen2IO(infile, outfile) super(PopenCmdGateway, self).__init__(io=io) @@ -130,14 +133,16 @@ class SocketGateway(InstallableGateway): class SshGateway(PopenCmdGateway): - """ This Gateway provides interaction with a remote process, + """ This Gateway provides interaction with a remote Python process, established via the 'ssh' command line binary. The remote side needs to have a Python interpreter executable. """ - def __init__(self, sshaddress, remotepython='python', identity=None): + def __init__(self, sshaddress, remotepython='python', + identity=None, ssh_config=None): """ instantiate a remote ssh process with the given 'sshaddress' and remotepython version. - you may specify an 'identity' filepath. + you may specify an ssh_config file. + DEPRECATED: you may specify an 'identity' filepath. """ self.remoteaddress = sshaddress remotecmd = '%s -u -c "exec input()"' % (remotepython,) @@ -147,9 +152,13 @@ class SshGateway(PopenCmdGateway): cmdline[i] = "'" + cmdline[i].replace("'", "'\\''") + "'" cmd = 'ssh -C' if identity is not None: + APIWARN("1.0", "pass in 'ssh_config' file instead of identity") cmd += ' -i %s' % (identity,) + if ssh_config is not None: + cmd += ' -F %s' % (ssh_config) cmdline.insert(0, cmd) - super(SshGateway, self).__init__(' '.join(cmdline)) + cmd = ' '.join(cmdline) + super(SshGateway, self).__init__(cmd) def _remote_bootstrap_gateway(self, io, s=""): extra = "\n".join([ diff --git a/py/execnet/testing/test_gateway.py b/py/execnet/testing/test_gateway.py index d5948b596..d65fb5db2 100644 --- a/py/execnet/testing/test_gateway.py +++ b/py/execnet/testing/test_gateway.py @@ -439,6 +439,9 @@ class BasicRemoteExecution: text = c1.receive() assert text.find("execution disallowed") != -1 +class BasicCmdbasedRemoteExecution(BasicRemoteExecution): + def test_cmdattr(self): + assert hasattr(self.gw, '_cmd') def test_channel_endmarker_remote_killterm(): gw = py.execnet.PopenGateway() @@ -571,20 +574,31 @@ class TestSshGateway(BasicRemoteExecution): py.test.skip("no known ssh target, use -S to set one") cls.gw = py.execnet.SshGateway(option.sshtarget) + def test_sshconfig_functional(self): + tmpdir = py.test.ensuretemp("test_sshconfig") + ssh_config = tmpdir.join("ssh_config") + ssh_config.write( + "Host alias123\n" + " HostName %s\n" % (option.sshtarget,)) + gw = py.execnet.SshGateway("alias123", ssh_config=ssh_config) + assert gw._cmd.find("-F") != -1 + assert gw._cmd.find(str(ssh_config)) != -1 + pid = gw.remote_exec("import os ; channel.send(os.getpid())").receive() + gw.exit() + def test_sshaddress(self): assert self.gw.remoteaddress == option.sshtarget - def test_failed_connexion(self): - gw = py.execnet.SshGateway('nowhere.codespeak.net') - try: - channel = gw.remote_exec("...") - except IOError: - pass # connexion failed already - else: - # connexion did not fail yet - py.test.raises(EOFError, channel.receive) - # now it did - py.test.raises(IOError, gw.remote_exec, "...") + def test_connexion_failes_on_non_existing_hosts(self): + py.test.raises(IOError, + "py.execnet.SshGateway('nowhere.codespeak.net')") + + def test_deprecated_identity(self): + py.test.deprecated_call( + py.test.raises, IOError, + py.execnet.SshGateway, + 'nowhere.codespeak.net', identity='qwe') + def test_threads(): gw = py.execnet.PopenGateway()