--- test/lib/tarantool_silverbox_server.py.orig 2011-05-14 12:16:32.000000000 +0000 +++ test/lib/tarantool_silverbox_server.py 2011-12-13 00:23:04.673107891 +0000 @@ -1,35 +1,234 @@ +import os +import stat import shutil import subprocess -import yaml -import ConfigParser -from tarantool_server import TarantoolServer, TarantoolConfigFile -from tarantool_admin import TarantoolAdmin -from silverbox import Silverbox - -class TarantoolSilverboxServer(TarantoolServer): - def __new__(cls, core="tarantool", module="silverbox"): - return TarantoolServer.__new__(cls) - - def __init__(self, core="tarantool", module="silverbox"): - TarantoolServer.__init__(self, core, module) - - def configure(self, config): - TarantoolServer.configure(self, config) - with open(self.config) as fp: - dummy_section_name = "tarantool" - config = ConfigParser.ConfigParser() - config.readfp(TarantoolConfigFile(fp, dummy_section_name)) - self.primary_port = int(config.get(dummy_section_name, "primary_port")) - self.admin_port = int(config.get(dummy_section_name, "admin_port")) - self.port = self.admin_port - self.admin = TarantoolAdmin("localhost", self.admin_port) - self.sql = Silverbox("localhost", self.primary_port) - - def init(self): -# init storage - subprocess.check_call([self.binary, "--init_storage"], - cwd = self.vardir, +import pexpect +import sys +import signal +import time +import socket +import daemon +import glob + +def wait_until_connected(host, port): + """Wait until the server is started and accepting connections""" + is_connected = False + while not is_connected: + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((host, port)) + is_connected = True + sock.close() + except socket.error as e: + time.sleep(0.001) + + +def prepare_gdb(args): + """Prepare server startup arguments to run under gdb.""" + if "TERM" in os.environ: + term = os.environ["TERM"] + else: + term = "xterm" + + if term not in ["xterm", "rxvt", "urxvt", "gnome-terminal", "konsole"]: + raise RuntimeError("--gdb: unsupported terminal {0}".format(term)) + + args = [ term, "-e ", "gdb", "-ex", "break main", "-ex", "run"] + args + return args + + +def prepare_valgrind(args, valgrind_opts): + "Prepare server startup arguments to run under valgrind." + args = ([ "valgrind", "--log-file=valgrind.log", "--quiet" ] + + valgrind_opts.split(" ") + args) + return args + + +def check_tmpfs_exists(): + return os.uname()[0] in 'Linux' and os.path.isdir("/dev/shm") + +def create_tmpfs_vardir(vardir): + os.mkdir(os.path.join("/dev/shm", vardir)) + os.symlink(os.path.join("/dev/shm", vardir), vardir) + +class TarantoolSilverboxServer: + """Server represents a single server instance. Normally, the + program operates with only one server, but in future we may add + replication slaves. The server is started once at the beginning + of each suite, and stopped at the end.""" + + def __init__(self, args, suite_ini): + """Set server options: path to configuration file, pid file, exe, etc.""" + self.args = args + self.suite_ini = suite_ini + self.path_to_pidfile = os.path.join(args.vardir, suite_ini["pidfile"]) + self.path_to_exe = None + self.abspath_to_exe = None + self.is_started = False + + def install(self, silent = False): + """Start server instance: check if the old one exists, kill it + if necessary, create necessary directories and files, start + the server. The server working directory is taken from 'vardir', + specified in the prgoram options. + Currently this is implemented for tarantool_silverbox only.""" + + vardir = self.args.vardir + + if not silent: + print "Installing the server..." + + if self.path_to_exe == None: + self.path_to_exe = self.find_exe() + self.abspath_to_exe = os.path.abspath(self.path_to_exe) + + if not silent: + print " Found executable at " + self.path_to_exe + "." + + if not silent: + print " Creating and populating working directory in " +\ + vardir + "..." + + if os.access(vardir, os.F_OK): + if not silent: + print " Found old vardir, deleting..." + self.kill_old_server() + for filename in (glob.glob(os.path.join(vardir, "*.snap")) + + glob.glob(os.path.join(vardir, "*.inprogress")) + + glob.glob(os.path.join(vardir, "*.xlog")) + + glob.glob(os.path.join(vardir, "*.cfg")) + + glob.glob(os.path.join(vardir, "*.log")) + + glob.glob(os.path.join(vardir, "*.core.*")) + + glob.glob(os.path.join(vardir, "core"))): + os.remove(filename) + else: + if (self.args.mem == True and check_tmpfs_exists() and + os.path.basename(vardir) == vardir): + create_tmpfs_vardir(vardir) + else: + os.mkdir(vardir) + + shutil.copy(self.suite_ini["config"], self.args.vardir) + + subprocess.check_call([self.abspath_to_exe, "--init_storage"], + cwd = self.args.vardir, # catch stdout/stderr to not clutter output stdout = subprocess.PIPE, stderr = subprocess.PIPE) + p = subprocess.Popen([self.abspath_to_exe, "--version"], + cwd = self.args.vardir, + stdout = subprocess.PIPE) + version = p.stdout.read().rstrip() + p.wait() + + if not silent: + print "Starting {0} {1}.".format(os.path.basename(self.abspath_to_exe), + version) + + def start(self, silent = False): + + if self.is_started: + if not silent: + print "The server is already started." + return + + if not silent: + print "Starting the server..." + + args = [self.abspath_to_exe] + + if (self.args.start_and_exit and + not self.args.valgrind and not self.args.gdb): + args.append("--daemonize") + if self.args.gdb: + args = prepare_gdb(args) + elif self.args.valgrind: + args = prepare_valgrind(args, self.args.valgrind_opts) + + if self.args.start_and_exit and self.args.valgrind: + pid = os.fork() + if pid > 0: + os.wait() + else: + with daemon.DaemonContext(working_directory = self.args.vardir): + os.execvp(args[0], args) + else: + self.server = pexpect.spawn(args[0], args[1:], cwd = self.args.vardir) + if self.args.start_and_exit: + self.server.wait() + +# wait until the server is connectedk + if self.args.gdb and self.args.start_and_exit: + time.sleep(1) + elif not self.args.start_and_exit and not self.args.gdb: + self.server.expect_exact("entering event loop") + else: + wait_until_connected(self.suite_ini["host"], self.suite_ini["port"]) + +# Set is_started flag, to nicely support cleanup during an exception. + self.is_started = True + + + def stop(self, silent = False): + """Stop server instance. Do nothing if the server is not started, + to properly shut down the server in case of an exception during + start up.""" + if self.is_started: + if not silent: + print "Stopping the server..." + if self.args.gdb: + self.kill_old_server(True) + self.server.terminate() + self.server.expect(pexpect.EOF) + self.is_started = False + elif not silent: + print "The server is not started." + + def restart(self): + self.stop(True) + self.start(True) + + def test_option(self, option_list_str): + args = [self.abspath_to_exe] + option_list_str.split() + print " ".join([os.path.basename(self.abspath_to_exe)] + args[1:]) + output = subprocess.Popen(args, + cwd = self.args.vardir, + stdout = subprocess.PIPE, + stderr = subprocess.STDOUT).stdout.read() + print output + + + def find_exe(self): + """Locate server executable in the bindir. We just take + the first thing looking like an exe in there.""" + + print " Looking for server binary in {0}...".format(self.args.bindir) + if (os.access(self.args.bindir, os.F_OK) == False or + stat.S_ISDIR(os.stat(self.args.bindir).st_mode) == False): + raise RuntimeError("Directory " + self.args.bindir + " doesn't exist") + + for f in os.listdir(self.args.bindir): + f = os.path.join(self.args.bindir, f) + st_mode = os.stat(f).st_mode + if stat.S_ISREG(st_mode) and st_mode & stat.S_IXUSR: + return f + + raise RuntimeError("Can't find server executable in " + self.args.bindir) + + def kill_old_server(self, silent = False): + """Kill old server instance if it exists.""" + if os.access(self.path_to_pidfile, os.F_OK) == False: + return # Nothing to do + pid = 0 + with open(self.path_to_pidfile) as f: + pid = int(f.read()) + if not silent: + print " Found old server, pid {0}, killing...".format(pid) + try: + os.kill(pid, signal.SIGTERM) + while os.kill(pid, 0) != -1: + time.sleep(0.001) + except OSError: + pass +