Exemple #1
0
    def wait_until_started(self, wait_load=True):
        """ Wait until server is started.

        Server consists of two parts:
        1) wait until server is listening on sockets
        2) wait until server tells us his status

        """
        if wait_load:
            msg = 'entering the event loop|will retry binding|hot standby mode'
            p = self.process if not self.gdb and not self.lldb else None
            self.logfile_pos.seek_wait(msg, p, self.name)
        while True:
            try:
                temp = AdminConnection('localhost', self.admin.port)
                if not wait_load:
                    ans = yaml.safe_load(temp.execute("2 + 2"))
                    return True
                ans = yaml.safe_load(temp.execute('box.info.status'))[0]
                if ans in ('running', 'hot_standby', 'orphan'):
                    return True
                elif ans in ('loading'):
                    continue
                else:
                    raise Exception(
                        "Strange output for `box.info.status`: %s" % (ans))
            except socket.error as e:
                if e.errno == errno.ECONNREFUSED:
                    time.sleep(0.1)
                    continue
                raise
    def wait_until_started(self, wait_load=True):
        """ Wait until server is started.

        Server consists of two parts:
        1) wait until server is listening on sockets
        2) wait until server tells us his status

        """
        if wait_load:
            msg = 'entering the event loop|will retry binding|hot standby mode'
            p = self.process if not self.gdb and not self.lldb else None
            self.logfile_pos.seek_wait(msg, p, self.name)
        while True:
            try:
                temp = AdminConnection('localhost', self.admin.port)
                if not wait_load:
                    ans = yaml.safe_load(temp.execute("2 + 2"))
                    return True
                ans = yaml.safe_load(temp.execute('box.info.status'))[0]
                if ans in ('running', 'hot_standby', 'orphan'):
                    return True
                elif ans in ('loading'):
                    continue
                else:
                    raise Exception(
                        "Strange output for `box.info.status`: %s" % (ans)
                    )
            except socket.error as e:
                if e.errno == errno.ECONNREFUSED:
                    time.sleep(0.1)
                    continue
                raise
    def configure(self, config):
        def get_option(config, section, key):
            if not config.has_option(section, key):
                return None
            value = config.get(section, key)
            if value.isdigit():
                value = int(value)
            return value

        self.config = os.path.abspath(config)
        # now read the server config, we need some properties from it
        with open(self.config) as fp:
            dummy_section_name = "tarantool"
            config = ConfigParser.ConfigParser()
            config.readfp(TarantoolConfigFile(fp, dummy_section_name))

            self.pidfile = get_option(config, dummy_section_name, "pid_file")
            self.primary_port = get_option(config, dummy_section_name,
                                           "primary_port")
            self.admin_port = get_option(config, dummy_section_name,
                                         "admin_port")
            self.memcached_port = get_option(config, dummy_section_name,
                                             "memcached_port")

        self.port = self.admin_port
        self.admin = AdminConnection("localhost", self.admin_port)
        self.sql = BoxConnection("localhost", self.primary_port)
        if self.memcached_port != 0:
            # Run memcached client
            self.memcached = MemcachedConnection('localhost',
                                                 self.memcached_port)
    def configure(self, config):
        def get_option(config, section, key):
            if not config.has_option(section, key):
                return None
            value = config.get(section, key)
            if value.isdigit():
                value = int(value)
            return value
        self.config = os.path.abspath(config)
        # now read the server config, we need some properties from it
        with open(self.config) as fp:
            dummy_section_name = "tarantool"
            config = ConfigParser.ConfigParser()
            config.readfp(TarantoolConfigFile(fp, dummy_section_name))

            self.pidfile = get_option(config, dummy_section_name, "pid_file")
            self.primary_port = get_option(config, dummy_section_name, "primary_port")
            self.admin_port = get_option(config, dummy_section_name, "admin_port")
            self.memcached_port = get_option(config, dummy_section_name, "memcached_port")

        self.port = self.admin_port
        self.admin = AdminConnection("localhost", self.admin_port)
        self.sql = BoxConnection("localhost", self.primary_port)
        if self.memcached_port != 0:
            # Run memcached client
            self.memcached = MemcachedConnection('localhost', self.memcached_port)
Exemple #5
0
    def wait_until_started(self, wait_load=True):
        """ Wait until server is started.

        Server consists of two parts:
        1) wait until server is listening on sockets
        2) wait until server tells us his status

        """
        color_log('DEBUG: [Instance {}] Waiting until started '
                  '(wait_load={})\n'.format(self.name, str(wait_load)),
                  schema='info')

        if wait_load:
            msg = 'entering the event loop|will retry binding|hot standby mode'
            p = self.process if not self.gdb and not self.lldb else None
            self.logfile_pos.seek_wait(msg, p, self.name)
        while True:
            try:
                temp = AdminConnection('localhost', self.admin.port)
                if not wait_load:
                    ans = yaml.safe_load(temp.execute("2 + 2"))
                    color_log(" | Successful connection check; don't wait for "
                              "loading")
                    return True
                ans = yaml.safe_load(temp.execute('box.info.status'))[0]
                if ans in ('running', 'hot_standby', 'orphan'):
                    color_log(" | Started {} (box.info.status: '{}')\n".format(
                        format_process(self.process.pid), ans))
                    return True
                elif ans in ('loading',):
                    continue
                else:
                    raise Exception(
                        "Strange output for `box.info.status`: %s" % (ans)
                    )
            except socket.error as e:
                if e.errno == errno.ECONNREFUSED:
                    color_log(' | Connection refused; will retry every 0.1 '
                              'seconds...')
                    time.sleep(0.1)
                    continue
                raise
 def _admin(self, port):
     try:
         int(port)
     except ValueError as e:
         raise ValueError("Bad port number: '%s'" % port)
     if not hasattr(self, 'admin') or self.admin is None:
         self.admin = AdminConnection('localhost', port)
         return
     if self.admin.port != port:
         self.admin.port = port
         self.admin.reconnect()
    def wait_until_started(self):
        """ Wait until server is started.

        Server consists of two parts:
        1) wait until server is listening on sockets
        2) wait until server tells us his status

        """
        self.logfile_pos.seek_from('entering the event loop', self.process if not self.gdb else None)
        while True:
            try:
                temp = AdminConnection('localhost', self.admin.port)
                ans = yaml.load(temp.execute('box.info.status'))[0]
                if ans in ('running', 'hot_standby', 'orphan'):
                    return True
                else:
                    raise Exception("Strange output for `box.info.status`: %s" % (ans))
            except socket.error as e:
                if e.errno == errno.ECONNREFUSED:
                    time.sleep(0.1)
                    continue
                raise
