[svn r63147] get rid of old method for specifying remote execution places.

--HG--
branch : trunk
This commit is contained in:
hpk 2009-03-20 17:28:14 +01:00
parent 9cd41c91bd
commit a4c14e83f0
7 changed files with 60 additions and 224 deletions

View File

@ -27,8 +27,8 @@ version = "1.0.0a1"
initpkg(__name__, initpkg(__name__,
description = "pylib and py.test: agile development and test support library", description = "pylib and py.test: agile development and test support library",
revision = int('$LastChangedRevision: 63144 $'.split(':')[1][:-1]), revision = int('$LastChangedRevision: 63147 $'.split(':')[1][:-1]),
lastchangedate = '$LastChangedDate: 2009-03-20 16:36:45 +0100 (Fri, 20 Mar 2009) $', lastchangedate = '$LastChangedDate: 2009-03-20 17:28:14 +0100 (Fri, 20 Mar 2009) $',
version = version, version = version,
url = "http://pylib.org", url = "http://pylib.org",
download_url = "http://codespeak.net/py/0.9.2/download.html", download_url = "http://codespeak.net/py/0.9.2/download.html",
@ -151,7 +151,6 @@ initpkg(__name__,
'execnet.SocketGateway' : ('./execnet/register.py', 'SocketGateway'), 'execnet.SocketGateway' : ('./execnet/register.py', 'SocketGateway'),
'execnet.PopenGateway' : ('./execnet/register.py', 'PopenGateway'), 'execnet.PopenGateway' : ('./execnet/register.py', 'PopenGateway'),
'execnet.SshGateway' : ('./execnet/register.py', 'SshGateway'), 'execnet.SshGateway' : ('./execnet/register.py', 'SshGateway'),
'execnet.GatewaySpec' : ('./execnet/gwmanage.py', 'GatewaySpec'),
'execnet.XSpec' : ('./execnet/xspec.py', 'XSpec'), 'execnet.XSpec' : ('./execnet/xspec.py', 'XSpec'),
'execnet.makegateway' : ('./execnet/xspec.py', 'makegateway'), 'execnet.makegateway' : ('./execnet/xspec.py', 'makegateway'),
'execnet.MultiGateway' : ('./execnet/multi.py', 'MultiGateway'), 'execnet.MultiGateway' : ('./execnet/multi.py', 'MultiGateway'),

View File

@ -37,9 +37,11 @@ def getspecssh(config=None):
if not py.path.local.sysfind("ssh"): if not py.path.local.sysfind("ssh"):
py.test.skip("command not found: ssh") py.test.skip("command not found: ssh")
return spec return spec
py.test.skip("need '--gx ssh=...'")
def getsocketspec(config=None): def getsocketspec(config=None):
xspecs = getgspecs(config) xspecs = getgspecs(config)
for spec in xspecs: for spec in xspecs:
if spec.socket: if spec.socket:
return spec return spec
py.test.skip("need '--gx socket=...'")

View File

@ -1,105 +1,19 @@
""" """
instantiating, managing and rsyncing to hosts instantiating, managing and rsyncing to hosts
Host specification strings and implied gateways:
socket:hostname:port:path SocketGateway
popen[-executable][:path] PopenGateway
[ssh:]spec:path SshGateway
* [SshGateway]
on hostspec.makeconnection() a Host object
will be created which has an instantiated gateway.
the remote side will be chdir()ed to the specified path.
if no path was specified, do no chdir() at all.
""" """
import py import py
import sys, os import sys, os
from py.__.test.dsession.masterslave import MasterNode
from py.__.test import event
from py.__.execnet.channel import RemoteError from py.__.execnet.channel import RemoteError
NO_ENDMARKER_WANTED = object() NO_ENDMARKER_WANTED = object()
class GatewaySpec(object):
python = None
def __init__(self, spec, defaultjoinpath="pyexecnetcache"):
self._spec = spec
if spec == "popen" or spec.startswith("popen:"):
parts = spec.split(":", 2)
self.type = self.address = parts.pop(0)
if parts:
python = parts.pop(0)
# XXX XXX XXX do better GWSPEC that can deal
# with "C:"
if py.std.sys.platform == "win32" and len(python) == 1:
python = "%s:%s" %(python, parts.pop(0))
self.python = python
if parts:
self.joinpath = parts.pop(0)
else:
self.joinpath = ""
if not self.python:
self.python = py.std.sys.executable
elif spec.startswith("socket:"):
parts = spec[7:].split(":", 2)
self.address = parts.pop(0)
if parts:
port = int(parts.pop(0))
self.address = self.address, port
self.joinpath = parts and parts.pop(0) or ""
self.type = "socket"
else:
if spec.startswith("ssh:"):
spec = spec[4:]
parts = spec.split(":", 2)
self.address = parts.pop(0)
self.python = parts and parts.pop(0) or "python"
self.joinpath = parts and parts.pop(0) or ""
self.type = "ssh"
if not self.joinpath and not self.inplacelocal():
self.joinpath = defaultjoinpath
def inplacelocal(self):
return bool(self.type == "popen" and not self.joinpath)
def __str__(self):
return "<GatewaySpec %s>" % self._spec
__repr__ = __str__
def makegateway(self, waitclose=True):
if self.type == "popen":
gw = py.execnet.PopenGateway(python=self.python)
elif self.type == "socket":
gw = py.execnet.SocketGateway(*self.address)
elif self.type == "ssh":
gw = py.execnet.SshGateway(self.address, remotepython=self.python)
if self.joinpath:
channel = gw.remote_exec("""
import os
path = %r
try:
os.chdir(path)
except OSError:
os.mkdir(path)
os.chdir(path)
""" % self.joinpath)
if waitclose:
channel.waitclose()
else:
if waitclose:
gw.remote_exec("").waitclose()
gw.spec = self
return gw
class GatewayManager: class GatewayManager:
RemoteError = RemoteError RemoteError = RemoteError
def __init__(self, specs): def __init__(self, specs):
self.specs = [GatewaySpec(spec) for spec in specs] self.specs = [py.execnet.XSpec(spec) for spec in specs]
self.gateways = [] self.gateways = []
def trace(self, msg): def trace(self, msg):
@ -111,7 +25,7 @@ class GatewayManager:
def makegateways(self): def makegateways(self):
assert not self.gateways assert not self.gateways
for spec in self.specs: for spec in self.specs:
gw = spec.makegateway() gw = py.execnet.makegateway(spec)
self.gateways.append(gw) self.gateways.append(gw)
gw.id = "[%s]" % len(self.gateways) gw.id = "[%s]" % len(self.gateways)
self.notify("gwmanage_newgateway", gw) self.notify("gwmanage_newgateway", gw)
@ -121,7 +35,7 @@ class GatewayManager:
self.makegateways() self.makegateways()
l = [] l = []
for gw in self.gateways: for gw in self.gateways:
if gw.spec.inplacelocal(): if gw.spec._samefilesystem():
if inplacelocal: if inplacelocal:
l.append(gw) l.append(gw)
else: else:
@ -150,15 +64,14 @@ class GatewayManager:
seen = {} seen = {}
for gateway in self.gateways: for gateway in self.gateways:
spec = gateway.spec spec = gateway.spec
if not spec.inplacelocal(): if not spec._samefilesystem():
key = spec.type, spec.address, spec.joinpath if spec in seen:
if key in seen:
continue continue
def finished(): def finished():
if notify: if notify:
notify("rsyncrootready", spec, source) notify("rsyncrootready", spec, source)
rsync.add_target_host(gateway, finished=finished) rsync.add_target_host(gateway, finished=finished)
seen[key] = gateway seen[spec] = gateway
if seen: if seen:
self.notify("gwmanage_rsyncstart", source=source, gateways=seen.values()) self.notify("gwmanage_rsyncstart", source=source, gateways=seen.values())
rsync.send() rsync.send()
@ -203,5 +116,5 @@ class HostRSync(py.execnet.RSync):
def _report_send_file(self, gateway, modified_rel_path): def _report_send_file(self, gateway, modified_rel_path):
if self._verbose: if self._verbose:
path = os.path.basename(self._sourcedir) + "/" + modified_rel_path path = os.path.basename(self._sourcedir) + "/" + modified_rel_path
remotepath = gateway.spec.joinpath remotepath = gateway.spec.chdir
print '%s:%s <= %s' % (gateway.remoteaddress, remotepath, path) print '%s:%s <= %s' % (gateway.remoteaddress, remotepath, path)

View File

@ -37,7 +37,7 @@ class TestGatewayManagerPopen:
assert not len(hm.gateways) assert not len(hm.gateways)
def test_hostmanager_rsync_popen_with_path(self, source, dest): def test_hostmanager_rsync_popen_with_path(self, source, dest):
hm = GatewayManager(["popen::%s" %dest] * 1) hm = GatewayManager(["popen//chdir=%s" %dest] * 1)
hm.makegateways() hm.makegateways()
source.ensure("dir1", "dir2", "hello") source.ensure("dir1", "dir2", "hello")
l = [] l = []
@ -51,7 +51,7 @@ class TestGatewayManagerPopen:
assert dest.join("dir1", "dir2", 'hello').check() assert dest.join("dir1", "dir2", 'hello').check()
def test_hostmanage_rsync_same_popen_twice(self, source, dest, eventrecorder): def test_hostmanage_rsync_same_popen_twice(self, source, dest, eventrecorder):
hm = GatewayManager(["popen::%s" %dest] * 2) hm = GatewayManager(["popen//chdir=%s" %dest] * 2)
hm.makegateways() hm.makegateways()
source.ensure("dir1", "dir2", "hello") source.ensure("dir1", "dir2", "hello")
hm.rsync(source) hm.rsync(source)
@ -65,7 +65,7 @@ class TestGatewayManagerPopen:
def test_multi_chdir_popen_with_path(self, testdir): def test_multi_chdir_popen_with_path(self, testdir):
import os import os
hm = GatewayManager(["popen::hello"] * 2) hm = GatewayManager(["popen//chdir=hello"] * 2)
testdir.tmpdir.chdir() testdir.tmpdir.chdir()
hellopath = testdir.tmpdir.mkdir("hello") hellopath = testdir.tmpdir.mkdir("hello")
hm.makegateways() hm.makegateways()
@ -122,8 +122,7 @@ class TestHRSync:
assert 'somedir' in basenames assert 'somedir' in basenames
def test_hrsync_one_host(self, source, dest): def test_hrsync_one_host(self, source, dest):
spec = py.execnet.GatewaySpec("popen::%s" % dest) gw = py.execnet.makegateway("popen//chdir=%s" % dest)
gw = spec.makegateway()
finished = [] finished = []
rsync = HostRSync(source) rsync = HostRSync(source)
rsync.add_target_host(gw, finished=lambda: finished.append(1)) rsync.add_target_host(gw, finished=lambda: finished.append(1))

View File

@ -1,101 +0,0 @@
"""
tests for py.execnet.GatewaySpec
"""
import py
class TestGatewaySpec:
"""
socket:hostname:port:path SocketGateway
popen[-executable][:path] PopenGateway
[ssh:]spec:path SshGateway
* [SshGateway]
"""
def test_popen(self):
for python in ('', 'python2.4'):
for joinpath in ('', 'abc', 'ab:cd', '/x/y'):
s = ":".join(["popen", python, joinpath])
print s
spec = py.execnet.GatewaySpec(s)
assert spec.address == "popen"
assert spec.python == (python or py.std.sys.executable)
assert spec.joinpath == joinpath
assert spec.type == "popen"
spec2 = py.execnet.GatewaySpec("popen" + joinpath)
self._equality(spec, spec2)
def test_ssh(self):
for prefix in ('ssh', ''): # ssh is default
for hostpart in ('x.y', 'xyz@x.y'):
for python in ('python', 'python2.5'):
for joinpath in ('', 'abc', 'ab:cd', '/tmp'):
specstring = ":".join([prefix, hostpart, python, joinpath])
if specstring[0] == ":":
specstring = specstring[1:]
print specstring
spec = py.execnet.GatewaySpec(specstring)
assert spec.address == hostpart
assert spec.python == python
if joinpath:
assert spec.joinpath == joinpath
else:
assert spec.joinpath == "pyexecnetcache"
assert spec.type == "ssh"
spec2 = py.execnet.GatewaySpec(specstring)
self._equality(spec, spec2)
def test_socket(self):
for hostpart in ('x.y', 'x', 'popen'):
for port in ":80", ":1000":
for joinpath in ('', ':abc', ':abc:de'):
spec = py.execnet.GatewaySpec("socket:" + hostpart + port + joinpath)
assert spec.address == (hostpart, int(port[1:]))
if joinpath[1:]:
assert spec.joinpath == joinpath[1:]
else:
assert spec.joinpath == "pyexecnetcache"
assert spec.type == "socket"
spec2 = py.execnet.GatewaySpec("socket:" + hostpart + port + joinpath)
self._equality(spec, spec2)
def _equality(self, spec1, spec2):
assert spec1 != spec2
assert hash(spec1) != hash(spec2)
assert not (spec1 == spec2)
class TestGatewaySpecAPI:
def test_popen_nopath_makegateway(self, testdir):
spec = py.execnet.GatewaySpec("popen")
gw = spec.makegateway()
p = gw.remote_exec("import os; channel.send(os.getcwd())").receive()
curdir = py.std.os.getcwd()
assert curdir == p
gw.exit()
def test_popen_makegateway(self, testdir):
spec = py.execnet.GatewaySpec("popen::" + str(testdir.tmpdir))
gw = spec.makegateway()
p = gw.remote_exec("import os; channel.send(os.getcwd())").receive()
assert spec.joinpath == p
gw.exit()
def test_popen_makegateway_python(self, testdir):
spec = py.execnet.GatewaySpec("popen:%s" % py.std.sys.executable)
gw = spec.makegateway()
res = gw.remote_exec("import sys ; channel.send(sys.executable)").receive()
assert py.std.sys.executable == py.std.sys.executable
gw.exit()
def test_ssh(self, specssh):
sshhost = specssh.ssh
spec = py.execnet.GatewaySpec("ssh:" + sshhost)
gw = spec.makegateway()
p = gw.remote_exec("import os ; channel.send(os.getcwd())").receive()
gw.exit()
@py.test.mark.xfail("implement socketserver test scenario")
def test_socketgateway(self):
gw = py.execnet.PopenGateway()
spec = py.execnet.GatewaySpec("ssh:" + sshhost)

View File

@ -4,23 +4,23 @@ XSpec = py.execnet.XSpec
class TestXSpec: class TestXSpec:
def test_attributes(self): def test_attributes(self):
spec = XSpec("socket=192.168.102.2:8888//python=c:/this/python2.5//path=d:\hello") spec = XSpec("socket=192.168.102.2:8888//python=c:/this/python2.5//chdir=d:\hello")
assert spec.socket == "192.168.102.2:8888" assert spec.socket == "192.168.102.2:8888"
assert spec.python == "c:/this/python2.5" assert spec.python == "c:/this/python2.5"
assert spec.path == "d:\hello" assert spec.chdir == "d:\hello"
assert spec.xyz is None assert not hasattr(spec, 'xyz')
py.test.raises(AttributeError, "spec._hello") py.test.raises(AttributeError, "spec._hello")
spec = XSpec("socket=192.168.102.2:8888//python=python2.5") spec = XSpec("socket=192.168.102.2:8888//python=python2.5")
assert spec.socket == "192.168.102.2:8888" assert spec.socket == "192.168.102.2:8888"
assert spec.python == "python2.5" assert spec.python == "python2.5"
assert spec.path is None assert spec.chdir is None
spec = XSpec("ssh=user@host//path=/hello/this//python=/usr/bin/python2.5") spec = XSpec("ssh=user@host//chdir=/hello/this//python=/usr/bin/python2.5")
assert spec.ssh == "user@host" assert spec.ssh == "user@host"
assert spec.python == "/usr/bin/python2.5" assert spec.python == "/usr/bin/python2.5"
assert spec.path == "/hello/this" assert spec.chdir == "/hello/this"
spec = XSpec("popen") spec = XSpec("popen")
assert spec.popen == True assert spec.popen == True
@ -28,7 +28,17 @@ class TestXSpec:
def test__samefilesystem(self): def test__samefilesystem(self):
assert XSpec("popen")._samefilesystem() assert XSpec("popen")._samefilesystem()
assert XSpec("popen//python=123")._samefilesystem() assert XSpec("popen//python=123")._samefilesystem()
assert not XSpec("popen//path=hello")._samefilesystem() assert not XSpec("popen//chdir=hello")._samefilesystem()
def test__spec_spec(self):
for x in ("popen", "popen//python=this"):
assert XSpec(x)._spec == x
def test_hash_equality(self):
assert XSpec("popen") == XSpec("popen")
assert hash(XSpec("popen")) == hash(XSpec("popen"))
assert XSpec("popen//python=123") != XSpec("popen")
assert hash(XSpec("socket=hello:8080")) != hash(XSpec("popen"))
class TestMakegateway: class TestMakegateway:
def test_popen(self): def test_popen(self):

View File

@ -9,22 +9,36 @@ class XSpec:
* keys are not allowed to start with underscore * keys are not allowed to start with underscore
* if no "=value" is given, assume a boolean True value * if no "=value" is given, assume a boolean True value
""" """
def __init__(self, *strings): # XXX for now we are very restrictive about actually allowed key-values
for string in strings: popen = ssh = socket = python = chdir = None
def __init__(self, string):
self._spec = string
for keyvalue in string.split("//"): for keyvalue in string.split("//"):
i = keyvalue.find("=") i = keyvalue.find("=")
if i == -1: if i == -1:
setattr(self, keyvalue, True) key, value = keyvalue, True
else: else:
setattr(self, keyvalue[:i], keyvalue[i+1:]) key, value = keyvalue[:i], keyvalue[i+1:]
# XXX be restrictive for now
if key not in XSpec.__dict__:
raise AttributeError("%r not a valid attribute" % key)
setattr(self, key, value)
def __getattr__(self, name): def __hash__(self):
if name[0] == "_": return hash(self._spec)
raise AttributeError(name) def __eq__(self, other):
return None return self._spec == getattr(other, '_spec', None)
def __ne__(self, other):
return self._spec != getattr(other, '_spec', None)
#def __getattr__(self, name):
# if name[0] == "_":
# raise AttributeError(name)
# return None
def _samefilesystem(self): def _samefilesystem(self):
return bool(self.popen and not self.path) return bool(self.popen and not self.chdir)
def makegateway(spec): def makegateway(spec):
if not isinstance(spec, XSpec): if not isinstance(spec, XSpec):