2009-03-04 01:42:32 +08:00
|
|
|
"""
|
|
|
|
instantiating, managing and rsyncing to hosts
|
|
|
|
|
|
|
|
Host specification strings and implied gateways:
|
|
|
|
|
|
|
|
socket:hostname:port:path SocketGateway
|
2009-03-17 00:53:52 +08:00
|
|
|
popen[-executable][:path] PopenGateway
|
2009-03-04 01:42:32 +08:00
|
|
|
[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 sys, os
|
|
|
|
from py.__.test.dsession.masterslave import MasterNode
|
|
|
|
from py.__.test import event
|
2009-03-18 06:41:56 +08:00
|
|
|
from py.__.execnet.channel import RemoteError
|
2009-03-04 01:42:32 +08:00
|
|
|
|
2009-03-18 06:41:56 +08:00
|
|
|
NO_ENDMARKER_WANTED = object()
|
2009-03-04 01:42:32 +08:00
|
|
|
|
|
|
|
class GatewaySpec(object):
|
2009-03-18 20:05:18 +08:00
|
|
|
python = None
|
2009-03-17 00:53:52 +08:00
|
|
|
def __init__(self, spec, defaultjoinpath="pyexecnetcache"):
|
2009-03-20 02:25:13 +08:00
|
|
|
self._spec = spec
|
2009-03-17 00:53:52 +08:00
|
|
|
if spec == "popen" or spec.startswith("popen:"):
|
2009-03-18 09:13:07 +08:00
|
|
|
parts = spec.split(":", 2)
|
|
|
|
self.type = self.address = parts.pop(0)
|
|
|
|
if parts:
|
2009-03-19 04:49:38 +08:00
|
|
|
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
|
2009-03-18 09:13:07 +08:00
|
|
|
if parts:
|
|
|
|
self.joinpath = parts.pop(0)
|
|
|
|
else:
|
|
|
|
self.joinpath = ""
|
2009-03-18 20:05:18 +08:00
|
|
|
if not self.python:
|
|
|
|
self.python = py.std.sys.executable
|
|
|
|
|
2009-03-04 01:42:32 +08:00
|
|
|
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:]
|
2009-03-18 09:13:07 +08:00
|
|
|
parts = spec.split(":", 2)
|
2009-03-04 01:42:32 +08:00
|
|
|
self.address = parts.pop(0)
|
2009-03-18 09:13:07 +08:00
|
|
|
self.python = parts and parts.pop(0) or "python"
|
2009-03-04 01:42:32 +08:00
|
|
|
self.joinpath = parts and parts.pop(0) or ""
|
2009-03-17 05:17:14 +08:00
|
|
|
self.type = "ssh"
|
2009-03-17 00:53:52 +08:00
|
|
|
if not self.joinpath and not self.inplacelocal():
|
|
|
|
self.joinpath = defaultjoinpath
|
2009-03-04 01:42:32 +08:00
|
|
|
|
|
|
|
def inplacelocal(self):
|
|
|
|
return bool(self.type == "popen" and not self.joinpath)
|
|
|
|
|
|
|
|
def __str__(self):
|
2009-03-20 02:25:13 +08:00
|
|
|
return "<GatewaySpec %s>" % self._spec
|
2009-03-04 01:42:32 +08:00
|
|
|
__repr__ = __str__
|
|
|
|
|
2009-03-18 09:13:07 +08:00
|
|
|
def makegateway(self, waitclose=True):
|
2009-03-04 01:42:32 +08:00
|
|
|
if self.type == "popen":
|
2009-03-18 09:13:07 +08:00
|
|
|
gw = py.execnet.PopenGateway(python=self.python)
|
2009-03-04 01:42:32 +08:00
|
|
|
elif self.type == "socket":
|
|
|
|
gw = py.execnet.SocketGateway(*self.address)
|
|
|
|
elif self.type == "ssh":
|
2009-03-18 09:13:07 +08:00
|
|
|
gw = py.execnet.SshGateway(self.address, remotepython=self.python)
|
2009-03-04 01:42:32 +08:00
|
|
|
if self.joinpath:
|
2009-03-17 05:17:14 +08:00
|
|
|
channel = gw.remote_exec("""
|
|
|
|
import os
|
|
|
|
path = %r
|
|
|
|
try:
|
|
|
|
os.chdir(path)
|
|
|
|
except OSError:
|
|
|
|
os.mkdir(path)
|
|
|
|
os.chdir(path)
|
|
|
|
""" % self.joinpath)
|
2009-03-04 01:42:32 +08:00
|
|
|
if waitclose:
|
|
|
|
channel.waitclose()
|
|
|
|
else:
|
|
|
|
if waitclose:
|
|
|
|
gw.remote_exec("").waitclose()
|
|
|
|
gw.spec = self
|
|
|
|
return gw
|
|
|
|
|
|
|
|
class GatewayManager:
|
2009-03-18 06:41:56 +08:00
|
|
|
RemoteError = RemoteError
|
|
|
|
|
2009-03-04 01:42:32 +08:00
|
|
|
def __init__(self, specs):
|
2009-03-17 19:53:09 +08:00
|
|
|
self.specs = [GatewaySpec(spec) for spec in specs]
|
|
|
|
self.gateways = []
|
2009-03-04 01:42:32 +08:00
|
|
|
|
|
|
|
def trace(self, msg):
|
2009-03-20 02:25:13 +08:00
|
|
|
self.notify("trace", "gatewaymanage", msg)
|
|
|
|
|
|
|
|
def notify(self, eventname, *args, **kwargs):
|
|
|
|
py._com.pyplugins.notify(eventname, *args, **kwargs)
|
2009-03-04 01:42:32 +08:00
|
|
|
|
|
|
|
def makegateways(self):
|
2009-03-17 19:53:09 +08:00
|
|
|
assert not self.gateways
|
|
|
|
for spec in self.specs:
|
2009-03-20 02:25:13 +08:00
|
|
|
gw = spec.makegateway()
|
|
|
|
self.gateways.append(gw)
|
|
|
|
gw.id = "[%s]" % len(self.gateways)
|
|
|
|
self.notify("gwmanage_newgateway", gw)
|
2009-03-17 19:53:09 +08:00
|
|
|
|
|
|
|
def getgateways(self, remote=True, inplacelocal=True):
|
2009-03-18 06:41:56 +08:00
|
|
|
if not self.gateways and self.specs:
|
|
|
|
self.makegateways()
|
2009-03-17 19:53:09 +08:00
|
|
|
l = []
|
|
|
|
for gw in self.gateways:
|
|
|
|
if gw.spec.inplacelocal():
|
|
|
|
if inplacelocal:
|
|
|
|
l.append(gw)
|
|
|
|
else:
|
|
|
|
if remote:
|
|
|
|
l.append(gw)
|
2009-03-20 01:05:41 +08:00
|
|
|
return py.execnet.MultiGateway(gateways=l)
|
2009-03-04 01:42:32 +08:00
|
|
|
|
|
|
|
def multi_exec(self, source, inplacelocal=True):
|
2009-03-17 05:17:14 +08:00
|
|
|
""" remote execute code on all gateways.
|
|
|
|
@param inplacelocal=False: don't send code to inplacelocal hosts.
|
|
|
|
"""
|
2009-03-17 19:53:09 +08:00
|
|
|
multigw = self.getgateways(inplacelocal=inplacelocal)
|
|
|
|
return multigw.remote_exec(source)
|
2009-03-04 01:42:32 +08:00
|
|
|
|
|
|
|
def multi_chdir(self, basename, inplacelocal=True):
|
2009-03-17 05:17:14 +08:00
|
|
|
""" perform a remote chdir to the given path, may be relative.
|
|
|
|
@param inplacelocal=False: don't send code to inplacelocal hosts.
|
|
|
|
"""
|
2009-03-04 01:42:32 +08:00
|
|
|
self.multi_exec("import os ; os.chdir(%r)" % basename,
|
2009-03-17 05:17:14 +08:00
|
|
|
inplacelocal=inplacelocal).waitclose()
|
2009-03-04 01:42:32 +08:00
|
|
|
|
|
|
|
def rsync(self, source, notify=None, verbose=False, ignores=None):
|
2009-03-17 05:17:14 +08:00
|
|
|
""" perform rsync to all remote hosts.
|
|
|
|
"""
|
2009-03-04 01:42:32 +08:00
|
|
|
rsync = HostRSync(source, verbose=verbose, ignores=ignores)
|
2009-03-20 08:34:59 +08:00
|
|
|
seen = {}
|
2009-03-17 19:53:09 +08:00
|
|
|
for gateway in self.gateways:
|
|
|
|
spec = gateway.spec
|
2009-03-04 01:42:32 +08:00
|
|
|
if not spec.inplacelocal():
|
2009-03-20 08:34:59 +08:00
|
|
|
key = spec.type, spec.address, spec.joinpath
|
|
|
|
if key in seen:
|
|
|
|
continue
|
2009-03-04 01:42:32 +08:00
|
|
|
def finished():
|
|
|
|
if notify:
|
|
|
|
notify("rsyncrootready", spec, source)
|
|
|
|
rsync.add_target_host(gateway, finished=finished)
|
2009-03-20 08:34:59 +08:00
|
|
|
seen[key] = gateway
|
|
|
|
if seen:
|
|
|
|
self.notify("gwmanage_rsyncstart", source=source, gateways=seen.values())
|
2009-03-04 01:42:32 +08:00
|
|
|
rsync.send()
|
2009-03-20 08:34:59 +08:00
|
|
|
self.notify("gwmanage_rsyncfinish", source=source, gateways=seen.values())
|
2009-03-04 01:42:32 +08:00
|
|
|
else:
|
|
|
|
self.trace("rsync: nothing to do.")
|
|
|
|
|
|
|
|
def exit(self):
|
2009-03-17 19:53:09 +08:00
|
|
|
while self.gateways:
|
|
|
|
gw = self.gateways.pop()
|
2009-03-04 01:42:32 +08:00
|
|
|
self.trace("exiting gateway %s" % gw)
|
|
|
|
gw.exit()
|
|
|
|
|
|
|
|
class HostRSync(py.execnet.RSync):
|
|
|
|
""" RSyncer that filters out common files
|
|
|
|
"""
|
|
|
|
def __init__(self, sourcedir, *args, **kwargs):
|
|
|
|
self._synced = {}
|
|
|
|
ignores= None
|
|
|
|
if 'ignores' in kwargs:
|
|
|
|
ignores = kwargs.pop('ignores')
|
|
|
|
self._ignores = ignores or []
|
|
|
|
super(HostRSync, self).__init__(sourcedir=sourcedir, **kwargs)
|
|
|
|
|
|
|
|
def filter(self, path):
|
|
|
|
path = py.path.local(path)
|
|
|
|
if not path.ext in ('.pyc', '.pyo'):
|
|
|
|
if not path.basename.endswith('~'):
|
|
|
|
if path.check(dotfile=0):
|
|
|
|
for x in self._ignores:
|
|
|
|
if path == x:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
|
|
|
|
def add_target_host(self, gateway, finished=None):
|
|
|
|
remotepath = os.path.basename(self._sourcedir)
|
|
|
|
super(HostRSync, self).add_target(gateway, remotepath,
|
|
|
|
finishedcallback=finished,
|
|
|
|
delete=True,)
|
|
|
|
|
|
|
|
def _report_send_file(self, gateway, modified_rel_path):
|
|
|
|
if self._verbose:
|
|
|
|
path = os.path.basename(self._sourcedir) + "/" + modified_rel_path
|
|
|
|
remotepath = gateway.spec.joinpath
|
|
|
|
print '%s:%s <= %s' % (gateway.remoteaddress, remotepath, path)
|