Exemple #8
0
 def connection(self, ctype, cnames, sname):
     # we always get a list of connections as input here
     cname = cnames[0]
     if ctype == 'create':
         if sname not in self.servers:
             raise LuaPreprocessorException('Can\'t create connection to nonexistent server '+repr(sname))
         if cname in self.connections:
             raise LuaPreprocessorException('Connection {0} already exists'.format(repr(cname)))
         self.connections[cname] = AdminConnection('localhost', self.servers[sname].admin.port)
         self.connections[cname].connect()
     elif ctype == 'drop':
         if cname not in self.connections:
             raise LuaPreprocessorException('Can\'t drop nonexistent connection '+repr(cname))
         self.connections[cname].disconnect()
         self.connections.pop(cname)
     elif ctype == 'set':
         for i in cnames:
             if not i in self.connections:
                 raise LuaPreprocessorException('Can\'t set nonexistent connection '+repr(cname))
         self.curcon = [self.connections[i] for i in cnames]
     else:
         raise LuaPreprocessorException('Unknown command for connection: '+repr(ctype))
Exemple #9
0
admin("box.session.on_connect(1)")
admin("box.session.on_disconnect(1)")

# use of nil to clear the trigger
admin("type(box.session.on_connect(nil))")
admin("type(box.session.on_disconnect(nil))")
admin("type(box.session.on_connect(nil))")
admin("type(box.session.on_disconnect(nil))")

# check how connect/disconnect triggers work
admin("function inc() active_connections = active_connections + 1 end")
admin("function dec() active_connections = active_connections - 1 end")
admin("box.session.on_connect(inc)")
admin("box.session.on_disconnect(dec)")
admin("active_connections = 0")
con1 = AdminConnection('localhost', server.admin_port)
con1("active_connections")
con2 = AdminConnection('localhost', server.admin_port)
con2("active_connections")
con1.disconnect()
con2.disconnect()
admin("type(box.session.on_connect(nil))")
admin("type(box.session.on_disconnect(nil))")

# write audit trail of connect/disconnect into a space
admin("box.session.on_connect(function() box.insert(0, box.session.id()) end)")
admin("box.session.on_disconnect(function() box.delete(0, box.session.id()) end)")
con1("box.unpack('i', box.select(0, 0, box.session.id())[0]) == box.session.id()")
con1.disconnect()

# if on_connect() trigger raises an exception, the connection is dropped
Exemple #10
0
class TarantoolServer(Server):
    default_tarantool = {
            "bin":       "tarantool_box",
            "config":    "tarantool.cfg",
            "logfile":   "tarantool.log",
            "init":           "init.lua",
            "pidfile":         "box.pid",
            "name":            "default"}
    generate_ports = [
            'primary_port',
            'admin_port',
            ]
    generated_props = [
            'replication_source'
            ]
#----------------------------------PROPERTIES----------------------------------#
    @property
    def debug(self):
        return self.test_debug()
    @property
    def name(self):
        if not hasattr(self, '_name') or not self._name:
            return self.default_tarantool["name"]
        return self._name
    @name.setter
    def name(self, val):
        self._name = val

    @property
    def logfile(self):
        if not hasattr(self, '_logfile') or not self._logfile:
            return os.path.join(self.vardir, self.default_tarantool["logfile"])
        return self._logfile
    @logfile.setter
    def logfile(self, val):
        self._logfile = os.path.join(self.vardir, val)

    @property
    def pidfile(self):
        if not hasattr(self, '_pidfile') or not self._pidfile:
            return os.path.join(self.vardir, self.default_tarantool["pidfile"])
        return self._pidfile
    @pidfile.setter
    def pidfile(self, val):
        self._pidfile = os.path.join(self.vardir, val)

    @property
    def cfgfile(self):
        if not hasattr(self, '_cfgfile') or not self._cfgfile:
            return os.path.join(self.vardir, self.default_tarantool["config"])
        return self._cfgfile
    @cfgfile.setter
    def cfgfile(self, val):
        self._cfgfile = os.path.join(self.vardir, val)

    @property
    def cfgfile_source(self):
        if not hasattr(self, '_cfgfile_source'):
            raise ValueError("No config-file is specified")
        return self._cfgfile_source
    @cfgfile_source.setter
    def cfgfile_source(self, path):
        if path == None:
            if hasattr(self, '_cfgfile_source'):
                delattr(self, '_cfgfile_source')
            return
        self._cfgfile_source = os.path.abspath(path)

    @property
    def init_lua_source(self):
        if not hasattr(self, '_init_lua_source'): self._init_lua_source = None
        return self._init_lua_source
    @init_lua_source.setter
    def init_lua_source(self, val):
        if val is None:
            return
        self._init_lua_source = os.path.abspath(val)

    @property
    def builddir(self):
        if not hasattr(self, '_builddir'):
            raise ValueError("No build-dir is specified")
        return self._builddir
    @builddir.setter
    def builddir(self, val):
        if val is None:
            return
        self._builddir = os.path.abspath(val)

    @property
    def init_lua(self):
        return os.path.join(self.vardir, self.default_tarantool['init'])

    @property
    def logfile_pos(self):
        if not hasattr(self, '_logfile_pos'): self._logfile_pos = None
        return self._logfile_pos
    @logfile_pos.setter
    def logfile_pos(self, val):
        self._logfile_pos = TarantoolLog(val)
        self._logfile_pos.positioning()

    @property
    def shebang(self):
        if not hasattr(self, '_shebang'): self._shebang = None
        return self._shebang
    @shebang.setter
    def shebang(self, val):
        if val is None:
            if hasattr(self, '_shebang'):
                delattr(self, '_shebang')
            return
        self._shebang = os.path.abspath(val)

    @property
    def _admin(self):
        if not hasattr(self, 'admin'): self.admin = None
        return self.admin
    @_admin.setter
    def _admin(self, port):
        try:
            int(port)
        except ValueError as e:
            raise ValueError("Bad port number: '%s'" % port)
        if not hasattr(self, 'admin') or self.admin is None:
            self.admin = AdminConnection('localhost', port)
            return
        if self.admin.port != port:
            self.admin.port = port
            self.admin.reconnect()

    @property
    def _sql(self):
        if not hasattr(self, 'sql'): self.sql = None
        return self.sql
    @_sql.setter
    def _sql(self, port):
        try:
            port = int(port)
        except ValueError as e:
            raise ValueError("Bad port number: '%s'" % port)
        if not hasattr(self, 'sql') or self.sql is None:
            self.sql = BoxConnection('localhost', port)
            return
        if self.sql.port != port:
            self.sql.port = port
            self.sql.reconnect()

    @property
    def log_des(self):
        if not hasattr(self, '_log_des'): self._log_des = open(self.logfile, 'a')
        return self._log_des
    @log_des.deleter
    def log_des(self):
        if not hasattr(self, '_log_des'): return
        if not self._log_des.closed: self._log_des.closed()
        delattr(self, _log_des)

    @property
    def rpl_master(self):
        if not hasattr(self, '_rpl_master'): self._rpl_master = None
        return self._rpl_master
    @rpl_master.setter
    def rpl_master(self, val):
        if not isinstance(self, (TarantoolServer, None)):
            raise ValueError('Replication master must be Tarantool'
                    ' Server class, his derivation or None')
        self._rpl_master = val

    @property
    def hot_master(self):
        if not hasattr(self, '_hot_master'): self._hot_master = None
        return self._hot_master
    @hot_master.setter
    def hot_master(self, val):
        if not isinstance(self, (TarantoolServer, None)):
            raise ValueError('Hot-standby master must be Tarantool'
                    ' Server class, his derivation or None')
        self._hot_master = val


