203 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			203 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
	
| import sys, os
 | |
| import py
 | |
| import time
 | |
| import thread, threading 
 | |
| from py.__.test.rsession.master import \
 | |
|      setup_slave, MasterNode
 | |
| from py.__.test.rsession import report 
 | |
| 
 | |
| class HostInfo(object):
 | |
|     """ Class trying to store all necessary attributes
 | |
|     for host
 | |
|     """
 | |
|     host_ids = {}
 | |
|     
 | |
|     def __init__(self, hostname, relpath=None):
 | |
|         self.hostid = self._getuniqueid(hostname)
 | |
|         self.hostname = hostname
 | |
|         self.relpath = relpath
 | |
| 
 | |
|     def _getuniqueid(cls, hostname):
 | |
|         if not hostname in cls.host_ids:
 | |
|             cls.host_ids[hostname] = 0
 | |
|             return hostname
 | |
|         retval = hostname + '_' + str(cls.host_ids[hostname])
 | |
|         cls.host_ids[hostname] += 1
 | |
|         return retval
 | |
|     _getuniqueid = classmethod(_getuniqueid)
 | |
| 
 | |
|     def __str__(self):
 | |
|         return "<HostInfo %s>" % (self.hostname,)
 | |
| 
 | |
|     def __hash__(self):
 | |
|         return hash(self.hostid)
 | |
| 
 | |
|     def __eq__(self, other):
 | |
|         return self.hostid == other.hostid
 | |
| 
 | |
|     def __ne__(self, other):
 | |
|         return not self == other
 | |
| 
 | |
| class HostRSync(py.execnet.RSync):
 | |
|     """ An rsync wrapper which filters out *~, .svn/ and *.pyc
 | |
|     """
 | |
|     def __init__(self, config):
 | |
|         py.execnet.RSync.__init__(self, delete=True)
 | |
|         self.config = config
 | |
| 
 | |
|     def filter(self, path):
 | |
|         if path.endswith('.pyc') or path.endswith('~'):
 | |
|             return False
 | |
|         dir, base = os.path.split(path)
 | |
|         try:
 | |
|             rsync_roots = self.config.getvalue_from_confpath("dist_rsyncroots",
 | |
|                                                              dir)
 | |
|         except AttributeError:
 | |
|             rsync_roots = None
 | |
|         if base == '.svn':
 | |
|             return False
 | |
|         if rsync_roots is None:
 | |
|             return True
 | |
|         return base in rsync_roots
 | |
| 
 | |
| class DummyGateway(object):
 | |
|     pass
 | |
| 
 | |
| class HostOptions(object):
 | |
|     """ Dummy container for host options, not to keep that
 | |
|     as different function parameters, mostly related to
 | |
|     tests only
 | |
|     """
 | |
|     def __init__(self, remote_python="python",
 | |
|                  optimise_localhost=True, do_sync=True,
 | |
|                  create_gateways=True):
 | |
|         self.remote_python = remote_python
 | |
|         self.optimise_localhost = optimise_localhost
 | |
|         self.do_sync = do_sync
 | |
|         self.create_gateways = create_gateways
 | |
| 
 | |
| class HostManager(object):
 | |
|     def __init__(self, sshhosts, config, options=HostOptions()):
 | |
|         self.sshhosts = sshhosts
 | |
|         self.config = config
 | |
|         self.options = options
 | |
|         if not options.create_gateways:
 | |
|             self.prepare_gateways = self.prepare_dummy_gateways
 | |
|         #assert pkgdir.join("__init__.py").check(), (
 | |
|         #    "%s probably wrong" %(pkgdir,))
 | |
| 
 | |
|     def prepare_dummy_gateways(self):
 | |
|         for host in self.sshhosts:
 | |
|             gw = DummyGateway()
 | |
|             host.gw = gw
 | |
|             gw.host = host
 | |
|         return self.sshhosts
 | |
| 
 | |
|     def prepare_ssh_gateway(self, host):
 | |
|         if self.options.remote_python is None:
 | |
|             gw = py.execnet.SshGateway(host.hostname)
 | |
|         else:
 | |
|             gw = py.execnet.SshGateway(host.hostname,
 | |
|                                        remotepython=self.options.remote_python)
 | |
|         return gw
 | |
| 
 | |
|     def prepare_popen_rsync_gateway(self, host):
 | |
