You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
5.2 KiB
Python
206 lines
5.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import os
|
|
import atexit
|
|
import subprocess
|
|
import socket
|
|
import select
|
|
import signal
|
|
import time
|
|
|
|
import base
|
|
|
|
__all__ = [ "Service", "ServiceException", "devnull", "Lighttpd", "FastCGI" ]
|
|
|
|
class ServiceException(Exception):
|
|
def __init__(self, value): self.value = value
|
|
def __str__(self): return repr(self.value)
|
|
|
|
def devnull():
|
|
try:
|
|
f = open("/dev/null", "r")
|
|
return f
|
|
except:
|
|
return None
|
|
|
|
|
|
straceargs = [ 'strace', '-tt', '-f', '-s', '4096', '-o' ]
|
|
trussargs = [ 'truss', '-d', '-f', '-s', '4096', '-o' ]
|
|
|
|
def preexec():
|
|
os.setsid()
|
|
|
|
def procwait(proc, timeout = 3):
|
|
ts = time.time()
|
|
while True:
|
|
if proc.poll() is not None: return True
|
|
seconds_passed = time.time() - ts
|
|
if seconds_passed > timeout:
|
|
return False
|
|
time.sleep(0.1)
|
|
|
|
|
|
class Service(object):
|
|
name = None
|
|
|
|
def __init__(self):
|
|
self.proc = None
|
|
self.tests = None
|
|
self.failed = False
|
|
|
|
def devnull(self):
|
|
return devnull()
|
|
|
|
def fork(self, *args, **kwargs):
|
|
if kwargs.has_key('inp'):
|
|
inp = kwargs['inp']
|
|
else:
|
|
inp = devnull()
|
|
|
|
if None == self.name:
|
|
raise ServiceException("Service needs a name!")
|
|
if base.Env.debug:
|
|
logfile = None
|
|
else:
|
|
logfile = open(self.log, "w")
|
|
|
|
if base.Env.strace:
|
|
slog = self.tests.PrepareFile("log/strace-%s.log" % self.name, "")
|
|
args = straceargs + [ slog ] + list(args)
|
|
elif base.Env.truss:
|
|
tlog = self.tests.PrepareFile("log/truss-%s.log" % self.name, "")
|
|
args = trussargs + [ tlog ] + list(args)
|
|
|
|
print >> base.Env.log, "Spawning '%s': %s" % (self.name, ' '.join(args))
|
|
proc = subprocess.Popen(args, stdin = inp, stdout = logfile, stderr = logfile, close_fds = True, preexec_fn = preexec)
|
|
if None != inp: inp.close()
|
|
if None != logfile: logfile.close()
|
|
self.proc = proc
|
|
atexit.register(self.kill)
|
|
|
|
def kill(self):
|
|
proc = self.proc
|
|
if None == proc: return
|
|
self.proc = None
|
|
if None == proc.poll():
|
|
print >> base.Env.log, "Terminating service '%s'" % (self.name)
|
|
try:
|
|
os.killpg(proc.pid, signal.SIGINT)
|
|
proc.terminate()
|
|
except:
|
|
pass
|
|
print >> base.Env.log, "Waiting for service '%s'" % (self.name)
|
|
if base.Env.wait: proc.wait()
|
|
while not procwait(proc):
|
|
try:
|
|
os.killpg(proc.pid, signal.SIGINT)
|
|
proc.terminate()
|
|
except:
|
|
pass
|
|
|
|
def portfree(self, port):
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
try:
|
|
s.connect(("127.0.0.1", port))
|
|
except:
|
|
pass
|
|
else:
|
|
raise ServiceException("Cannot start service '%s', port 127.0.0.1:%i already in use" % (self.name, port))
|
|
finally:
|
|
s.close()
|
|
|
|
def waitconnect(self, port):
|
|
timeout = 5*10
|
|
while True:
|
|
if None != self.proc.poll():
|
|
raise ServiceException("Service %s died before we could establish a connection" % (self.name))
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
try:
|
|
s.connect(("127.0.0.1", port))
|
|
except:
|
|
pass
|
|
else:
|
|
return True
|
|
finally:
|
|
s.close()
|
|
select.select([], [], [], 0.1)
|
|
timeout -= 1
|
|
if 0 > timeout:
|
|
raise ServiceException("Timeout: cannot establish a connection to service %s on port %i" % (self.name, port))
|
|
|
|
def _prepare(self):
|
|
self.log = self.tests.PrepareFile("log/service-%s.log" % self.name, "")
|
|
self.failed = True
|
|
self.Prepare()
|
|
self.failed = False
|
|
|
|
def _cleanup(self):
|
|
self.kill()
|
|
if not base.Env.force_cleanup and self.failed:
|
|
return
|
|
self.tests.CleanupFile("log/service-%s.log" % self.name)
|
|
self.tests.CleanupFile("log/strace-%s.log" % self.name)
|
|
self.tests.CleanupFile("log/truss-%s.log" % self.name)
|
|
self.Cleanup()
|
|
|
|
def _stop(self):
|
|
self.kill()
|
|
self.Stop()
|
|
|
|
def Prepare(self):
|
|
raise BaseException("Not implemented yet")
|
|
|
|
def Cleanup(self):
|
|
pass
|
|
|
|
def Stop(self):
|
|
pass
|
|
|
|
class Lighttpd(Service):
|
|
name = "lighttpd"
|
|
|
|
def TestConfig(self):
|
|
logfile = open(self.log, "w")
|
|
inp = self.devnull()
|
|
args = [base.Env.worker, '-m', base.Env.plugindir, '-c', base.Env.lighttpdconf, '-t']
|
|
print >> base.Env.log, "Testing lighttpd config: %s" % (' '.join(args))
|
|
proc = subprocess.Popen(args, stdin = inp, stdout = logfile, stderr = logfile, close_fds = True)
|
|
if None != inp: inp.close()
|
|
logfile.close()
|
|
status = proc.wait()
|
|
if 0 != status:
|
|
os.system("cat '%s'" % self.log)
|
|
raise BaseException("testing lighttpd config failed with returncode %i" % (status))
|
|
|
|
def Prepare(self):
|
|
self.TestConfig()
|
|
|
|
self.portfree(base.Env.port)
|
|
if base.Env.no_angel:
|
|
self.fork(base.Env.worker, '-m', base.Env.plugindir, '-c', base.Env.lighttpdconf)
|
|
else:
|
|
self.fork(base.Env.angel, '-m', base.Env.plugindir, '-c', base.Env.angelconf)
|
|
self.waitconnect(base.Env.port)
|
|
|
|
class FastCGI(Service):
|
|
binary = [None]
|
|
|
|
def __init__(self):
|
|
self.sockfile = os.path.join(base.Env.dir, "tmp", "sockets", self.name + ".sock")
|
|
super(FastCGI, self).__init__()
|
|
|
|
def Prepare(self):
|
|
sockdir = self.tests.PrepareDir(os.path.join("tmp", "sockets"))
|
|
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
sock.bind(os.path.relpath(self.sockfile))
|
|
sock.listen(8)
|
|
self.fork(*self.binary, inp = sock)
|
|
|
|
def Cleanup(self):
|
|
if None != self.sockfile:
|
|
try:
|
|
os.remove(self.sockfile)
|
|
except BaseException, e:
|
|
print >>sys.stderr, "Couldn't delete socket '%s': %s" % (self.sockfile, e)
|
|
self.tests.CleanupDir(os.path.join("tmp", "sockets"))
|