#------------------------------------------------------------------------------#

    def __new__(cls, ini=None):
        if ini is None:
            ini = {'core': 'tarantool'}
        if ('valgrind' in ini and ini['valgrind']) and ('gdb' in ini and ini['gdb']):
            raise OSError('Can\'t run under valgrind and gdb simultaniously')
        if 'valgrind' in ini and ini['valgrind']:
            cls = type('ValgrindTarantooServer', (ValgrindMixin, TarantoolServer), {})
        elif 'gdb' in ini and ini['gdb']:
            cls = type('GdbTarantoolServer', (GdbMixin, TarantoolServer), {})

        return super(TarantoolServer, cls).__new__(cls)

    def __init__(self, _ini=None):
        if _ini is None:
            _ini = {}
        ini = {
            'config': None,
            'core': 'tarantool',
            'gdb': False,
            'init_lua': None,
            'lua_libs': [],
            'random_ports': True,
            'valgrind': False,
            'vardir': None,
            'start_and_exit': False
        }; ini.update(_ini)
        Server.__init__(self, ini)
        self.generated_fields = self.generate_ports + self.generated_props
        self.testdir = os.path.abspath(os.curdir)
        self.re_vardir_cleanup += [
            "*.snap", "*.xlog", "*.inprogress",
            "*.cfg", "*.sup", "*.lua", "*.pid"]
        self.name = "default"
        self.conf = {}
        self.status = None
        #-----InitBasicVars-----#
        self.cfgfile_source = ini['config']
        self.core = ini['core']
        self.gdb = ini['gdb']
        self.init_lua_source = ini['init_lua']
        self.lua_libs = ini['lua_libs']
        self.random_ports = ini['random_ports']
        self.valgrind = ini['valgrind']
        self._start_and_exit = ini['start_and_exit']

    def __del__(self):
        self.stop()

    @classmethod
    def find_exe(cls, builddir, silent=True):
        cls.builddir = os.path.abspath(builddir)
        builddir = os.path.join(builddir, "src/box")
        path = builddir + os.pathsep + os.environ["PATH"]
        if not silent:
            color_stdout("Looking for server binary in ", schema='serv_text')
            color_stdout(path + ' ...\n', schema='path')
        for _dir in path.split(os.pathsep):
            exe = os.path.join(_dir, cls.default_tarantool["bin"])
            if os.access(exe, os.X_OK):
                cls.binary = os.path.abspath(exe)
                return exe
        raise RuntimeError("Can't find server executable in " + path)

    def install(self, silent=True):
        if not silent:
            color_stdout('Installing the server ...\n', schema='serv_text')
            color_stdout('    Found executable at ', schema='serv_text')
            color_stdout(self.binary + '\n', schema='path')
            color_stdout('    Creating and populating working directory in ', schema='serv_text')
            color_stdout(self.vardir + ' ...\n', schema='path')
        if not os.path.exists(self.vardir):
            os.makedirs(self.vardir)
        else:
            if not silent:
                color_stdout('    Found old vardir, deleting ...\n', schema='serv_text')
            self.kill_old_server()
            self.cleanup()
        self.copy_files()
        self.configure()
        return

    def deploy(self, silent=True):
        self.install(silent)
        if not self._start_and_exit:
            self.start(silent)
        else:
            self.start_and_exit()

    def configure(self, config=None):
        self.copy_config(config)
        self.port   = self.conf['admin_port']
        self._sql    = self.conf['primary_port']
        self._admin  = self.conf['admin_port']

    def reconfigure(self, config, silent=False, override=['all']):
        if config == None:
            os.unlink(self.cfgfile)
        else:
            self.cfgfile_source = config
            self.copy_config(override=override)
        self.admin.execute("box.cfg.reload()", silent=silent)

    def copy_config(self, rand=True, override = ['all']):
        override_all = (True if 'all' in override else False)

        port = random.randrange(3300, 9999)
        for t in self.generate_ports:
            if not t in self.conf:
                self.conf[t] = find_port(port)
                port += 1
        if not self.hot_master is None:
            self.conf['primary_port'] = self.hot_master.sql.port
        if not self.rpl_master is None and 'replication_source' in self.generated_fields:
            self.conf['replication_source'] = \
                '127.0.0.1:'+str(self.rpl_master.conf['primary_port'])


        basic = TarantoolConfig(self.cfgfile_source).parse()
        addit = {}
        for key in self.generated_fields:
            if key in basic and (override_all or key in override) and key in self.conf:
                addit[key] = str(self.conf[key])
        basic.update(addit)
        TarantoolConfig(self.cfgfile).generate(basic)

    def copy_files(self):
        if self.shebang:
            shutil.copy(self.shebang, self.init_lua)
            os.chmod(self.init_lua, 0777)
        elif self.init_lua_source:
            shutil.copy(self.init_lua_source, self.init_lua)
        if self.lua_libs:
            for i in self.lua_libs:
                source = os.path.join(self.testdir, i)
                shutil.copy(source, self.vardir)

    def prepare_args(self):
        return shlex.split(self.init_lua if self.shebang else self.binary)

    def start_and_exit(self):
        color_stdout('Starting the server {0} on ports {1} ...\n'.format(
            os.path.basename(self.binary) if not self.shebang else self.shebang,
            ', '.join([': '.join([str(j) for j in i]) for i in self.conf.items() if i[0].find('port') != -1])
            ), schema='serv_text')
        with daemon.DaemonContext():
            self.start()
            self.process.wait()

    def start(self, silent=True):
        if self.status == 'started':
            if not silent:
                color_stdout('The server is already started.\n', schema='lerror')
            return
        if not silent or self._start_and_exit:
            color_stdout("Starting the server ...\n", schema='serv_text')
            color_stdout("Starting ", schema='serv_text')
            color_stdout((os.path.basename(self.binary) if not self.shebang else self.shebang) + " \n", schema='path')
            color_stdout(self.version() + "\n", schema='version')

        check_port(self.conf['admin_port'])

        args = self.prepare_args()
        self.logfile_pos = self.logfile
        self.process = subprocess.Popen(args,
                cwd = self.vardir,
                stdout=self.log_des,
                stderr=self.log_des)
        self.wait_until_started()
        self.status = 'started'

    def wait_stop(self):
        self.process.wait()

    def stop(self, silent=True):
        if self.status != 'started':
            if not silent:
                color_stdout('The server is not started.\n', schema='lerror')
            return
        if not silent:
            color_stdout('Stopping the server ...\n', schema='serv_text')
        self.process.terminate()
        self.wait_stop()
        self.status = None

    def restart(self):
        self.stop()
        self.start()

    def kill_old_server(self, silent=True):
        pid = self.read_pidfile()
        if pid == -1:
            return False
        if not silent:
            color_stdout('    Found old server, pid {0}, killing ...'.format(pid), schema='info')
        try:
            os.kill(pid, signal.SIGTERM)
        except OSError:
            pass
        self.wait_until_stopped(pid)
        return True

    def wait_until_started(self):
        """ Wait until server is started.

        Server consists of two parts:
        1) wait until server is listening on sockets
        2) wait until server tells us his status

        """

        self.logfile_pos.seek_from('entering the event loop\n', self.process if not self.gdb else None)
        while True:
            try:
                temp = AdminConnection('localhost', self.conf['admin_port'])
                ans = yaml.load(temp.execute('box.info.status'))[0]
                if ans in ('primary', 'hot_standby', 'orphan') or ans.startswith('replica'):
                    return True
                else:
                    raise Exception("Strange output for `box.info.status`: %s" % (ans))
            except socket.error as e:
                if e.errno == errno.ECONNREFUSED:
                    time.sleep(0.1)
                    continue
                raise

    def wait_until_stopped(self, pid):
        while True:
            try:
                time.sleep(0.01)
                os.kill(pid, 0)
                continue
            except OSError as err:
                break

    def read_pidfile(self):
        pid = -1
        if os.path.exists(self.pidfile):
            try:
                with open(self.pidfile) as f:
                    pid = int(f.read())
            except:
                pass
        return pid

    def print_log(self, lines):
        color_stdout("\nLast {0} lines of Tarantool Log file:\n".format(lines), schema='error')
        with open(self.logfile, 'r') as log:
            return log.readlines()[-lines:]

    def test_option_get(self, option_list_str, silent=False):
        args = [self.binary] + shlex.split(option_list_str)
        if not silent:
            print " ".join([os.path.basename(self.binary)] + args[1:])
        output = subprocess.Popen(args, cwd = self.vardir, stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT).stdout.read()
        return output

    def test_option(self, option_list_str):
        print self.test_option_get(option_list_str)

    def test_debug(self):
        if self.test_option_get("-V", True).find("-Debug"):
            return True
        return False

    def find_tests(self, test_suite, suite_path):
        def patterned(test, patterns):
            return [test for i in patterns if test.name.find(i) != -1]

        tests  = [PythonTest(k, test_suite.args, test_suite.ini) \
                for k in sorted(glob.glob(os.path.join(suite_path, "*.test.py" )))]
        tests += [LuaTest(k, test_suite.args, test_suite.ini)    \
                for k in sorted(glob.glob(os.path.join(suite_path, "*.test.lua")))]
        test_suite.tests = sum(map((lambda x: patterned(x, test_suite.args.tests)), tests), [])

    def get_param(self, param = None):
        if not param is None:
            return yaml.load(self.admin("box.info." + param, silent=True))[0]
        return yaml.load(self.admin("box.info", silent=True))

    def wait_lsn(self, lsn):
        while (int(self.get_param("lsn")) < lsn):
            time.sleep(0.01)

    def version(self):
        p = subprocess.Popen([self.binary, "--version"],
                             cwd = self.vardir,
                             stdout = subprocess.PIPE)
        version = p.stdout.read().rstrip()
        p.wait()
        return version
 def _admin(self, port):
     if hasattr(self, 'admin'):
         del self.admin
     self.admin = AdminConnection('localhost', port)