|         """ Popen gateway, but with forced rsync
 | |
|         """
 | |
|         from py.__.execnet.register import PopenCmdGateway
 | |
|         gw = PopenCmdGateway("cd ~; python -u -c 'exec input()'")
 | |
|         if not host.relpath.startswith("/"):
 | |
|             host.relpath = os.environ['HOME'] + '/' + host.relpath
 | |
|         return gw
 | |
| 
 | |
|     def prepare_popen_gateway(self, host):
 | |
|         if self.options.remote_python is None:
 | |
|             gw = py.execnet.PopenGateway()
 | |
|         else:
 | |
|             gw = py.execnet.PopenGateway(python=self.options.remote_python)
 | |
|         host.relpath = str(self.config.topdir)
 | |
|         return gw
 | |
| 
 | |
|     def prepare_gateways(self):
 | |
|         for host in self.sshhosts:
 | |
|             if host.hostname == 'localhost':
 | |
|                 if not self.options.optimise_localhost:
 | |
|                     gw = self.prepare_popen_rsync_gateway(host)
 | |
|                 else:
 | |
|                     gw = self.prepare_popen_gateway(host)
 | |
|             else:
 | |
|                 gw = self.prepare_ssh_gateway(host)
 | |
|             host.gw = gw
 | |
|             gw.host = host
 | |
|         return self.sshhosts
 | |
| 
 | |
|     def need_rsync(self, rsynced, hostname, remoterootpath):
 | |
|         if (hostname, remoterootpath) in rsynced:
 | |
|             return False
 | |
|         if hostname == 'localhost' and self.options.optimise_localhost:
 | |
|             return False
 | |
|         return True
 | |
| 
 | |
|     def init_hosts(self, reporter, done_dict={}):
 | |
|         if done_dict is None:
 | |
|             done_dict = {}
 | |
| 
 | |
|         hosts = self.prepare_gateways()
 | |
|         
 | |
|         # rsyncing
 | |
|         rsynced = {}
 | |
| 
 | |
|         if self.options.do_sync:
 | |
|             rsync = HostRSync(self.config)
 | |
|         for host in hosts:
 | |
|             if not self.need_rsync(rsynced, host.hostname, host.relpath):
 | |
|                 reporter(report.HostReady(host))
 | |
|                 continue
 | |
|             rsynced[(host.hostname, host.relpath)] = True
 | |
|             def done(host=host):
 | |
|                 reporter(report.HostReady(host))
 | |
|             reporter(report.HostRSyncing(host))
 | |
|             if self.options.do_sync:
 | |
|                 rsync.add_target(host.gw, host.relpath, done)
 | |
|         if not self.options.do_sync:
 | |
|             return # for testing only
 | |
|         rsync.send(self.config.topdir)
 | |
|         # hosts ready
 | |
|         return self.setup_nodes(reporter, done_dict)
 | |
| 
 | |
|     def setup_nodes(self, reporter, done_dict):
 | |
|         nodes = []
 | |
|         for host in self.sshhosts:
 | |
|             ch = setup_slave(host.gw, host.relpath, self.config)
 | |
|             nodes.append(MasterNode(ch, reporter, done_dict))
 | |
|     
 | |
|         return nodes
 | |
| 
 | |
|     def teardown_hosts(self, reporter, channels, nodes,
 | |
|                        waiter=lambda : time.sleep(.1), exitfirst=False):
 | |
|         for channel in channels:
 | |
|             channel.send(None)
 | |
|     
 | |
|         clean = exitfirst
 | |
|         while not clean:
 | |
|             clean = True
 | |
|             for node in nodes:
 | |
|                 if node.pending:
 | |
|                     clean = False
 | |
|             waiter()
 | |
|         self.teardown_gateways(reporter, channels)
 | |
| 
 | |
|     def kill_channels(self, channels):
 | |
|         for channel in channels:
 | |
|             channel.send(42)
 | |
| 
 | |
|     def teardown_gateways(self, reporter, channels):
 | |
|         for channel in channels:
 | |
|             try:
 | |
|                 report.wrapcall(reporter, channel.waitclose)
 | |
|             except KeyboardInterrupt, SystemExit:
 | |
|                 raise
 | |
|             except:
 | |
|                 pass
 | |
|             channel.gateway.exit()
 |