""" instantiating, managing and rsyncing to hosts """ import py import sys, os from py.__.execnet.channel import RemoteError NO_ENDMARKER_WANTED = object() class GatewayManager: RemoteError = RemoteError def __init__(self, specs, defaultchdir="pyexecnetcache"): self.gateways = [] self.specs = [] for spec in specs: if not isinstance(spec, py.execnet.XSpec): spec = py.execnet.XSpec(spec) if not spec.chdir and not spec.popen: spec.chdir = defaultchdir self.specs.append(spec) def trace(self, msg): self.notify("trace", "gatewaymanage", msg) def notify(self, eventname, *args, **kwargs): py._com.pyplugins.notify(eventname, *args, **kwargs) def makegateways(self): assert not self.gateways for spec in self.specs: gw = py.execnet.makegateway(spec) self.gateways.append(gw) gw.id = "[%s]" % len(self.gateways) self.notify("gwmanage_newgateway", gw) def getgateways(self, remote=True, inplacelocal=True): if not self.gateways and self.specs: self.makegateways() l = [] for gw in self.gateways: if gw.spec._samefilesystem(): if inplacelocal: l.append(gw) else: if remote: l.append(gw) return py.execnet.MultiGateway(gateways=l) def multi_exec(self, source, inplacelocal=True): """ remote execute code on all gateways. @param inplacelocal=False: don't send code to inplacelocal hosts. """ multigw = self.getgateways(inplacelocal=inplacelocal) return multigw.remote_exec(source) def multi_chdir(self, basename, inplacelocal=True): """ perform a remote chdir to the given path, may be relative. @param inplacelocal=False: don't send code to inplacelocal hosts. """ self.multi_exec("import os ; os.chdir(%r)" % basename, inplacelocal=inplacelocal).waitclose() def rsync(self, source, notify=None, verbose=False, ignores=None): """ perform rsync to all remote hosts. """ rsync = HostRSync(source, verbose=verbose, ignores=ignores) seen = {} for gateway in self.gateways: spec = gateway.spec if not spec._samefilesystem(): if spec in seen: continue def finished(): if notify: notify("rsyncrootready", spec, source) rsync.add_target_host(gateway, finished=finished) seen[spec] = gateway if seen: self.notify("gwmanage_rsyncstart", source=source, gateways=seen.values()) rsync.send() self.notify("gwmanage_rsyncfinish", source=source, gateways=seen.values()) else: self.trace("rsync: nothing to do.") def exit(self): while self.gateways: gw = self.gateways.pop() 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.chdir print '%s:%s <= %s' % (gateway.remoteaddress, remotepath, path)