class TarantoolServer(Server):
    def __new__(cls, core="tarantool"):
        return super(TarantoolServer, cls).__new__(cls)

    def __init__(self, core="tarantool"):
        Server.__init__(self, core)
        self.default_bin_name = "tarantool_box"
        self.default_config_name = "tarantool.cfg"
        self.default_init_lua_name = "init.lua"
        # append additional cleanup patterns
        self.re_vardir_cleanup += ['*.snap',
                                   '*.xlog',
                                   '*.inprogress',
                                   '*.cfg',
                                   '*.sup',
                                   '*.lua',
                                   '*.pid']
        self.process = None
        self.config = None
        self.vardir = None
        self.valgrind_log = "valgrind.log"
        self.valgrind_sup = os.path.join("share/", "%s.sup" % ('tarantool'))
        self.init_lua = None
        self.default_suppression_name = "valgrind.sup"
        self.pidfile = None
        self.port = None
        self.binary = None
        self.is_started = False
        self.mem = False
        self.start_and_exit = False
        self.gdb = False
        self.valgrind = False
        self.installed = False
        self.need_init = True

    def __del__(self):
        self.stop()

    def find_exe(self, builddir, silent=True):
        "Locate server executable in the build dir or in the PATH."
        self.builddir = builddir
        builddir = os.path.join(builddir, "src/box")
        path = builddir + os.pathsep + os.environ["PATH"]
        if not silent:
            print "Looking for server binary in {0} ...".format(path)
        for _dir in path.split(os.pathsep):
            exe = os.path.join(_dir, self.default_bin_name)
            if os.access(exe, os.X_OK):
                return exe
        raise RuntimeError("Can't find server executable in " + path)

    def install(self, binary=None, vardir=None, mem=None, silent=True):
        """Install server instance: create necessary directories and files.
        The server working directory is taken from 'vardir',
        specified in the program options."""

        if vardir != None: self.vardir = vardir
        if binary != None: self.binary = os.path.abspath(binary)
        if mem != None: self.mem = mem

        self.pidfile = os.path.abspath(os.path.join(self.vardir, self.pidfile))
        self.valgrind_log = os.path.abspath(os.path.join(self.vardir, self.valgrind_log))

        if not silent:
            print "Installing the server..."
            print "  Found executable at " + self.binary
            print "  Creating and populating working directory in " + self.vardir + "..."

        if os.access(self.vardir, os.F_OK):
            if not silent:
                print "  Found old vardir, deleting..."
            self.kill_old_server()
            self.cleanup()
        else:
            if (self.mem == True and check_tmpfs_exists() and
                os.path.basename(self.vardir) == self.vardir):
                create_tmpfs_vardir(self.vardir)
            else:
                os.makedirs(self.vardir)

        shutil.copy(self.config,
                    os.path.join(self.vardir, self.default_config_name))
        shutil.copy(self.valgrind_sup,
                    os.path.join(self.vardir, self.default_suppression_name))

        var_init_lua = os.path.join(self.vardir, self.default_init_lua_name)

        if self.init_lua != None:
            if os.path.exists(var_init_lua):
                os.remove(var_init_lua)
            shutil.copy(self.init_lua, var_init_lua)

        self.installed = True


    def configure(self, config):
        def get_option(config, section, key):
            if not config.has_option(section, key):
                return None
            value = config.get(section, key)
            if value.isdigit():
                value = int(value)
            return value
        self.config = os.path.abspath(config)
        # now read the server config, we need some properties from it
        with open(self.config) as fp:
            dummy_section_name = "tarantool"
            config = ConfigParser.ConfigParser()
            config.readfp(TarantoolConfigFile(fp, dummy_section_name))

            self.pidfile = get_option(config, dummy_section_name, "pid_file")
            self.primary_port = get_option(config, dummy_section_name, "primary_port")
            self.admin_port = get_option(config, dummy_section_name, "admin_port")
            self.memcached_port = get_option(config, dummy_section_name, "memcached_port")

        self.port = self.admin_port
        self.admin = AdminConnection("localhost", self.admin_port)
        self.sql = BoxConnection("localhost", self.primary_port)
        if self.memcached_port != 0:
            # Run memcached client
            self.memcached = MemcachedConnection('localhost', self.memcached_port)

    def reconfigure(self, config, silent=False):
        if config == None:
            os.unlink(os.path.join(self.vardir, self.default_config_name))
        else:
            self.config = os.path.abspath(config)
            shutil.copy(self.config, os.path.join(self.vardir, self.default_config_name))
        self.admin.execute("box.cfg.reload()", silent=silent)

    def init(self):
        # init storage
        cmd = [self.binary, "--init-storage"]
        _init = subprocess.Popen(cmd, cwd=self.vardir,
                stderr = subprocess.STDOUT, stdout = subprocess.PIPE)
        retcode = _init.wait()
        if retcode:
            sys.stderr.write("tarantool_box --init-storage error: \n%s\n" %  _init.stdout.read())
            raise subprocess.CalledProcessError(retcode, cmd)

    def get_param(self, param):
        if param:
            data = yaml.load(self.admin("box.info." + param, silent=True))[0]
        else:
            data = yaml.load(self.admin("box.info", silent=True))
        return data

    def wait_lsn(self, lsn):
        while True:
            curr_lsn = int(self.get_param("lsn"))
            if (curr_lsn >= lsn):
                break
            time.sleep(0.01)

    def version(self):
        p = subprocess.Popen([self.binary, "--version"],
                             cwd = self.vardir,
                             stdout = subprocess.PIPE)
        version = p.stdout.read().rstrip()
        p.wait()
        return version

    def _start_and_exit(self, args, gdb=None, valgrind=None):
        if gdb != None: self.gdb = gdb
        if valgrind != None: self.valgrind = valgrind

        if self.valgrind:
            with daemon.DaemonContext(working_directory = self.vardir):
                subprocess.check_call(args)
        else:
            if not self.gdb:
                args.append("--background")
            else:
                raise RuntimeError("'--gdb' and '--start-and-exit' can't be defined together")
            self.server = subprocess.Popen(args, cwd = self.vardir)
            self.server.wait()

    def start(self, start_and_exit=None, gdb=None, valgrind=None, silent=True):
        if start_and_exit != None: self.start_and_exit = start_and_exit
        if gdb != None: self.gdb = gdb
        if valgrind != None: self.valgrind = valgrind
        self.debug = self.test_debug()

        if self.is_started:
            if not silent:
                print "The server is already started."
            return

        if not silent:
            print "Starting the server..."
            version = self.version()
            print "Starting {0} {1}.".format(os.path.basename(self.binary), version)

        check_port(self.port)
        args = self.prepare_args()

        if self.gdb:
            args = prepare_gdb(self.binary, args)
            print "You started the server in gdb mode."
            print "To attach, use `screen -r tnt-gdb`"
        elif self.valgrind:
            args = prepare_valgrind([self.binary] + args, self.valgrind_log,
                                    os.path.abspath(os.path.join(self.vardir,
                                    self.default_suppression_name)))
        else:
            args = [self.binary] + args

        if self.start_and_exit:
            self._start_and_exit(args)
            return

        self.process = subprocess.Popen(args, cwd = self.vardir)

        # wait until the server is connected
        self.wait_until_started()
        # Set is_started flag, to nicely support cleanup during an exception.
        self.is_started = True


    def stop(self, silent=True):
        """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 not self.is_started:
            if not silent:
                print "The server is not started."
            return

        if not silent:
            print "Stopping the server..."

        if self.process == None:
            self.kill_old_server()
            return

        # kill process
        pid = self.read_pidfile()
        if pid != -1:
            os.kill(pid, signal.SIGTERM)

#       self.process.terminate()
        if self.gdb or self.valgrind:
            while True:
                if self.process.poll() != None:
                    break
                time.sleep(1)
        else:
            self.process.wait()

        self.wait_until_stopped(pid)
        # clean-up processs flags
        self.is_started = False
        self.process = None

    def deploy(self, config=None, binary=None, vardir=None,
               mem=None, start_and_exit=None, gdb=None, valgrind=None,
               valgrind_sup=None, init_lua=None, silent=True, need_init=None):
        if config != None: self.config = config
        if binary != None: self.binary = binary
        if vardir != None: self.vardir = vardir
        if mem != None: self.mem = mem
        if start_and_exit != None: self.start_and_exit = start_and_exit
        if gdb != None: self.gdb = gdb
        if valgrind != None: self.valgrind = valgrind
        if need_init != None: self.need_init = need_init

        if init_lua != None:
            self.init_lua = os.path.abspath(init_lua)
        else:
            self.init_lua = None

        self.configure(self.config)
        self.install(self.binary, self.vardir, self.mem, silent)
        if self.need_init:
            self.init()
        self.start(self.start_and_exit, self.gdb, self.valgrind, silent)

    def restart(self):
        self.stop(silent=True)
        self.start(silent=True)

    def test_option_get(self, show, option_list_str):
        args = [self.binary] + option_list_str.split()
        if show:
            print " ".join([os.path.basename(self.binary)] + args[1:])
        output = subprocess.Popen(args,
                                  cwd = self.vardir,
                                  stdout = subprocess.PIPE,
                                  stderr = subprocess.STDOUT).stdout.read()
        return output

    def test_option(self, option_list_str):
        print self.test_option_get(True, option_list_str)

    def test_debug(self):
        output = self.test_option_get(False, "-V")
        if re.search("-Debug", output):
            return True
        return False

    def kill_old_server(self, silent=True):
        """Kill old server instance if it exists."""
        pid = self.read_pidfile()
        if pid == -1:
            return # Nothing to do

        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

    def read_pidfile(self):
        if os.access(self.pidfile, os.F_OK) == False:
            # file is inaccessible (not exist or permission denied)
            return -1

        pid = -1
        try:
            with open(self.pidfile) as f:
                pid = int(f.read())
        except:
            pass
        return pid

    def wait_until_started(self):
        """Wait until the server is started and accepting connections"""

        while self.read_pidfile() == -1:
            time.sleep(0.001)

        is_connected = False
        while not is_connected and not self.gdb:
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.connect(("localhost", self.port))
                is_connected = True
                sock.close()
            except socket.error as e:
                time.sleep(0.001)

    def wait_until_stopped(self, pid):
        """Wait until the server is stoped and has closed sockets"""
        while True:
            try:
                time.sleep(0.001)
                os.kill(pid, 0)
                continue
            except OSError as err:
                if err.errno == errno.ESRCH:
                    break
                raise

    def find_tests(self, test_suite, suite_path):
        def patterned(test, patterns):
            answer = []
            for i in patterns:
                if test.name.find(i) != -1:
                    answer.append(test)
            return answer

        tests  = [PythonTest(k, test_suite.args, test_suite.ini) for k in sorted(glob.glob(os.path.join(suite_path, "*.test.py" )))]
        tests += [LuaTest(k, test_suite.args, test_suite.ini)    for k in sorted(glob.glob(os.path.join(suite_path, "*.test.lua")))]
        test_suite.tests = sum(map((lambda x: patterned(x, test_suite.args.tests)), tests), [])
    def execute(self, server):
        delimiter = ''
        cmd = None
        curcon = server.admin
        for line in open(self.name, 'r'):
            if not cmd:
                cmd = StringIO.StringIO()
            if line.find('--#') == 0:
                line = line[3:].strip()
                matched1 = re.match(r"setopt\s+(.*)", line)
                matched2 = re.match(r"(.*)\s+server\s+(.*)", line)
                matched3 = re.match(r"(.*)\s+connection\s+(.*)", line)
                matched4 = re.match(r"(.*)\s+filter[s]?(\s+.*)?", line)
                if matched1:
                    command = re.split(r'\s*=\s*|\s+|\s*,\s*',
                                       matched1.group(1))
                    command = {
                        command[i]: command[i + 1]
                        for i in xrange(0,
                                        len(command) - 1, 2)
                    }
                    if 'delimiter' in command:
                        delimiter = command['delimiter'].strip()[1:-1]
                        command.pop('delimiter')
                    if command:
                        raise LuaPreprocessorException(
                            "Wrong setopt options - " + str(command))
                elif matched2:
                    try:
                        if matched2.group(1) == 'create':
                            name, params = re.match(
                                r"(.*)\s+with\s+(.*)",
                                matched2.group(2)).groups()
                            params = re.split(r'\s*=\s*|\s+|\s*,\s*', params)
                            params = {
                                params[i]: params[i + 1]
                                for i in xrange(0,
                                                len(params) - 1, 2)
                            }
                            temp_server = TarantoolServer()
                            if 'configuration' in params:
                                temp_server.config = params['configuration'][
                                    1:-1]
                            else:
                                temp_server.config = self.suite_ini['config']
                            if 'need_init' in params:
                                temp_server.need_init = False
                            if 'init' in params:
                                temp_server.init_lua = params['init'][1:-1]

                            temp_server.vardir = os.path.join(
                                self.suite_ini['vardir'], name)
                            temp_server.binary = temp_server.find_exe(
                                self.suite_ini['builddir'])
                            self.suite_ini['servers'][name] = temp_server
                            temp_server.configure(temp_server.config)
                            temp_server.install(temp_server.binary,
                                                temp_server.vardir,
                                                temp_server.mem, True)
                            if temp_server.need_init:
                                temp_server.init()
                        elif matched2.group(1) == 'start':
                            name = matched2.group(2)
                            if name in self.suite_ini['servers']:
                                self.suite_ini['servers'][name].start(
                                    silent=True)
                                self.suite_ini['connections'][
                                    name] = self.suite_ini['servers'][
                                        name].admin
                                try:
                                    self.suite_ini['connections'][name](
                                        'print( 1 )', silent=True)
                                except socket.error as e:
                                    raise LuaPreprocessorException(
                                        "Can't connect to server with errno " +
                                        str(e.errno))
                            else:
                                raise LuaPreprocessorException(
                                    "Wrong server name: " + name)
                        elif matched2.group(1) == 'stop':
                            name = matched2.group(2)
                            if name in self.suite_ini['servers']:
                                self.suite_ini['servers'][name].stop()
                                self.suite_ini['connections'].pop(name)
                            else:
                                raise LuaPreprocessorException(
                                    "Wrong server name: " + name)
                        elif matched2.group(1) == 'deploy':
                            name = matched2.group(2)
                            if name in self.suite_ini['servers']:
                                self.suite_ini['servers'][name].deploy(
                                    silent=True)
                            else:
                                raise LuaPreprocessorException(
                                    "Wrong server name: " + name)
                        elif matched2.group(1) == 'reconfigure':
                            name, params = re.match(
                                r"(.*)\s+with\s+(.*)",
                                matched2.group(2)).groups()
                            if name not in self.suite_ini['servers']:
                                raise LuaPreprocessorException(
                                    "Wrong server name: " + name)
                            params = re.split(r'\s*=\s*|\s+|\s*,\s*', params)
                            params = {
                                params[i]: params[i + 1]
                                for i in xrange(0,
                                                len(params) - 1, 2)
                            }
                            tmp_srv = self.suite_ini['servers'][name]

                            if 'configuration' in params:
                                tmp_srv.reconfigure(
                                    params['configuration'][1:-1], silent=True)
                            else:
                                tmp_srv.config = self.suite_ini['config']

                            if tmp_srv.init_lua != None:
                                var_init_lua = os.path.join(
                                    tmp_srv.vardir,
                                    tmp_srv.default_init_lua_name)
                                if os.path.exists(var_init_lua):
                                    os.path.remove(var_init_lua)
                            if 'init' in params:
                                tmp_srv.init_lua = params['init'][1:-1]
                                var_init_lua = os.path.join(
                                    tmp_srv.vardir,
                                    tmp_srv.default_init_lua_name)
                                shutil.copy(tmp_srv.init_lua, var_init_lua)
                                tmp_srv.restart()

                        elif matched2.group(1) == 'cleanup':
                            name = matched2.group(2)
                            if name in self.suite_ini['servers']:
                                self.suite_ini['servers'][name].cleanup()
                        else:
                            raise LuaPreprocessorException(
                                "Wrong command for server - " +
                                repr(matched2.group(1)))
                    except (AttributeError, ValueError) as e:
                        raise LuaPreprocessorException(
                            "Wrong command for server - " + repr(e.message))
                elif matched3:
                    try:
                        if matched3.group(1) == 'create':
                            namecon, name = re.match(
                                "(.*)\s+to\s+(.*)",
                                matched3.group(2)).groups()
                            self.suite_ini['connections'][
                                namecon] = AdminConnection(
                                    'localhost',
                                    self.suite_ini['servers'][name].port)
                            self.suite_ini['connections'][namecon].connect()
                        elif matched3.group(1) == 'drop':
                            name = matched3.group(2)
                            if name in self.suite_ini['connections']:
                                self.suite_ini['connections'][name].disconnect(
                                )
                                self.suite_ini['connections'].pop(name)
                            else:
                                raise LuaPreprocessorException(
                                    "Wrong connection name: " + name)
                        elif matched3.group(1) == 'set':
                            name = re.split(",\s*", matched3.group(2))
                            for _name in name:
                                if not _name in self.suite_ini['connections']:
                                    raise LuaPreprocessorException(
                                        "Wrong connection name: " + _name)
                            if len(name) == 1:
                                curcon = self.suite_ini['connections'][name[0]]
                            else:
                                curcon = [
                                    self.suite_ini['connections'][_name]
                                    for _name in name
                                ]
                        else:
                            raise LuaPreprocessorException(
                                "Wrong command for connection - " +
                                repr(matched3.group(1)))
                    except (AttributeError, ValueError) as e:
                        raise LuaPreprocessorException(
                            "Wrong command for connection - " +
                            repr(e.message))
                elif matched4:
                    try:
                        if matched4.group(1) == 'push':
                            p1, p2 = re.split(r"\"\s*to\s*\"|\'\s*to\s*\'",
                                              matched4.group(2).strip())
                            sys.stdout.push_filter(p1[1:], p2[:-1])
                        elif matched4.group(1) == 'pop':
                            sys.stdout.pop_filter()
                        elif matched4.group(1) == 'clear':
                            sys.stdout.clear_all_filters()
                        else:
                            raise LuaPreprocessorException(
                                "Wrong command for filters - " +
                                repr(matched4.group(1)))
                    except (AttributeError, ValueError) as e:
                        raise LuaPreprocessorException(
                            "Wrong command for filters - " + repr(e.message))
                else:
                    raise LuaPreprocessorException("Wrong command - " +
                                                   repr(line))
                sys.stdout.write("--# " + line + '\n')
            elif line.find('--') == 0:
                sys.stdout.write(line)
            else:
                if not delimiter:
                    if line.strip():
                        curcon(line.strip())
                    continue
                cmd.write(line)
                if cmd.getvalue().endswith(delimiter +
                                           '\n') and cmd.getvalue():
                    if isinstance(curcon, list):
                        for con in curcon:
                            res = con(cmd.getvalue()[:-len(delimiter)].replace(
                                '\n\n', '\n'),
                                      silent=True)
                    else:
                        res = curcon(cmd.getvalue()[:-len(delimiter)].replace(
                            '\n\n', '\n'),
                                     silent=True)
                    sys.stdout.write(cmd.getvalue()[:-1].strip() + '\n')
                    sys.stdout.write(res.replace("\r\n", "\n"))
                    cmd.close()
                    cmd = None
        if cmd and cmd.getvalue().strip():
            if isinstance(curcon, list):
                for con in curcon:
                    res = con(cmd.getvalue()[:-len(delimiter)].replace(
                        '\n\n', '\n'),
                              silent=True)
            else:
                res = curcon(cmd.getvalue()[:-len(delimiter)].replace(
                    '\n\n', '\n'),
                             silent=True)
            sys.stdout.write(cmd.getvalue()[:-1].strip() + '\n')
            sys.stdout.write(res.replace("\r\n", "\n"))
            cmd.close
            cmd = None
        sys.stdout.clear_all_filters()
class TarantoolServer(Server):
    def __new__(cls, core="tarantool"):
        return super(TarantoolServer, cls).__new__(cls)

    def __init__(self, core="tarantool"):
        Server.__init__(self, core)
        self.default_bin_name = "tarantool_box"
        self.default_config_name = "tarantool.cfg"
        self.default_init_lua_name = "init.lua"
        # append additional cleanup patterns
        self.re_vardir_cleanup += [
            '*.snap', '*.xlog', '*.inprogress', '*.cfg', '*.sup', '*.lua',
            '*.pid'
        ]
        self.process = None
        self.config = None
        self.vardir = None
        self.valgrind_log = "valgrind.log"
        self.valgrind_sup = os.path.join("share/", "%s.sup" % ('tarantool'))
        self.init_lua = None
        self.default_suppression_name = "valgrind.sup"
        self.pidfile = None
        self.port = None
        self.binary = None
        self.is_started = False
        self.mem = False
        self.start_and_exit = False
        self.gdb = False
        self.valgrind = False
        self.installed = False
        self.need_init = True

    def __del__(self):
        self.stop()

    def find_exe(self, builddir, silent=True):
        "Locate server executable in the build dir or in the PATH."
        self.builddir = builddir
        builddir = os.path.join(builddir, "src/box")
        path = builddir + os.pathsep + os.environ["PATH"]
        if not silent:
            print "Looking for server binary in {0} ...".format(path)
        for _dir in path.split(os.pathsep):
            exe = os.path.join(_dir, self.default_bin_name)
            if os.access(exe, os.X_OK):
                return exe
        raise RuntimeError("Can't find server executable in " + path)

    def install(self, binary=None, vardir=None, mem=None, silent=True):
        """Install server instance: create necessary directories and files.
        The server working directory is taken from 'vardir',
        specified in the program options."""

        if vardir != None: self.vardir = vardir
        if binary != None: self.binary = os.path.abspath(binary)
        if mem != None: self.mem = mem

        self.pidfile = os.path.abspath(os.path.join(self.vardir, self.pidfile))
        self.valgrind_log = os.path.abspath(
            os.path.join(self.vardir, self.valgrind_log))

        if not silent:
            print "Installing the server..."
            print "  Found executable at " + self.binary
            print "  Creating and populating working directory in " + self.vardir + "..."

        if os.access(self.vardir, os.F_OK):
            if not silent:
                print "  Found old vardir, deleting..."
            self.kill_old_server()
            self.cleanup()
        else:
            if (self.mem == True and check_tmpfs_exists()
                    and os.path.basename(self.vardir) == self.vardir):
                create_tmpfs_vardir(self.vardir)
            else:
                os.makedirs(self.vardir)

        shutil.copy(self.config,
                    os.path.join(self.vardir, self.default_config_name))
        shutil.copy(self.valgrind_sup,
                    os.path.join(self.vardir, self.default_suppression_name))

        var_init_lua = os.path.join(self.vardir, self.default_init_lua_name)

        if self.init_lua != None:
            if os.path.exists(var_init_lua):
                os.remove(var_init_lua)
            shutil.copy(self.init_lua, var_init_lua)

        self.installed = True

    def configure(self, config):
        def get_option(config, section, key):
            if not config.has_option(section, key):
                return None
            value = config.get(section, key)
            if value.isdigit():
                value = int(value)
            return value

        self.config = os.path.abspath(config)
        # now read the server config, we need some properties from it
        with open(self.config) as fp:
            dummy_section_name = "tarantool"
            config = ConfigParser.ConfigParser()
            config.readfp(TarantoolConfigFile(fp, dummy_section_name))

            self.pidfile = get_option(config, dummy_section_name, "pid_file")
            self.primary_port = get_option(config, dummy_section_name,
                                           "primary_port")
            self.admin_port = get_option(config, dummy_section_name,
                                         "admin_port")
            self.memcached_port = get_option(config, dummy_section_name,
                                             "memcached_port")

        self.port = self.admin_port
        self.admin = AdminConnection("localhost", self.admin_port)
        self.sql = BoxConnection("localhost", self.primary_port)
        if self.memcached_port != 0:
            # Run memcached client
            self.memcached = MemcachedConnection('localhost',
                                                 self.memcached_port)

    def reconfigure(self, config, silent=False):
        if config == None:
            os.unlink(os.path.join(self.vardir, self.default_config_name))
        else:
            self.config = os.path.abspath(config)
            shutil.copy(self.config,
                        os.path.join(self.vardir, self.default_config_name))
        self.admin.execute("box.cfg.reload()", silent=silent)

    def init(self):
        # init storage
        cmd = [self.binary, "--init-storage"]
        _init = subprocess.Popen(cmd,
                                 cwd=self.vardir,
                                 stderr=subprocess.STDOUT,
                                 stdout=subprocess.PIPE)
        retcode = _init.wait()
        if retcode:
            sys.stderr.write("tarantool_box --init-storage error: \n%s\n" %
                             _init.stdout.read())
            raise subprocess.CalledProcessError(retcode, cmd)

    def get_param(self, param):
        if param:
            data = yaml.load(self.admin("box.info." + param, silent=True))[0]
        else:
            data = yaml.load(self.admin("box.info", silent=True))
        return data

    def wait_lsn(self, lsn):
        while True:
            curr_lsn = int(self.get_param("lsn"))
            if (curr_lsn >= lsn):
                break
            time.sleep(0.01)

    def version(self):
        p = subprocess.Popen([self.binary, "--version"],
                             cwd=self.vardir,
                             stdout=subprocess.PIPE)
        version = p.stdout.read().rstrip()
        p.wait()
        return version

    def _start_and_exit(self, args, gdb=None, valgrind=None):
        if gdb != None: self.gdb = gdb
        if valgrind != None: self.valgrind = valgrind

        if self.valgrind:
            with daemon.DaemonContext(working_directory=self.vardir):
                subprocess.check_call(args)
        else:
            if not self.gdb:
                args.append("--background")
            else:
                raise RuntimeError(
                    "'--gdb' and '--start-and-exit' can't be defined together")
            self.server = subprocess.Popen(args, cwd=self.vardir)
            self.server.wait()

    def start(self, start_and_exit=None, gdb=None, valgrind=None, silent=True):
        if start_and_exit != None: self.start_and_exit = start_and_exit
        if gdb != None: self.gdb = gdb
        if valgrind != None: self.valgrind = valgrind
        self.debug = self.test_debug()

        if self.is_started:
            if not silent:
                print "The server is already started."
            return

        if not silent:
            print "Starting the server..."
            version = self.version()
            print "Starting {0} {1}.".format(os.path.basename(self.binary),
                                             version)

        check_port(self.port)
        args = self.prepare_args()

        if self.gdb:
            args = prepare_gdb(self.binary, args)
            print "You started the server in gdb mode."
            print "To attach, use `screen -r tnt-gdb`"
        elif self.valgrind:
            args = prepare_valgrind([self.binary] + args, self.valgrind_log,
                                    os.path.abspath(
                                        os.path.join(
                                            self.vardir,
                                            self.default_suppression_name)))
        else:
            args = [self.binary] + args

        if self.start_and_exit:
            self._start_and_exit(args)
            return

        self.process = subprocess.Popen(args, cwd=self.vardir)

        # wait until the server is connected
        self.wait_until_started()
        # Set is_started flag, to nicely support cleanup during an exception.
        self.is_started = True

    def stop(self, silent=True):
        """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 not self.is_started:
            if not silent:
                print "The server is not started."
            return

        if not silent:
            print "Stopping the server..."

        if self.process == None:
            self.kill_old_server()
            return

        # kill process
        pid = self.read_pidfile()
        if pid != -1:
            os.kill(pid, signal.SIGTERM)

#       self.process.terminate()
        if self.gdb or self.valgrind:
            while True:
                if self.process.poll() != None:
                    break
                time.sleep(1)
        else:
            self.process.wait()

        self.wait_until_stopped(pid)
        # clean-up processs flags
        self.is_started = False
        self.process = None

    def deploy(self,
               config=None,
               binary=None,
               vardir=None,
               mem=None,
               start_and_exit=None,
               gdb=None,
               valgrind=None,
               valgrind_sup=None,
               init_lua=None,
               silent=True,
               need_init=None):
        if config != None: self.config = config
        if binary != None: self.binary = binary
        if vardir != None: self.vardir = vardir
        if mem != None: self.mem = mem
        if start_and_exit != None: self.start_and_exit = start_and_exit
        if gdb != None: self.gdb = gdb
        if valgrind != None: self.valgrind = valgrind
        if need_init != None: self.need_init = need_init

        if init_lua != None:
            self.init_lua = os.path.abspath(init_lua)
        else:
            self.init_lua = None

        self.configure(self.config)
        self.install(self.binary, self.vardir, self.mem, silent)
        if self.need_init:
            self.init()
        self.start(self.start_and_exit, self.gdb, self.valgrind, silent)

    def restart(self):
        self.stop(silent=True)
        self.start(silent=True)

    def test_option_get(self, show, option_list_str):
        args = [self.binary] + option_list_str.split()
        if show:
            print " ".join([os.path.basename(self.binary)] + args[1:])
        output = subprocess.Popen(args,
                                  cwd=self.vardir,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.STDOUT).stdout.read()
        return output

    def test_option(self, option_list_str):
        print self.test_option_get(True, option_list_str)

    def test_debug(self):
        output = self.test_option_get(False, "-V")
        if re.search("-Debug", output):
            return True
        return False

    def kill_old_server(self, silent=True):
        """Kill old server instance if it exists."""
        pid = self.read_pidfile()
        if pid == -1:
            return  # Nothing to do

        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

    def read_pidfile(self):
        if os.access(self.pidfile, os.F_OK) == False:
            # file is inaccessible (not exist or permission denied)
            return -1

        pid = -1
        try:
            with open(self.pidfile) as f:
                pid = int(f.read())
        except:
            pass
        return pid

    def wait_until_started(self):
        """Wait until the server is started and accepting connections"""

        while self.read_pidfile() == -1:
            time.sleep(0.001)

        is_connected = False
        while not is_connected and not self.gdb:
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.connect(("localhost", self.port))
                is_connected = True
                sock.close()
            except socket.error as e:
                time.sleep(0.001)

    def wait_until_stopped(self, pid):
        """Wait until the server is stoped and has closed sockets"""
        while True:
            try:
                time.sleep(0.001)
                os.kill(pid, 0)
                continue
            except OSError as err:
                if err.errno == errno.ESRCH:
                    break
                raise

    def find_tests(self, test_suite, suite_path):
        def patterned(test, patterns):
            answer = []
            for i in patterns:
                if test.name.find(i) != -1:
                    answer.append(test)
            return answer

        tests = [
            PythonTest(k, test_suite.args, test_suite.ini)
            for k in sorted(glob.glob(os.path.join(suite_path, "*.test.py")))
        ]
        tests += [
            LuaTest(k, test_suite.args, test_suite.ini)
            for k in sorted(glob.glob(os.path.join(suite_path, "*.test.lua")))
        ]
        test_suite.tests = sum(
            map((lambda x: patterned(x, test_suite.args.tests)), tests), [])