diff --git a/test/box/admin.result b/test/box/admin.result
index 4ecf4c9cf1b76894599bab064cc4716b319b4492..8c4b4881fa6c775bc9f1738d58c307063ac12e68 100644
--- a/test/box/admin.result
+++ b/test/box/admin.result
@@ -6,6 +6,8 @@ space = box.schema.create_space('tweedledum', { id = 0 })
 space:create_index('primary', { type = 'hash' })
 ---
 ...
+--# push filter 'primary_port: .*' to 'primary_port: <number>'
+--# push filter 'admin_port: .*' to 'admin_port: <number>'
 box.stat()
 ---
 - DELETE:
@@ -47,7 +49,7 @@ box.cfg()
   pid_file: box.pid
   slab_alloc_factor: 2
   slab_alloc_minimal: 64
-  admin_port: 33015
+  admin_port: <number>
   logger: cat - >> tarantool.log
   snap_io_rate_limit: 0
   log_level: 5
@@ -66,7 +68,7 @@ box.cfg()
   replication_port: 0
   bind_ipaddr: INADDR_ANY
   wal_fsync_delay: 0
-  primary_port: 33013
+  primary_port: <number>
   wal_dir_rescan_delay: 0.1
 ...
 box.stat()
@@ -102,6 +104,7 @@ space:delete{1}
 ---
 - [1, 'tuple']
 ...
+--# clear filter
 --# setopt delimiter ';'
 function check_type(arg, typeof)
     return type(arg) == typeof
diff --git a/test/box/admin.test.lua b/test/box/admin.test.lua
index ccbf51b1b1417bf875dd56d245bddaadcbaad428..673fd3b7465375fbf35d1bb5fd5814501444185b 100644
--- a/test/box/admin.test.lua
+++ b/test/box/admin.test.lua
@@ -4,6 +4,8 @@
 space = box.schema.create_space('tweedledum', { id = 0 })
 space:create_index('primary', { type = 'hash' })
 
+--# push filter 'primary_port: .*' to 'primary_port: <number>'
+--# push filter 'admin_port: .*' to 'admin_port: <number>'
 box.stat()
 help()
 box.cfg()
@@ -11,6 +13,7 @@ box.stat()
 space:insert{1, 'tuple'}
 box.snapshot()
 space:delete{1}
+--# clear filter
 
 --# setopt delimiter ';'
 function check_type(arg, typeof)
diff --git a/test/box/args.test.py b/test/box/args.test.py
index 8e73abc7bdd9bc8f780fb79553483ee6d384e804..f8043549fcefa99ba8a96d7f2091879eacf6eab0 100644
--- a/test/box/args.test.py
+++ b/test/box/args.test.py
@@ -3,7 +3,7 @@ import os
 
 # mask BFD warnings: https://bugs.launchpad.net/tarantool/+bug/1018356
 sys.stdout.push_filter("unable to read unknown load command 0x2\d+", "")
-
+vardir = server.vardir
 server.test_option("--help")
 server.test_option("-h")
 sys.stdout.push_filter("(/\S+)+/tarantool", "tarantool")
diff --git a/test/box/bad_trigger.test.py b/test/box/bad_trigger.test.py
index 547ab82237019b78aedfcc5375f45c28e49a9b25..083c7c6235afb1f6b4bac38a265e1e42d8b79c74 100644
--- a/test/box/bad_trigger.test.py
+++ b/test/box/bad_trigger.test.py
@@ -7,7 +7,7 @@ print """
 
 admin("function f1() nosuchfunction() end")
 admin("box.session.on_connect(f1)")
-con1 = BoxConnection('localhost', server.primary_port)
+con1 = BoxConnection('localhost', sql.port)
 con1("select * from t0 where k0=0")
 if not con1.check_connection():
     print "Connection is dead.\n"
diff --git a/test/box/cfg.result b/test/box/cfg.result
index ee9f83433f05b44ec2f68bd7371c7b1d6ffb6e46..8733d27902cfce86d6ad4f443060cb5d2541e4ed 100644
--- a/test/box/cfg.result
+++ b/test/box/cfg.result
@@ -1,3 +1,5 @@
+--# push filter 'primary_port: .*' to 'primary_port: <number>'
+--# push filter 'admin_port: .*' to 'admin_port: <number>'
 box.cfg.nosuchoption = 1
 ---
 - error: '[string "getmetatable(box.cfg).__newindex = function(t..."]:2: Attempt to
@@ -12,7 +14,7 @@ t
   - 'pid_file: box.pid'
   - 'slab_alloc_factor: 2'
   - 'slab_alloc_minimal: 64'
-  - 'admin_port: 33015'
+  - 'admin_port: <number>
   - 'logger: cat - >> tarantool.log'
   - 'readahead: 16320'
   - 'log_level: 5'
@@ -21,7 +23,7 @@ t
   - 'too_long_threshold: 0.5'
   - 'snap_dir: .'
   - 'coredump: false'
-  - 'primary_port: 33013'
+  - 'primary_port: <number>
   - 'panic_on_wal_error: false'
   - 'snap_io_rate_limit: 0'
   - 'wal_mode: fsync_delay'
@@ -48,7 +50,7 @@ t
   - 'pid_file: box.pid'
   - 'slab_alloc_factor: 2'
   - 'slab_alloc_minimal: 64'
-  - 'admin_port: 33015'
+  - 'admin_port: <number>
   - 'logger: cat - >> tarantool.log'
   - 'readahead: 16320'
   - 'log_level: 5'
@@ -57,7 +59,7 @@ t
   - 'too_long_threshold: 0.5'
   - 'snap_dir: .'
   - 'coredump: false'
-  - 'primary_port: 33013'
+  - 'primary_port: <number>
   - 'panic_on_wal_error: false'
   - 'snap_io_rate_limit: 0'
   - 'wal_mode: fsync_delay'
@@ -70,3 +72,4 @@ t
   - 'wal_dir: .'
   - 'wal_dir_rescan_delay: 0.1'
 ...
+--# clear filter
diff --git a/test/box/cfg.test.lua b/test/box/cfg.test.lua
index f47270927851166d8a89e7246ac9293c0d9721e4..c6631f4df824919cc787f4c9de377548ed2468e6 100644
--- a/test/box/cfg.test.lua
+++ b/test/box/cfg.test.lua
@@ -1,3 +1,5 @@
+--# push filter 'primary_port: .*' to 'primary_port: <number>'
+--# push filter 'admin_port: .*' to 'admin_port: <number>'
 box.cfg.nosuchoption = 1
 t = {} for k,v in pairs(box.cfg) do if type(v) ~= 'table' and type(v) ~= 'function' then table.insert(t, k..': '..tostring(v)) end end
 t
@@ -5,3 +7,4 @@ t
 box.cfg.reload()
 t = {} for k,v in pairs(box.cfg) do if type(v) ~= 'table' and type(v) ~= 'function' then table.insert(t, k..': '..tostring(v)) end end
 t
+--# clear filter
diff --git a/test/box/configuration.result b/test/box/configuration.result
index e76aa4a6f2f0ed7e66c9798790372d5d032f2bd2..ce0d4c3b4008bcdda0f39cede9ab9de32f22b0a8 100644
--- a/test/box/configuration.result
+++ b/test/box/configuration.result
@@ -13,7 +13,7 @@ print_config()
   pid_file: box.pid
   slab_alloc_factor: 2
   slab_alloc_minimal: 64
-  admin_port: 33015
+  admin_port: <number>
   logger: cat - >> tarantool.log
   snap_io_rate_limit: 0
   log_level: 5
@@ -32,7 +32,7 @@ print_config()
   replication_port: 0
   bind_ipaddr: INADDR_ANY
   wal_fsync_delay: 0
-  primary_port: 33013
+  primary_port: <number>
   wal_dir_rescan_delay: 0.1
 ...
 
@@ -88,7 +88,7 @@ mod.test(10, 15)
 # Bug#99 Salloc initialization is not checked on startup
 #  (https://github.com/tarantool/tarantool/issues/99)
 
-Can't start tarantool
+Can't start Tarantool
 ok
 
 # Bug#100 Segmentation fault if rows_per_wal = 1
diff --git a/test/box/configuration.test.py b/test/box/configuration.test.py
index b6e2d0ad61aa001d43ecc86287b4ac3263752e4b..424dad1eae7a7e6893ec28cea9b5be7812114d20 100644
--- a/test/box/configuration.test.py
+++ b/test/box/configuration.test.py
@@ -13,19 +13,23 @@ print """
 # stop current server
 server.stop()
 
-server.deploy("box/tarantool_bug876541.cfg")
+cfgfile_bkp = server.cfgfile_source
+server.cfgfile_source = "box/tarantool_bug876541.cfg"
+server.deploy()
 # check values
 admin("box.cfg.wal_fsync_delay")
 
-script = os.path.join(vardir, "init.lua")
-shutil.copy("box/lua/test_init.lua", script)
-os.chmod(script, 0744)
-
 server.stop()
 old_binary = server.binary
-server.binary = script
-server.deploy("box/tarantool_scriptdir.cfg")
+server.cfgfile_source = "box/tarantool_scriptdir.cfg"
+server.shebang = "box/lua/test_init.lua"
+server.deploy()
+os.chmod(server.init_lua, 0744)
+sys.stdout.push_filter("admin_port: .*", "admin_port: <number>")
+sys.stdout.push_filter("primary_port: .*", "primary_port: <number>")
 admin("print_config()")
+sys.stdout.pop_filter()
+sys.stdout.pop_filter()
 
 print """
 # Test bug #977898
@@ -49,10 +53,11 @@ admin("floor(1.1)")
 
 # Test script_dir + require
 server.stop()
-shutil.copy("box/lua/require_init.lua", script)
-os.chmod(script, 0744)
-shutil.copy("box/lua/require_mod.lua", os.path.join(vardir, "mod.lua"))
-server.deploy("box/tarantool_scriptdir.cfg")
+server.shebang = "box/lua/require_init.lua"
+server.lua_libs.append("box/lua/require_mod.lua")
+server.cfgfile_source = "box/tarantool_scriptdir.cfg"
+server.deploy()
+server.lua_libs.pop()
 admin("string.gmatch(package_path, '([^;]*)')()")
 admin("string.gmatch(package_cpath, '([^;]*)')()")
 admin("mod.test(10, 15)")
@@ -67,9 +72,10 @@ print """
 server.stop()
 
 # Restore the old binary
-server.binary = old_binary
+self.shebang=None
 try:
-    server.deploy("box/tarantool_bug_gh-99.cfg")
+    server.cfgfile_source="box/tarantool_bug_gh-99.cfg"
+    server.deploy()
 except OSError as e:
     print e
     print("ok")
@@ -85,8 +91,6 @@ server.test_option("-c " + os.path.join(os.getcwd(), "box/tarantool_bug_gh100.cf
 sys.stdout.pop_filter()
 
 # restore default server
-server.stop()
-os.remove(script)
-os.remove(os.path.join(vardir, "mod.lua"))
-server.deploy(self.suite_ini["config"])
-
+server.shebang = None
+server.cfgfile_source = cfgfile_bkp
+server.deploy()
diff --git a/test/box/iproto.test.py b/test/box/iproto.test.py
index 8ce6deb39750063e0ccf63aadfba364fc0635670..895a8c90ad792aa3d6b1ff58a61837d62b0555a6 100644
--- a/test/box/iproto.test.py
+++ b/test/box/iproto.test.py
@@ -11,7 +11,7 @@ print """
 
 # opeing new connection to tarantool/box
 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-s.connect(('localhost', server.primary_port))
+s.connect(('localhost', server.sql.port))
 
 print """
 # Test bug #899343 (server assertion failure on incorrect packet)
diff --git a/test/box/lua/require_init.lua b/test/box/lua/require_init.lua
index 23cd45b4bcbf471e86670c30e5fa42a45683fd94..b32a392a0932790d67103d04c68edc51b929764a 100644
--- a/test/box/lua/require_init.lua
+++ b/test/box/lua/require_init.lua
@@ -1,4 +1,4 @@
 #!/usr/bin/env tarantool_box
-mod = require("mod")
+mod = require("require_mod")
 package_path = package.path
 package_cpath = package.cpath
diff --git a/test/box/print.test.py b/test/box/print.test.py
index 16dc9f7cc34c622366873415e17ed4898b2d3dac..b61a7c344a36c9e58923ec25ebfc0e516cd29df2 100644
--- a/test/box/print.test.py
+++ b/test/box/print.test.py
@@ -7,7 +7,7 @@ import re
 
 admin('print("Hello, world")')
 
-log = os.path.join(vardir, "tarantool.log")
+log = server.logfile
 f = open(log, "r")
 f.seek(0, 2)
 
diff --git a/test/box/reconfigure.result b/test/box/reconfigure.result
index bdb4fc4f4be8d609010c0504fea3acda7d1daa45..935ccdcce078a3a185140e48a95f21c9209bccd9 100644
--- a/test/box/reconfigure.result
+++ b/test/box/reconfigure.result
@@ -6,16 +6,16 @@ box.cfg.reload()
 ---
 - error: 'Could not accept read only ''slab_alloc_arena'' option.
 
-    Could not accept read only ''pid_file'' option.
-
-    Could not accept read only ''logger'' option.
-
     Could not accept read only ''primary_port'' option.
 
     Could not accept read only ''admin_port'' option.
 
     Could not accept read only ''rows_per_wal'' option.
 
+    Could not accept read only ''pid_file'' option.
+
+    Could not accept read only ''logger'' option.
+
     empty configuration file ''tarantool.cfg''.
 
 '
diff --git a/test/box/reconfigure.test.py b/test/box/reconfigure.test.py
index ae579f4b946bf7985d1eb8c748a993ffafd5e9a9..e20aac1bea1c8351c794959ca5e1436e08185662 100644
--- a/test/box/reconfigure.test.py
+++ b/test/box/reconfigure.test.py
@@ -2,7 +2,7 @@
 #
 admin("box.cfg.too_long_threshold")
 # bad1
-server.reconfigure("box/tarantool_bad1.cfg")
+server.reconfigure("box/tarantool_bad1.cfg", override=[''])
 admin("box.cfg.too_long_threshold")
 # good
 server.reconfigure("box/tarantool_good.cfg")
diff --git a/test/box/snapshot.test.py b/test/box/snapshot.test.py
index d2b45cdcac1f133015f597776ee92caadb2c47a1..746b2ca35998a0b21c1eca4f07fafd77ecb6f72f 100644
--- a/test/box/snapshot.test.py
+++ b/test/box/snapshot.test.py
@@ -28,11 +28,11 @@ admin("space:insert{2, 'second tuple'}")
 #
 # Check for other errors, e.g. "Permission denied".
 print "# Make 'var' directory read-only."
-os.chmod(vardir, 0555)
+os.chmod(server.vardir, 0555)
 admin("box.snapshot()")
 
 # cleanup
-os.chmod(vardir, 0755)
+os.chmod(server.vardir, 0755)
 
 admin("space:delete{1}")
 admin("space:delete{2}")
@@ -52,7 +52,7 @@ pid = int(yaml.load(admin("box.info.pid", silent=True))[0])
 lsn = yaml.load(admin("box.info.lsn", silent=True))[0]
 
 snapshot = str(lsn).zfill(20) + ".snap"
-snapshot = os.path.join(vardir, snapshot)
+snapshot = os.path.join(server.vardir, snapshot)
 
 iteration = 0
 
diff --git a/test/box/xlog.test.py b/test/box/xlog.test.py
index 0abf8aee0e8a197dcabd622dd679058b86a6b9ff..096cb249d52f376a583445d1b3bd22c7ccde395f 100644
--- a/test/box/xlog.test.py
+++ b/test/box/xlog.test.py
@@ -3,7 +3,7 @@ import shutil
 
 from os.path import abspath
 
-# cleanup vardir
+# cleanup server.vardir
 server.stop()
 server.deploy()
 server.stop()
@@ -11,8 +11,8 @@ server.stop()
 print """
 # Inprogress xlog must be renamed before second insert.
 """
-wal_inprogress = os.path.join(vardir, "00000000000000000002.xlog.inprogress")
-wal = os.path.join(vardir, "00000000000000000002.xlog")
+wal_inprogress = os.path.join(server.vardir, "00000000000000000002.xlog.inprogress")
+wal = os.path.join(server.vardir, "00000000000000000002.xlog")
 
 server.start()
 
@@ -31,8 +31,8 @@ print """
 """
 server.start()
 
-wal_inprogress = os.path.join(vardir, "00000000000000000004.xlog.inprogress")
-wal = os.path.join(vardir, "00000000000000000004.xlog")
+wal_inprogress = os.path.join(server.vardir, "00000000000000000004.xlog.inprogress")
+wal = os.path.join(server.vardir, "00000000000000000004.xlog")
 
 admin("box.space[0]:insert{3, 'third tuple'}")
 
@@ -48,8 +48,8 @@ print """
 # An inprogress xlog file with one record must be renamed during recovery.
 """
 
-wal_inprogress = os.path.join(vardir, "00000000000000000005.xlog.inprogress")
-wal = os.path.join(vardir, "00000000000000000005.xlog")
+wal_inprogress = os.path.join(server.vardir, "00000000000000000005.xlog.inprogress")
+wal = os.path.join(server.vardir, "00000000000000000005.xlog")
 
 os.symlink(abspath("box/unfinished.xlog"), wal_inprogress)
 
@@ -63,8 +63,8 @@ print """
 # Empty (zero size) inprogress xlog must be deleted during recovery.
 """
 
-wal_inprogress = os.path.join(vardir, "00000000000000000006.xlog.inprogress")
-wal = os.path.join(vardir, "00000000000000000006.xlog")
+wal_inprogress = os.path.join(server.vardir, "00000000000000000006.xlog.inprogress")
+wal = os.path.join(server.vardir, "00000000000000000006.xlog")
 
 os.symlink(abspath("box/empty.xlog"), wal_inprogress)
 server.start()
@@ -112,12 +112,13 @@ A test case for https://bugs.launchpad.net/tarantool/+bug/1052018
 panic_on_wal_error doens't work for duplicate key errors
 """
 server.stop()
-server.deploy("box/panic_on_wal_error.cfg")
+server.cfgfile_source = "box/panic_on_wal_error.cfg"
+server.deploy()
 server.stop()
 shutil.copy(abspath("box/dup_key1.xlog"),
-            os.path.join(vardir, "00000000000000000002.xlog"))
+            os.path.join(server.vardir, "00000000000000000002.xlog"))
 shutil.copy(abspath("box/dup_key2.xlog"),
-           os.path.join(vardir, "00000000000000000004.xlog"))
+           os.path.join(server.vardir, "00000000000000000004.xlog"))
 server.start()
 admin("box.space[0]:select{1}")
 admin("box.space[0]:select{2}")
diff --git a/test/lib/box_connection.py b/test/lib/box_connection.py
index 4b08d9839515ddb0467ce17fe1bf9799e38b406b..967c8305e837f9b35aa39efe6e549e8d21558288 100644
--- a/test/lib/box_connection.py
+++ b/test/lib/box_connection.py
@@ -21,7 +21,6 @@ __author__ = "Konstantin Osipov <kostja.osipov@gmail.com>"
 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.
 import os
-import sql
 import sys
 import errno
 import ctypes
@@ -29,10 +28,9 @@ import socket
 import struct
 import warnings
 
-from test_suite import check_libs
+import sql
 from tarantool_connection import TarantoolConnection
 
-check_libs()
 from tarantool import Connection as tnt_connection
 from tarantool import Schema
 
@@ -50,7 +48,8 @@ class BoxConnection(TarantoolConnection):
         self.py_con.close()
 
     def reconnect(self):
-        self.disconnect()
+        if self.py_con.connected:
+            self.disconnect()
         self.connect()
 
     def set_schema(self, schemadict):
@@ -71,7 +70,7 @@ class BoxConnection(TarantoolConnection):
         if statement == None:
             return "You have an error in your SQL syntax\n"
         statement.sort = self.sort
- 
+
         if not silent:
             print command
 
diff --git a/test/lib/colorer.py b/test/lib/colorer.py
index 79e68d7f84a5071372f1bafd54b3306865e93dc0..643ea87d20a1c1fe84961b317b3092011c34ef5a 100644
--- a/test/lib/colorer.py
+++ b/test/lib/colorer.py
@@ -131,7 +131,6 @@ class Colorer(object):
         self.stdout = sys.stdout
         self.is_term = self.stdout.isatty()
         self.colors = int(os.popen('tput colors').read()) if self.is_term else None
-        print os.getenv('TT_SCHEMA')
         schema = os.getenv('TT_SCHEMA', 'ascetic')
         if schema == 'ascetic':
             self.schema = SchemaAscetic()
diff --git a/test/lib/preprocessor.py b/test/lib/preprocessor.py
index 6fb976d64fde556138ff8bf5aeb6fdeefc7856b0..97f06c584edbcfa881f61f123af6f46f14a5a6de 100644
--- a/test/lib/preprocessor.py
+++ b/test/lib/preprocessor.py
@@ -1,4 +1,5 @@
 import os
+
 import sys
 import shlex
 import socket
@@ -7,6 +8,9 @@ from collections import deque
 
 from lib.admin_connection import AdminConnection
 
+class Namespace(object):
+    pass
+
 class LuaPreprocessorException(Exception):
     def __init__(self, val):
         super(LuaPreprocessorException, self).__init__()
@@ -20,6 +24,11 @@ class State(object):
         self.suite_ini = suite_ini
         self.curcon = [curcon]
         self.tarantool_server = server
+        self.environ = Namespace()
+        nmsp = Namespace()
+        setattr(nmsp, 'admin_port', self.suite_ini['servers']['default'].admin.port)
+        setattr(nmsp, 'primary_port', self.suite_ini['servers']['default'].sql.port)
+        setattr(self.environ, 'default', nmsp)
 
     def parse_preprocessor(self, string):
         token_store = deque()
@@ -96,6 +105,14 @@ class State(object):
                     raise LuaPreprocessorException("Wrong token for filter: expected filter2")
                 ret = temp
             return self.filter(ftype, ref, ret)
+        elif token == 'variable':
+            ftype = token_store.popleft()
+            ref = lexer.get_token()
+            temp = lexer.get_token()
+            if temp != 'to':
+                raise LuaPreprocessorException("Wrong token for filter: exptected 'to', got {0}".format(repr(temp)))
+            ret = lexer.get_token()
+            return self.variable(ftype, ref, ret)
         else:
             raise LuaPreprocessorException("Wrong command: "+repr(lexer.instream.getvalue()))
 
@@ -111,21 +128,25 @@ class State(object):
                 raise LuaPreprocessorException('Server {0} already exists'.format(repr(sname)))
             temp = self.tarantool_server()
             if 'configuration' in opts:
-                temp.config = opts['configuration'][1:-1]
+                temp.cfgfile_source = opts['configuration'][1:-1]
             else:
-                temp.cofnfig = self.suite_ini['config']
+                temp.cfgfile_source = self.suite_ini['config']
             if 'need_init' in opts:
-                temp.need_init = True if opts['need_init'] == 'True' else False
+                temp.need_init   = True if opts['need_init'] == 'True' else False
             if 'init' in opts:
                 temp.init_lua = params['init'][1:-1]
+            if 'rpl_master' in opts:
+                temp.rpl_master = (self.suite_ini['servers'][opts['rpl_master']] if (not opts['rpl_master'] == 'None') else None)
+            elif 'hot_master' in opts:
+                temp.hot_master = (self.suite_ini['servers'][opts['hot_master']] if (not opts['hot_master'] == 'None') else None)
             temp.vardir = os.path.join(self.suite_ini['vardir'], sname)
-            temp.binary = temp.find_exe(self.suite_ini['builddir'])
+            temp.name = sname
             self.suite_ini['servers'][sname] = temp
-            temp.configure(temp.config)
-            temp.install(temp.binary,
-                    temp.vardir, temp.mem, True)
-            if temp.need_init:
-                temp.init()
+            self.suite_ini['servers'][sname].deploy(silent=True)
+            nmsp = Namespace()
+            setattr(nmsp, 'admin_port', temp.admin.port)
+            setattr(nmsp, 'primary_port', temp.sql.port)
+            setattr(self.environ, sname, nmsp)
         elif ctype == 'start':
             if sname not in self.suite_ini['servers']:
                 raise LuaPreprocessprException('Can\'t start nonexistent server '+repr(sname))
@@ -140,7 +161,7 @@ class State(object):
                 raise LuaPreprocessorException('Can\'t stop nonexistent server '+repr(sname))
             self.suite_ini['servers'][sname].stop()
             for cname in [k for k, v in self.suite_ini['connections'].iteritems() if v[1] == 'sname']:
-                self.suite_ini['connections'][cname].disconnect()
+                self.suite_ini['connections'][cname][0].disconnect()
                 self.suite_ini['connections'].pop(cname)
         elif ctype == 'deploy':
             pass
@@ -148,6 +169,10 @@ class State(object):
             if sname not in self.suite_ini['servers']:
                 raise LuaPreprocessorException('Can\'t reconfigure nonexistent server '+repr(sname))
             temp = self.suite_ini['servers'][sname]
+            if 'rpl_master' in opts:
+                temp.rpl_master = (self.suite_ini['servers'][opts['rpl_master']] if (not opts['rpl_master'] == 'None') else None)
+            elif 'hot_master' in opts:
+                temp.hot_master = (self.suite_ini['servers'][opts['hot_master']] if (not opts['hot_master'] == 'None') else None)
             if 'configuration' in opts:
                 temp.reconfigure(opts['configuration'][1:-1], silent = True)
             else:
@@ -161,10 +186,15 @@ class State(object):
                     var_init_lua = os.path.join(temp.vardir, temp.default_init_lua_name)
                     shutil.copy(temp.init_lua, var_init_lua)
                     temp.restart()
+            nmsp = Namespace()
+            setattr(nmsp, 'admin_port', temp.admin.port)
+            setattr(nmsp, 'primary_port', temp.sql.port)
+            setattr(self.environ, sname, nmsp)
         elif ctype == 'cleanup':
             if sname not in self.suite_ini['servers']:
                 raise LuaPreprocessorException('Can\'t cleanup nonexistent server '+repr(sname))
             self.suite_ini['servers'][sname].cleanup()
+            delattr(self.environ, sname)
         else:
             raise LuaPreprocessorException('Unknown command for server: '+repr(ctype))
 
@@ -192,7 +222,7 @@ class State(object):
 
     def filter(self, ctype, ref, ret):
         if ctype == 'push':
-            sys.stdout.push_filter(ref[1:], ret[:-1])
+            sys.stdout.push_filter(ref[1:-1], ret[1:-1])
         elif ctype == 'pop':
             sys.stdout.pop_filter()
         elif ctype == 'clear':
@@ -200,6 +230,12 @@ class State(object):
         else:
             raise LuaPreprocessorException("Wrong command for filters: " + repr(ctype))
 
+    def variable(self, ctype, ref, ret):
+        if ctype == 'set':
+            self.curcon[0](ref+'='+str(eval(ret[1:-1], {}, self.environ.__dict__)), silent=True)
+        else:
+            raise LuaPreprocessorException("Wrong command for variables: " + repr(ctype))
+
     def __call__(self, string):
         string = string[3:].strip()
         self.parse_preprocessor(string)
diff --git a/test/lib/server.py b/test/lib/server.py
index b38ac49d5629dd4815d70d03f9f866fcff73c9a1..d7f8420abf5cce5f8a899a0e30abcb1086a64467 100644
--- a/test/lib/server.py
+++ b/test/lib/server.py
@@ -23,7 +23,7 @@ def check_port(port):
 
 def prepare_gdb(binary, args):
     """Prepare server startup arguments to run under gdb."""
-    args = shlex.split('screen -dmS tnt-gdb gdb %s -ex \'b main\' -ex run' % binary) + args 
+    args = shlex.split('screen -dmS tnt-gdb gdb %s -ex \'b main\' -ex run' % binary) + args
     return args
 
 def prepare_valgrind(args, valgrind_log, valgrind_sup):
@@ -47,19 +47,32 @@ class Server(object):
     replication slaves. The server is started once at the beginning
     of each suite, and stopped at the end."""
 
-    def __new__(cls, core=None):
-        if core  == None:
-            return super(Server, cls).__new__(cls)
-        core = core.lower().strip()
+    @property
+    def vardir(self):
+        if not hasattr(self, '_vardir'):
+            raise ValueError("No vardir specified")
+        return self._vardir
+    @vardir.setter
+    def vardir(self, path):
+        if path == None:
+            return
+        self._vardir = os.path.abspath(path)
+
+
+    def __new__(cls, ini=None):
+        if ini == None or 'core' not in ini or ini['core'] is None:
+            return object.__new__(cls)
+        core = ini['core'].lower().strip()
         cls.mdlname = "lib.{0}_server".format(core.replace(' ', '_'))
         cls.clsname = "{0}Server".format(core.title().replace(' ', ''))
         corecls = __import__(cls.mdlname, fromlist=cls.clsname).__dict__[cls.clsname]
         return corecls.__new__(corecls, core)
 
-    def __init__(self, core):
-        self.core = core
-        self.vardir = None
+    def __init__(self, ini):
+        self.core = ini['core']
+        self.ini = ini
         self.re_vardir_cleanup = ['*.core.*', 'core']
+        self.vardir = ini['vardir']
 
     def prepare_args(self):
         return []
@@ -72,14 +85,10 @@ class Server(object):
             for f in glob.glob(os.path.join(self.vardir, re)):
                 os.remove(f)
 
-    def configure(self, config):
-        pass
     def install(self, binary=None, vardir=None, mem=None, silent=True):
         pass
     def init(self):
         pass
-    def find_exe(self, builddir, silent=True):
-        pass
     def start(self, silent=True):
         pass
     def stop(self, silent=True):
diff --git a/test/lib/sql_ast.py b/test/lib/sql_ast.py
index 780645a5521e10d34c84e5206f1fed21bff59d37..1d36c91e88bf7ae19d667df25d9f69dc5c5e784d 100644
--- a/test/lib/sql_ast.py
+++ b/test/lib/sql_ast.py
@@ -4,9 +4,28 @@ import sys
 import ctypes
 import struct
 
-from lib.test_suite import check_libs
+def check_libs():
+    deps = [
+        ('msgpack', 'msgpack-python'),
+        ('tarantool', 'tarantool-python/src')
+    ]
+    base_path = os.path.dirname(os.path.abspath(__file__))
+
+    for (mod_name, mod_dir) in deps:
+        mod_path = os.path.join(base_path, mod_dir)
+        if mod_path not in sys.path:
+            sys.path = [mod_path] + sys.path
+
+    for (mod_name, _mod_dir) in deps:
+        try:
+            __import__(mod_name)
+        except ImportError as e:
+            color_stdout("\n\nNo %s library found\n" % mod_name, schema='error')
+            print(e)
+            sys.exit(1)
 
 check_libs()
+
 from tarantool.request import (
         RequestPing,
         RequestInsert,
diff --git a/test/lib/tarantool_connection.py b/test/lib/tarantool_connection.py
index b8b9246f52c231ce1728886e035231e6910c8de1..3b2235a822964defe03e3b3389b59ca130b48ded 100644
--- a/test/lib/tarantool_connection.py
+++ b/test/lib/tarantool_connection.py
@@ -72,6 +72,6 @@ class TarantoolConnection(object):
 
     def __exit__(self, type, value, tb):
         self.disconnect()
- 
+
     def __call__(self, command, silent=False, simple=False):
         return self.execute(command, silent)
diff --git a/test/lib/tarantool_server.py b/test/lib/tarantool_server.py
index 90f934f0423db9ad445732711431377c1027bde7..ca8012ab6ede76d78462835e4415efb8d858b61f 100644
--- a/test/lib/tarantool_server.py
+++ b/test/lib/tarantool_server.py
@@ -4,60 +4,243 @@ import sys
 import glob
 import time
 import yaml
-import errno
-import daemon
-import socket
-import signal
 import shlex
+import daemon
+import random
 import shutil
+import difflib
+import signal
+import socket
+import filecmp
 import traceback
 import subprocess
-import ConfigParser
+import collections
 
 from lib.server import Server
-from lib.box_connection import BoxConnection
-from lib.test_suite import FilteredStream, Test, check_libs
 from lib.admin_connection import AdminConnection
+from lib.box_connection import BoxConnection
 
 from lib.preprocessor import State
 from lib.colorer import Colorer
-try:
-    import cStringIO as StringIO
-except ImportError:
-    import StringIO
 
-check_libs()
-import tarantool
 color_stdout = Colorer()
+try:
+    from cStringIO import StringIO
+except ImportError:
+    from StringIO import StringIO
 
-def check_port(port):
-    """Check if the port we're connecting to is available"""
+def check_port(port, rais=True):
     try:
         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         sock.connect(("localhost", port))
-    except socket.error as e:
-        return
-    raise RuntimeError("The server is already running on port {0}".format(port))
-
-def prepare_gdb(binary, args):
-    """Prepare server startup arguments to run under gdb."""
-    args = shlex.split('screen -dmS tnt-gdb gdb %s -ex \'b main\' -ex run' % binary) + args
-    return args
-
-def prepare_valgrind(args, valgrind_log, valgrind_sup):
-    "Prepare server startup arguments to run under valgrind."
-    args = [ "valgrind", "--log-file={0}".format(valgrind_log),
-             "--suppressions={0}".format(valgrind_sup),
-             "--gen-suppressions=all", "--show-reachable=yes", "--leak-check=full",
-             "--read-var-info=yes", "--quiet" ] + args
-    return args
+    except socket.error:
+        return True
+    if rais:
+        raise RuntimeError("The server is already running on port {0}".format(port))
+    return False
+
+def find_port(port):
+    while port < 65536:
+        if check_port(port, False):
+            return port
+        port += 1
+    return find_port(34000)
+
+def find_in_path(name):
+    path = os.curdir + os.pathsep + os.environ["PATH"]
+    for _dir in path.split(os.pathsep):
+        exe = os.path.join(_dir, name)
+        if os.access(exe, os.X_OK):
+            return exe
+    return ''
+
+def check_valgrind_log(path_to_log):
+    """ Check that there were no warnings in the log."""
+    return os.path.exists(path_to_log) and os.path.getsize(path_to_log) != 0
+
+def print_tail_n(filename, num_lines):
+    """Print N last lines of a file."""
+    with open(filename, "r+") as logfile:
+        tail_n = collections.deque(logfile, num_lines)
+        for line in tail_n:
+            color_stdout(line, schema='tail')
+
+
+class FilteredStream:
+    """Helper class to filter .result file output"""
+    def __init__(self, filename):
+        self.stream = open(filename, "w+")
+        self.filters = []
+
+    def write(self, fragment):
+        """Apply all filters, then write result to the undelrying stream.
+        Do line-oriented filtering: the fragment doesn't have to represent
+        just one line."""
+        fragment_stream = StringIO(fragment)
+        skipped = False
+        for line in fragment_stream:
+            original_len = len(line.strip())
+            for pattern, replacement in self.filters:
+                line = re.sub(pattern, replacement, line)
+                # don't write lines that are completely filtered out:
+                skipped = original_len and not line.strip()
+                if skipped:
+                    break
+            if not skipped:
+                self.stream.write(line)
+
+    def push_filter(self, pattern, replacement):
+        self.filters.append([pattern, replacement])
+
+    def pop_filter(self):
+        self.filters.pop()
+
+    def clear_all_filters(self):
+        self.filters = []
+
+    def close(self):
+        self.clear_all_filters()
+        self.stream.close()
+
+    def flush(self):
+        self.stream.flush()
+
+
+class Test:
+    """An individual test file. A test object can run itself
+    and remembers completion state of the run.
+
+    If file <test_name>.skipcond is exists it will be executed before
+    test and if it sets self.skip to True value the test will be skipped.
+    """
+
+    def __init__(self, name, args, suite_ini):
+        """Initialize test properties: path to test file, path to
+        temporary result file, path to the client program, test status."""
+        rg = re.compile('.test.*')
+        self.name = name
+        self.args = args
+        self.suite_ini = suite_ini
+        self.result = rg.sub('.result', name)
+        self.skip_cond = rg.sub('.skipcond', name)
+        self.tmp_result = os.path.join(self.args.vardir,
+                                       os.path.basename(self.result))
+        self.reject = rg.sub('.reject', name)
+        self.is_executed = False
+        self.is_executed_ok = None
+        self.is_equal_result = None
+        self.is_valgrind_clean = True
+        self.is_terminated = False
+
+    def passed(self):
+        """Return true if this test was run successfully."""
+        return self.is_executed and self.is_executed_ok and self.is_equal_result
+
+    def execute(self):
+        pass
+
+    def run(self, server):
+        """Execute the test assuming it's a python program.
+        If the test aborts, print its output to stdout, and raise
+        an exception. Else, comprare result and reject files.
+        If there is a difference, print it to stdout and raise an
+        exception. The exception is raised only if is_force flag is
+        not set."""
+        diagnostics = "unknown"
+        save_stdout = sys.stdout
+        try:
+            self.skip = False
+            if os.path.exists(self.skip_cond):
+                sys.stdout = FilteredStream(self.tmp_result)
+                stdout_fileno = sys.stdout.stream.fileno()
+                execfile(self.skip_cond, dict(locals(), **server.__dict__))
+                sys.stdout.close()
+                sys.stdout = save_stdout
+            if not self.skip:
+                sys.stdout = FilteredStream(self.tmp_result)
+                stdout_fileno = sys.stdout.stream.fileno()
+                self.execute(server)
+                sys.stdout.flush()
+            self.is_executed_ok = True
+        except Exception as e:
+            traceback.print_exc(e)
+            diagnostics = str(e)
+        finally:
+            if sys.stdout and sys.stdout != save_stdout:
+                sys.stdout.close()
+            sys.stdout = save_stdout
+        self.is_executed = True
+        sys.stdout.flush()
+
+        if not self.skip:
+            if self.is_executed_ok and os.path.isfile(self.result):
+                self.is_equal_result = filecmp.cmp(self.result, self.tmp_result)
+        else:
+            self.is_equal_result = 1
+
+        if self.args.valgrind:
+            self.is_valgrind_clean = (check_valgrind_log(server.valgrind_log) == False)
+
+        if self.skip:
+            color_stdout("[ skip ]\n", schema='test_skip')
+            if os.path.exists(self.tmp_result):
+                os.remove(self.tmp_result)
+        elif self.is_executed_ok and self.is_equal_result and self.is_valgrind_clean:
+            color_stdout("[ pass ]\n", schema='test_pass')
+            if os.path.exists(self.tmp_result):
+                os.remove(self.tmp_result)
+        elif (self.is_executed_ok and not self.is_equal_result and not
+              os.path.isfile(self.result)):
+            os.rename(self.tmp_result, self.result)
+            color_stdout("[ new ]\n", schema='test_new')
+        else:
+            os.rename(self.tmp_result, self.reject)
+            color_stdout("[ fail ]\n", schema='test_fail')
+
+            where = ""
+            if not self.is_executed_ok:
+                self.print_diagnostics(self.reject, "Test failed! Last 10 lines of the result file:\n")
+                server.print_log(15)
+                where = ": test execution aborted, reason '{0}'".format(diagnostics)
+            elif not self.is_equal_result:
+                self.print_unidiff()
+                server.print_log(15)
+                where = ": wrong test output"
+            elif not self.is_valgrind_clean:
+                os.remove(self.reject)
+                self.print_diagnostics(server.valgrind_log, "Test failed! Last 10 lines of valgrind.log:\n")
+                where = ": there were warnings in valgrind.log"
+
+            if not self.args.is_force:
+                raise RuntimeError("Failed to run test " + self.name + where)
+
+    def print_diagnostics(self, logfile, message):
+        """Print 10 lines of client program output leading to test
+        failure. Used to diagnose a failure of the client program"""
+
+        color_stdout(message, schema='error')
+        print_tail_n(logfile, 10)
+
+    def print_unidiff(self):
+        """Print a unified diff between .test and .result files. Used
+        to establish the cause of a failure when .test differs
+        from .result."""
+
+        color_stdout("\nTest failed! Result content mismatch:\n", schema='error')
+        with open(self.result, "r") as result:
+            with open(self.reject, "r") as reject:
+                result_time = time.ctime(os.stat(self.result).st_mtime)
+                reject_time = time.ctime(os.stat(self.reject).st_mtime)
+                diff = difflib.unified_diff(result.readlines(),
+                                            reject.readlines(),
+                                            self.result,
+                                            self.reject,
+                                            result_time,
+                                            reject_time)
+
+                color_stdout.writeout_unidiff(diff)
 
-def check_tmpfs_exists():
-    return os.uname()[0] in 'Linux' and os.path.isdir("/dev/shm")
 
-def create_tmpfs_vardir(vardir):
-    os.makedirs(os.path.join("/dev/shm", vardir))
-    os.symlink(os.path.join("/dev/shm", vardir), vardir)
 
 class FuncTest(Test):
     def execute(self, server):
@@ -66,7 +249,7 @@ class FuncTest(Test):
 
 class LuaTest(FuncTest):
     def execute(self, server):
-        ts = State(self.suite_ini, server.admin, TarantoolServer)
+        ts = State(self.suite_ini, server._admin, TarantoolServer)
         cmd = None
 
         def send_command(command):
@@ -77,7 +260,7 @@ class LuaTest(FuncTest):
 
         for line in open(self.name, 'r'):
             if not cmd:
-                cmd = StringIO.StringIO()
+                cmd = StringIO()
             if line.find('--#') == 0:
                 rescom = cmd.getvalue().replace('\n\n', '\n')
                 if rescom:
@@ -107,420 +290,613 @@ class PythonTest(FuncTest):
         execfile(self.name, dict(locals(), **server.__dict__))
 
 
-class TarantoolConfigFile:
-    """ConfigParser can't read files without sections, work it around"""
-    def __init__(self, fp, section_name):
-        self.fp = fp
-        self.section_name = "[" + section_name + "]"
+class TarantoolConfig(object):
+    def __init__(self, path):
+        self.path = path
 
-    def readline(self):
-        if self.section_name:
-            section_name = self.section_name
-            self.section_name = None
-            return section_name
-        # tarantool.cfg puts string values in quote
-        return self.fp.readline().replace("\"", '')
+    def parse(self, gen = False):
+        cfg = {}
+        with open(self.path, 'r') as f:
+            for line in f:
+                line = [part.strip() for part in line.split('=', 1)]
+                if not line or not line[0] or line[0][0] == '#':
+                    continue
+                if len(line) != 2:
+                    raise Exception("Bad cfg line: {line}. file: {file}".format(\
+                                    line = repr(line), file = repr(self.path)))
+                cfg[line[0]] = line[1]
+        return cfg
+
+    def generate(self, original):
+        with open(self.path, 'w') as f:
+            for el in original.iteritems():
+                f.write(' = '.join(el) + '\n')
+
+class TarantoolLog(object):
+    def __init__(self, path):
+        self.path = path
+        self.log_begin = 0
+
+    def positioning(self):
+        if os.path.exists(self.path):
+            with open(self.path, 'r') as f:
+                f.seek(0, os.SEEK_END)
+                self.log_begin = f.tell()
+
+    def seek_from(self, msg, proc=None):
+        while True:
+            if os.path.exists(self.path):
+                break
+            time.sleep(0.001)
+
+        with open(self.path, 'r') as f:
+            f.seek(self.log_begin, os.SEEK_SET)
+            cur_pos = self.log_begin
+            while True:
+                if not (proc is None):
+                    if not (proc.poll() is None):
+                        raise OSError("Can't start Tarantool");
+                log_str = f.readline()
+                if not log_str:
+                    time.sleep(0.001)
+                    f.seek(cur_pos, os.SEEK_SET)
+                    continue
+                if log_str.find(msg) != -1:
+                    return
+                cur_pos = f.tell()
+
+class Mixin(object):
+    pass
+
+class ValgrindMixin(Mixin):
+    default_valgr = {
+            "logfile":        "valgrind.log",
+            "suppress_path":        "share/",
+            "suppress_name": "tarantool.sup"}
+
+    @property
+    def valgrind_log(self):
+        return os.path.join(self.vardir, self.default_valgr['logfile'])
+
+    @property
+    def valgrind_sup(self):
+        if not hasattr(self, '_valgrind_sup') or not self._valgrind_sup:
+            return os.path.join(self.testdir,
+                                self.default_valgr['suppress_path'],
+                                self.default_valgr['suppress_name'])
+        return self._valgrind_sup
+    @valgrind_sup.setter
+    def valgrind_sup(self, val):
+        self._valgrind_sup = os.path.abspath(val)
+
+    @property
+    def valgrind_sup_output(self):
+        return os.path.join(self.vardir, self.default_valgr['suppress_name'])
+
+    def prepare_args(self):
+        if not find_in_path('valgrind'):
+            raise OSError((-1, '`valgrind` executables not found in PATH'))
+        return  shlex.split("valgrind --log-file={log} --suppressions={sup} \
+                --gen-suppressions=all --leak-check=full \
+                --read-var-info=yes --quiet {bin}".format(log = self.valgrind_log,
+                                                        sup = self.valgrind_sup,
+                                                        bin = self.init_lua if self.shebang else self.binary))
+
+    def wait_stop(self):
+        return self.process.wait()
+
+class GdbMixin(Mixin):
+    default_gdb = {
+        "name": "tarantool-gdb"}
+
+    def start_and_exit(self):
+        color_stdout('You started the server in gdb mode.\n', schema='info')
+        color_stdout('To attach, use `screen -r tarantool-gdb`\n', schema='info')
+        TarantoolServer.start_and_exit(self)
+
+
+    def prepare_args(self):
+        if not find_in_path('screen'):
+            raise OSError((-1, '`screen` executables not found in PATH'))
+        if not find_in_path('gdb'):
+            raise OSError((-1, '`gdb` executables not found in PATH'))
+        color_stdout('You started the server in gdb mode.\n', schema='info')
+        color_stdout('To attach, use `screen -r tarantool-gdb`\n', schema='info')
+        return shlex.split("screen -dmS {0} gdb {1} -ex \
+                \'b main\' -ex \'run >> {2} 2>> {2}\'".format(self.default_gdb['name'],
+                                                       self.init_lua if self.shebang else self.binary,
+                                                       self.logfile))
+
+    def wait_stop(self):
+        self.kill_old_server()
+        self.process.wait()
 
 class TarantoolServer(Server):
-    def __new__(cls, core="tarantool"):
+    default_tarantool = {
+            "bin":       "tarantool_box",
+            "config":    "tarantool.cfg",
+            "logfile":   "tarantool.log",
+            "init":           "init.lua",
+            "pidfile":         "box.pid",
+            "name":            "default"}
+    generate_ports = [
+            'primary_port',
+            'secondary_port',
+            'admin_port',
+#            'replication_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 _sql_ro(self):
+        if not hasattr(self, 'sql_ro'): self.sql_ro = None
+        return self.sql_ro
+    @_sql_ro.setter
+    def _sql_ro(self, port):
+        try:
+            port = int(port)
+        except ValueError as e:
+            raise ValueError("Bad port number: '%s'" % port)
+        if not hasattr(self, 'sql_ro') or self.sql_ro is None:
+            self.sql_ro = BoxConnection('localhost', port)
+            return
+        if self.sql_ro.port != port:
+            self.sql_ro.port = port
+            self.sql_ro.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, core="tarantool"):
-        Server.__init__(self, core)
-        self.default_bin_name = "tarantool_box"
-        self.default_config_name = "tarantool.cfg"
-        self.default_log_name = "tarantool.log"
-        self.default_init_lua_name = "init.lua"
-        # append additional cleanup patterns
-        self.re_vardir_cleanup += ['*.snap',
-                                   '*.xlog',
-                                   '*.inprogress',
-                                   '*.cfg',
-                                   '*.sup',
-                                   '*.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 __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()
 
-    def find_exe(self, builddir, silent=True):
-        "Locate server executable in the build dir or in the PATH."
-        self.builddir = builddir
+    @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')
+            color_stdout(path + ' ...\n', schema='path')
         for _dir in path.split(os.pathsep):
-            exe = os.path.join(_dir, self.default_bin_name)
+            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, 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))
-
+    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 os.access(self.vardir, os.F_OK):
+            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')
+                color_stdout('    Found old vardir, deleting ...\n', schema='serv_text')
             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.port = self.admin_port
-        self.admin = AdminConnection("localhost", self.admin_port)
-        self.sql = BoxConnection("localhost", self.primary_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)
+        self.copy_files()
+        self.configure()
+        return
 
-    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:
-            color_stdout("tarantool_box --init-storage error: \n%s\n" %  _init.stdout.read(), schema='error')
-            raise subprocess.CalledProcessError(retcode, cmd)
-
-    def get_param(self, param):
-        if param:
-            data = yaml.load(self.admin("box.info." + param, silent=True))[0]
+    def deploy(self, silent=True):
+        self.install(silent)
+        if not self._start_and_exit:
+            self.start(silent)
         else:
-            data = yaml.load(self.admin("box.info", silent=True))
-        return data
+            self.start_and_exit()
 
-    def wait_lsn(self, lsn):
-        while True:
-            curr_lsn = int(self.get_param("lsn"))
-            if (curr_lsn >= lsn):
-                break
-            time.sleep(0.01)
+    def configure(self, config=None):
+        self.copy_config(config)
+        self.port   = self.conf['admin_port']
+        self._sql    = self.conf['primary_port']
+        self._sql_ro = self.conf['secondary_port']
+        self._admin  = self.conf['admin_port']
 
-    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)
+    def reconfigure(self, config, silent=False, override=['all']):
+        if config == None:
+            os.unlink(self.cfgfile)
         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()
+            self.cfgfile_source = config
+            self.copy_config(override=override)
+        self.admin.execute("box.cfg.reload()", silent=silent)
 
-        self.log_path = os.path.join(self.vardir, 'tarantool.log')
-        self.log_pos  = 0
+    def copy_config(self, rand=True, override = ['all']):
+        override_all = (True if 'all' in override else False)
+
+        port = random.randrange(34000, 65535)
+        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['replication_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):
+                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_sorce, 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()
 
-        if self.is_started:
+    def start(self, silent=True):
+        if self.status == 'started':
             if not silent:
-                color_stdout("The server is already started.\n", schema='lerror')
+                color_stdout('The server is already started.\n', schema='lerror')
             return
-
-        if not silent:
+        if not silent or self._start_and_exit:
             color_stdout("Starting the server ...\n", schema='serv_text')
-            version = self.version()
             color_stdout("Starting ", schema='serv_text')
-            color_stdout(os.path.basename(self.binary), " \n", schema='path')
-            color_stdout(version, "\n", schema='version')
+            color_stdout((os.path.basename(self.binary) if not self.shebang else self.shebang) + " \n", schema='path')
+            color_stdout(self.version() + "\n", schema='version')
 
-        if os.path.exists(self.log_path) and os.path.exists(self.log_path):
-            with open(self.log_path, 'r') as f:
-                f.seek(0, os.SEEK_END)
-                self.log_pos = f.tell()
+        check_port(self.conf['admin_port'])
 
-        check_port(self.port)
         args = self.prepare_args()
-
-        if self.gdb:
-            args = prepare_gdb(self.binary, args)
-            color_stdout("You started the server in gdb mode.\n", schema='info')
-            color_stdout("To attach, use `screen -r tnt-gdb`\n", schema='info')
-        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.logfile_pos = self.logfile
+        self.process = subprocess.Popen(args,
+                cwd = self.vardir,
+                stdout=self.log_des,
+                stderr=self.log_des)
         self.wait_until_started()
-        # Set is_started flag, to nicely support cleanup during an exception.
-        self.is_started = True
+        self.status = 'started'
 
+    def wait_stop(self):
+        self.process.wait()
 
     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 self.status != 'started':
             if not silent:
-                color_stdout("The server is not started.\n", schema='lerror')
+                color_stdout('The server is not started.\n', schema='lerror')
             return
-
         if not silent:
-            color_stdout("Stopping the server ...\n", schema='serv_text')
-
-        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)
+            color_stdout('Stopping the server ...\n', schema='serv_text')
+        self.process.terminate()
+        self.wait_stop()
+        self.status = None
 
     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
+        self.stop()
+        self.start()
 
     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
-
+            return False
         if not silent:
-            color_stdout("    Found old server, pid {0}, killing ...".format(pid), schema='info')
-
+            color_stdout('    Found old server, pid {0}, killing ...'.format(pid), schema='info')
         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 print_log(self, lines):
-        color_stdout("\nLast {0} lines of Tarantool Log file:\n".format(lines), schema='error')
-        with open(os.path.join(self.vardir, 'tarantool.log'), 'r') as log:
-            return log.readlines()[-lines:]
+        self.wait_until_stopped(pid)
+        return True
 
     def wait_until_started(self):
-        """Wait until the server is started and accepting connections"""
+        """ Wait until server is started.
 
-        while self.read_pidfile() == -1:
-            time.sleep(0.001)
+        Server consists of two parts:
+        1) wait until server is listening on sockets
+        2) wait until server tells us his status
 
-        is_connected = False
-        while not is_connected and not self.gdb:
-            if self.process.poll():
-                raise OSError("Can't start tarantool");
-            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)
+        """
 
-        while True:
-            try:
-                open(self.log_path, 'r')
-                break
-            except IOError:
-                pass
+        self.logfile_pos.seek_from('entering the event loop\n', self.process if not self.gdb else None)
 
-        with open(self.log_path, 'r') as f:
-            f.seek(self.log_pos, os.SEEK_SET)
-            cur_pos = self.log_pos
-            while True:
-                log_str = f.readline()
-                if not log_str:
-                    f.seek(cur_pos, os.SEEK_SET)
-                    continue
-                if log_str.find("entering the event loop\n") != -1:
-                    return True
-                cur_pos = f.tell()
+        while True:
+            temp = AdminConnection('localhost', self.conf['admin_port'])
+            ans = yaml.load(temp.execute('box.info.status'))[0]
+            if ans in ('primary', 'hot_standby') or ans.startswith('replica'):
+                return True
+            else:
+                raise Exception("Strange output for `box.info.status`: %s" % (ans))
 
     def wait_until_stopped(self, pid):
-        """Wait until the server is stoped and has closed sockets"""
         while True:
             try:
-                time.sleep(0.001)
+                time.sleep(0.01)
                 os.kill(pid, 0)
                 continue
             except OSError as err:
-                if err.errno == errno.ESRCH:
-                    break
-                raise
+                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):
-            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")))]
+            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
diff --git a/test/lib/test_suite.py b/test/lib/test_suite.py
index 72ae7c2ffb58f0ec3a3256f4a943ec04e32cf929..ca32fb80ea5adb796890dc383e3f6e5a76a6d15a 100644
--- a/test/lib/test_suite.py
+++ b/test/lib/test_suite.py
@@ -4,12 +4,13 @@ import sys
 import time
 import shutil
 import difflib
-import filecmp
 import threading
 import traceback
 import collections
 import ConfigParser
 
+
+from lib.tarantool_server import TarantoolServer
 from lib.server import Server
 from lib.colorer import Colorer
 
@@ -39,47 +40,6 @@ def check_libs():
             print(e)
             sys.exit(1)
 
-
-class FilteredStream:
-    """Helper class to filter .result file output"""
-    def __init__(self, filename):
-        self.stream = open(filename, "w+")
-        self.filters = []
-
-    def write(self, fragment):
-        """Apply all filters, then write result to the undelrying stream.
-        Do line-oriented filtering: the fragment doesn't have to represent
-        just one line."""
-        fragment_stream = StringIO(fragment)
-        skipped = False
-        for line in fragment_stream:
-            original_len = len(line.strip())
-            for pattern, replacement in self.filters:
-                line = re.sub(pattern, replacement, line)
-                # don't write lines that are completely filtered out:
-                skipped = original_len and not line.strip()
-                if skipped:
-                    break
-            if not skipped:
-                self.stream.write(line)
-
-    def push_filter(self, pattern, replacement):
-        self.filters.append([pattern, replacement])
-
-    def pop_filter(self):
-        self.filters.pop()
-
-    def clear_all_filters(self):
-        self.filters = []
-
-    def close(self):
-        self.clear_all_filters()
-        self.stream.close()
-
-    def flush(self):
-        self.stream.flush()
-
-
 def check_valgrind_log(path_to_log):
     """ Check that there were no warnings in the log."""
     return os.path.exists(path_to_log) and os.path.getsize(path_to_log) != 0
@@ -93,141 +53,6 @@ def print_tail_n(filename, num_lines):
             color_stdout(line, schema='tail')
 
 
-class Test:
-    """An individual test file. A test object can run itself
-    and remembers completion state of the run.
-
-    If file <test_name>.skipcond is exists it will be executed before
-    test and if it sets self.skip to True value the test will be skipped.
-    """
-
-    def __init__(self, name, args, suite_ini):
-        """Initialize test properties: path to test file, path to
-        temporary result file, path to the client program, test status."""
-        rg = re.compile('.test.*')
-        self.name = name
-        self.args = args
-        self.suite_ini = suite_ini
-        self.result = rg.sub('.result', name)
-        self.skip_cond = rg.sub('.skipcond', name)
-        self.tmp_result = os.path.join(self.args.vardir,
-                                       os.path.basename(self.result))
-        self.reject = rg.sub('.reject', name)
-        self.is_executed = False
-        self.is_executed_ok = None
-        self.is_equal_result = None
-        self.is_valgrind_clean = True
-        self.is_terminated = False
-
-    def passed(self):
-        """Return true if this test was run successfully."""
-        return self.is_executed and self.is_executed_ok and self.is_equal_result
-
-    def execute(self):
-        pass
-
-    def run(self, server):
-        """Execute the test assuming it's a python program.
-        If the test aborts, print its output to stdout, and raise
-        an exception. Else, comprare result and reject files.
-        If there is a difference, print it to stdout and raise an
-        exception. The exception is raised only if is_force flag is
-        not set."""
-        diagnostics = "unknown"
-        save_stdout = sys.stdout
-        try:
-            self.skip = False
-            if os.path.exists(self.skip_cond):
-                sys.stdout = FilteredStream(self.tmp_result)
-                stdout_fileno = sys.stdout.stream.fileno()
-                execfile(self.skip_cond, dict(locals(), **server.__dict__))
-                sys.stdout.close()
-                sys.stdout = save_stdout
-            if not self.skip:
-                sys.stdout = FilteredStream(self.tmp_result)
-                stdout_fileno = sys.stdout.stream.fileno()
-                self.execute(server)
-                sys.stdout.flush()
-            self.is_executed_ok = True
-        except Exception as e:
-            traceback.print_exc(e)
-            diagnostics = str(e)
-        finally:
-            if sys.stdout and sys.stdout != save_stdout:
-                sys.stdout.close()
-            sys.stdout = save_stdout
-        self.is_executed = True
-        sys.stdout.flush()
-
-        if not self.skip:
-            if self.is_executed_ok and os.path.isfile(self.result):
-                self.is_equal_result = filecmp.cmp(self.result, self.tmp_result)
-        else:
-            self.is_equal_result = 1
-
-        if self.args.valgrind:
-            self.is_valgrind_clean = \
-            check_valgrind_log(server.valgrind_log) == False
-
-        elif self.skip:
-            color_stdout("[ skip ]\n", schema='test_skip')
-            if os.path.exists(self.tmp_result):
-                os.remove(self.tmp_result)
-        elif self.is_executed_ok and self.is_equal_result and self.is_valgrind_clean:
-            color_stdout("[ pass ]\n", schema='test_pass')
-            if os.path.exists(self.tmp_result):
-                os.remove(self.tmp_result)
-        elif (self.is_executed_ok and not self.is_equal_result and not
-              os.path.isfile(self.result)):
-            os.rename(self.tmp_result, self.result)
-            color_stdout("[ new ]\n", schema='test_new')
-        else:
-            os.rename(self.tmp_result, self.reject)
-            color_stdout("[ fail ]\n", schema='test_fail')
-
-            where = ""
-            if not self.is_executed_ok:
-                self.print_diagnostics(self.reject, "Test failed! Last 10 lines of the result file:")
-                server.print_log(15)
-                where = ": test execution aborted, reason '{0}'".format(diagnostics)
-            elif not self.is_equal_result:
-                self.print_unidiff()
-                server.print_log(15)
-                where = ": wrong test output"
-            elif not self.is_valgrind_clean:
-                os.remove(self.reject)
-                self.print_diagnostics(server.valgrind_log, "Test failed! Last 10 lines of valgrind.log:")
-                where = ": there were warnings in valgrind.log"
-
-            if not self.args.is_force:
-                raise RuntimeError("Failed to run test " + self.name + where)
-
-    def print_diagnostics(self, logfile, message):
-        """Print 10 lines of client program output leading to test
-        failure. Used to diagnose a failure of the client program"""
-
-        color_stdout(message, schema='error')
-        print_tail_n(logfile, 10)
-
-    def print_unidiff(self):
-        """Print a unified diff between .test and .result files. Used
-        to establish the cause of a failure when .test differs
-        from .result."""
-
-        color_stdout("\nTest failed! Result content mismatch:\n", schema='error')
-        with open(self.result, "r") as result:
-            with open(self.reject, "r") as reject:
-                result_time = time.ctime(os.stat(self.result).st_mtime)
-                reject_time = time.ctime(os.stat(self.reject).st_mtime)
-                diff = difflib.unified_diff(result.readlines(),
-                                            reject.readlines(),
-                                            self.result,
-                                            self.reject,
-                                            result_time,
-                                            reject_time)
-
-                color_stdout.writeout_unidiff(diff)
-
 class TestSuite:
     """Each test suite contains a number of related tests files,
     located in the same directory on disk. Each test file has
@@ -258,6 +83,7 @@ class TestSuite:
         config = ConfigParser.ConfigParser()
         config.read(os.path.join(suite_path, "suite.ini"))
         self.ini.update(dict(config.items("default")))
+        self.ini.update(self.args.__dict__)
 
         for i in ["config", "init_lua"]:
             self.ini[i] = os.path.join(suite_path, self.ini[i]) if i in self.ini else None
@@ -267,17 +93,18 @@ class TestSuite:
             self.ini[i] = map(lambda x: os.path.join(suite_path, x),
                     dict.fromkeys(self.ini[i].split()) if i in self.ini else
                     dict())
-        for i in ["timeout"]:
-            self.ini[i] = int(self.ini[i]) if i in self.ini else 10
-
+        for i in ["random_ports"]:
+            self.ini[i] = True if i not in self.ini or self.ini[i].lower() == 'true' else False
         try:
-            self.server = Server(self.ini["core"])
+            if self.ini['core'] == 'tarantool':
+                self.server = TarantoolServer(self.ini)
+            else:
+                self.server = Server(self.ini)
             self.ini["server"] = self.server
         except Exception as e:
             print e
             raise RuntimeError("Unknown server: core = {0}".format(
                                self.ini["core"]))
-
         color_stdout("Collecting tests in ", schema='ts_text')
         color_stdout(repr(suite_path), schema='path')
         color_stdout(": ", self.ini["description"], ".\n", schema='ts_text')
@@ -291,19 +118,12 @@ class TestSuite:
         if not self.tests:
             # noting to test, exit
             return []
-
-        self.server.deploy(self.ini["config"],
-                      self.server.find_exe(self.args.builddir, silent=False),
-                      self.args.vardir, self.args.mem, self.args.start_and_exit,
-                      self.args.gdb, self.args.valgrind,
-                      init_lua=self.ini["init_lua"], silent=False)
+        self.server.deploy(silent=False)
         if self.ini['core'] != 'unittest':
             self.ini['servers'] = {'default' : self.server}
             self.ini['connections'] = {'default' : [self.server.admin, 'default']}
             self.ini['vardir'] = self.args.vardir
             self.ini['builddir'] = self.args.builddir
-        for i in self.ini['lua_libs']:
-            shutil.copy(i, self.args.vardir)
 
         if self.args.start_and_exit:
             color_stdout("    Start and exit requested, exiting...\n", schema='info')
@@ -342,13 +162,15 @@ class TestSuite:
             raise
 
         if failed_tests:
-            color_stdout("Failed {0} tests: {1}.".format(len(failed_tests),
+            color_stdout("Failed {0} tests: {1}.\n".format(len(failed_tests),
                                                 ", ".join(failed_tests)),
                                                 schema='error')
 
         if self.args.valgrind and check_valgrind_log(self.server.valgrind_log):
-            color_stdout("  Error! There were warnings/errors in valgrind log file:", schema='error')
+            color_stdout(shortsep, "\n", schema='separator')
+            color_stdout("  Error! There were warnings/errors in valgrind log file:\n", schema='error')
             print_tail_n(self.server.valgrind_log, 20)
+            color_stdout(shortsep, "\n", schema='separator')
             return ['valgrind error in ' + self.suite_path]
         return failed_tests
 
diff --git a/test/lib/unittest_server.py b/test/lib/unittest_server.py
index 909151048126a426ff2987fda77d586ba3251af8..2810df0a49985a84bb8723b9b198a42913ffe38e 100644
--- a/test/lib/unittest_server.py
+++ b/test/lib/unittest_server.py
@@ -6,8 +6,8 @@ import traceback
 import subprocess
 from subprocess import Popen, PIPE
 
-from server import Server
-from test_suite import FilteredStream, Test
+from lib.server import Server
+from lib.tarantool_server import Test
 
 class UnitTest(Test):
     def execute(self, server):
@@ -17,22 +17,38 @@ class UnitTest(Test):
 
 class UnittestServer(Server):
     """A dummy server implementation for unit test suite"""
-    def __new__(cls, core="unittest"):
+    def __new__(cls, ini=None):
         return Server.__new__(cls)
 
-    def __init__(self, core="unittest"):
-        Server.__init__(self, core)
+    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
+        }; ini.update(_ini)
+        core = ini['core']
+        Server.__init__(self, ini)
+        self.vardir = ini['vardir']
+        self.builddir = ini['builddir']
         self.debug = False
 
     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=True):
+               init_lua=None, silent=True, need_init=True):
         self.vardir = vardir
         if not os.access(self.vardir, os.F_OK):
             os.makedirs(self.vardir)
 
-    def find_exe(self, builddir, silent=False):
-        self.builddir = builddir
+    @classmethod
+    def find_exe(cls, builddir):
+        cls.builddir = builddir
 
     def find_tests(self, test_suite, suite_path):
         def patterned(test, patterns):
diff --git a/test/replication/consistent.result b/test/replication/consistent.result
index 89fd82b29c2ec0596b9fca2359214289ac7a576a..14b964b16a0c2a91c41e1f9bec6a0ba67a1ee58d 100644
--- a/test/replication/consistent.result
+++ b/test/replication/consistent.result
@@ -1,4 +1,4 @@
---# create server replica with configuration='replication/cfg/replica.cfg'
+--# create server replica with configuration='replication/cfg/replica.cfg', rpl_master=default
 --# start server replica
 --# set connection default
 --# setopt delimiter ';'
@@ -33,11 +33,12 @@ end;
 ...
 --# setopt delimiter ''
 --# set connection default
+--# set variable replica_port to 'replica.primary_port'
 -- set begin lsn on master and replica.
 begin_lsn = box.info.lsn
 ---
 ...
-a = box.net.box.new('127.0.0.1', 33113)
+a = box.net.box.new('127.0.0.1', replica_port)
 ---
 ...
 a:call('_set_pri_lsn', box.info.lsn)
@@ -111,7 +112,7 @@ _print_lsn()
 --------------------
 -- Replica to Master
 --------------------
---# reconfigure server replica with configuration 'replication/cfg/replica_to_master.cfg'
+--# reconfigure server replica with configuration 'replication/cfg/replica_to_master.cfg', rpl_master=None
 --# set connection default
 _insert(11, 20, 'master')
 ---
@@ -171,7 +172,7 @@ _print_lsn()
 -------------------
 -- rollback Replica
 -------------------
---# reconfigure server replica with configuration='replication/cfg/replica.cfg'
+--# reconfigure server replica with configuration='replication/cfg/replica.cfg', rpl_master=default
 _select(11, 20)
 ---
 - [11, 'replica - 11']
@@ -203,7 +204,7 @@ _print_lsn()
 --------------------
 -- Replica to Master
 --------------------
---# reconfigure server replica with configuration='replication/cfg/replica_to_master.cfg'
+--# reconfigure server replica with configuration='replication/cfg/replica_to_master.cfg', rpl_master=None
 --# set connection default
 _insert(21, 30, 'master')
 ---
@@ -273,7 +274,7 @@ _print_lsn()
 -------------------
 -- rollback Replica
 -------------------
---# reconfigure server replica with configuration='replication/cfg/replica.cfg'
+--# reconfigure server replica with configuration='replication/cfg/replica.cfg', rpl_master=default
 _select(21, 30)
 ---
 - [21, 'replica - 21']
@@ -305,7 +306,7 @@ _print_lsn()
 --------------------
 -- Replica to Master
 --------------------
---# reconfigure server replica with configuration='replication/cfg/replica_to_master.cfg'
+--# reconfigure server replica with configuration='replication/cfg/replica_to_master.cfg', rpl_master=None
 --# set connection default
 _insert(31, 40, 'master')
 ---
@@ -395,7 +396,7 @@ _print_lsn()
 -------------------
 -- rollback Replica
 -------------------
---# reconfigure server replica with configuration='replication/cfg/replica.cfg'
+--# reconfigure server replica with configuration='replication/cfg/replica.cfg', rpl_master=default
 _select(31, 50)
 ---
 - [31, 'replica - 31']
diff --git a/test/replication/consistent.test.lua b/test/replication/consistent.test.lua
index 209345f2f9620c1bbf5b85e371333811c1f24e3b..0252adb2a32ad6039a0beb786e631c97529ba3ca 100644
--- a/test/replication/consistent.test.lua
+++ b/test/replication/consistent.test.lua
@@ -1,4 +1,4 @@
---# create server replica with configuration='replication/cfg/replica.cfg'
+--# create server replica with configuration='replication/cfg/replica.cfg', rpl_master=default
 --# start server replica
 --# set connection default
 
@@ -32,10 +32,11 @@ do
 end;
 --# setopt delimiter ''
 --# set connection default
+--# set variable replica_port to 'replica.primary_port'
 
 -- set begin lsn on master and replica.
 begin_lsn = box.info.lsn
-a = box.net.box.new('127.0.0.1', 33113)
+a = box.net.box.new('127.0.0.1', replica_port)
 a:call('_set_pri_lsn', box.info.lsn)
 a:close()
 
@@ -60,7 +61,7 @@ _print_lsn()
 --------------------
 -- Replica to Master
 --------------------
---# reconfigure server replica with configuration 'replication/cfg/replica_to_master.cfg'
+--# reconfigure server replica with configuration 'replication/cfg/replica_to_master.cfg', rpl_master=None
 --# set connection default
 _insert(11, 20, 'master')
 _select(11, 20)
@@ -78,7 +79,7 @@ _print_lsn()
 -------------------
 -- rollback Replica
 -------------------
---# reconfigure server replica with configuration='replication/cfg/replica.cfg'
+--# reconfigure server replica with configuration='replication/cfg/replica.cfg', rpl_master=default
 _select(11, 20)
 --# set connection default
 -- Master LSN:
@@ -93,7 +94,7 @@ _print_lsn()
 --------------------
 -- Replica to Master
 --------------------
---# reconfigure server replica with configuration='replication/cfg/replica_to_master.cfg'
+--# reconfigure server replica with configuration='replication/cfg/replica_to_master.cfg', rpl_master=None
 --# set connection default
 _insert(21, 30, 'master')
 _select(21, 30)
@@ -111,7 +112,7 @@ _print_lsn()
 -------------------
 -- rollback Replica
 -------------------
---# reconfigure server replica with configuration='replication/cfg/replica.cfg'
+--# reconfigure server replica with configuration='replication/cfg/replica.cfg', rpl_master=default
 _select(21, 30)
 
 --# set connection default
@@ -127,7 +128,7 @@ _print_lsn()
 --------------------
 -- Replica to Master
 --------------------
---# reconfigure server replica with configuration='replication/cfg/replica_to_master.cfg'
+--# reconfigure server replica with configuration='replication/cfg/replica_to_master.cfg', rpl_master=None
 --# set connection default
 _insert(31, 40, 'master')
 _select(31, 40)
@@ -145,7 +146,7 @@ _print_lsn()
 -------------------
 -- rollback Replica
 -------------------
---# reconfigure server replica with configuration='replication/cfg/replica.cfg'
+--# reconfigure server replica with configuration='replication/cfg/replica.cfg', rpl_master=default
 _select(31, 50)
 --# set connection default
 _insert(41, 60, 'master')
diff --git a/test/replication/hot_standby.result b/test/replication/hot_standby.result
index 3533a561d7661ae941c9c43149d660fb56ce8a22..7026d13d6dcc69aa9a835055a9fc273dac339929 100644
--- a/test/replication/hot_standby.result
+++ b/test/replication/hot_standby.result
@@ -1,4 +1,4 @@
---# create server hot_standby with configuration='replication/cfg/hot_standby.cfg', need_init=False
+--# create server hot_standby with configuration='replication/cfg/hot_standby.cfg' with hot_master=default
 --# create server replica with configuration='replication/cfg/replica.cfg'
 --# start server hot_standby
 --# start server replica
@@ -18,7 +18,7 @@ do
     function _insert(_begin, _end)
         local a = {}
         for i = _begin, _end do
-            table.insert(a, box.space[0]:insert{i, 'the tuple '..i})
+            table.insert(a, box.space['tweedledum']:insert{i, 'the tuple '..i})
         end
         return unpack(a)
     end
@@ -26,7 +26,7 @@ do
     function _select(_begin, _end)
         local a = {}
         for i = _begin, _end do
-            table.insert(a, box.space[0]:select{i})
+            table.insert(a, box.space['tweedledum']:select{i})
         end
         return unpack(a)
     end
@@ -43,10 +43,11 @@ end;
 --# setopt delimiter ''
 --# set connection default
 -- set begin lsn on master, replica and hot_standby.
+--# set variable replica_port to 'replica.primary_port'
 begin_lsn = box.info.lsn
 ---
 ...
-a = box.net.box.new('127.0.0.1', 33113)
+a = box.net.box.new('127.0.0.1', replica_port)
 ---
 ...
 a:call('_set_pri_lsn', box.info.lsn)
@@ -56,17 +57,7 @@ a:close()
 ---
 - true
 ...
-a = box.net.box.new('127.0.0.1', 33013)
----
-...
-a:call('_set_pri_lsn', box.info.lsn)
----
-...
-a:close()
----
-- true
-...
-space = box.schema.create_space('tweedledum', { id = 0 })
+space = box.schema.create_space('tweedledum')
 ---
 ...
 space:create_index('primary', { type = 'hash' })
@@ -119,6 +110,17 @@ _select(1, 10)
 box.fiber.sleep(0.2)
 ---
 ...
+--# set variable hot_standby_port to 'hot_standby.primary_port'
+a = box.net.box.new('127.0.0.1', hot_standby_port)
+---
+...
+a:call('_set_pri_lsn', box.info.lsn)
+---
+...
+a:close()
+---
+- true
+...
 --# set connection hot_standby
 _insert(11, 20)
 ---
@@ -169,6 +171,6 @@ _select(11, 20)
 --# cleanup server replica
 --# start server default
 --# set connection default
-box.space[0]:drop()
+box.space['tweedledum']:drop()
 ---
 ...
diff --git a/test/replication/hot_standby.test.lua b/test/replication/hot_standby.test.lua
index 604dbc0fe66682fd2829c2cb37a709a62dac84ba..9cdb50a83e04a3f1c308803cd1b8176d65faea70 100644
--- a/test/replication/hot_standby.test.lua
+++ b/test/replication/hot_standby.test.lua
@@ -1,4 +1,4 @@
---# create server hot_standby with configuration='replication/cfg/hot_standby.cfg', need_init=False
+--# create server hot_standby with configuration='replication/cfg/hot_standby.cfg' with hot_master=default
 --# create server replica with configuration='replication/cfg/replica.cfg'
 --# start server hot_standby
 --# start server replica
@@ -19,7 +19,7 @@ do
     function _insert(_begin, _end)
         local a = {}
         for i = _begin, _end do
-            table.insert(a, box.space[0]:insert{i, 'the tuple '..i})
+            table.insert(a, box.space['tweedledum']:insert{i, 'the tuple '..i})
         end
         return unpack(a)
     end
@@ -27,7 +27,7 @@ do
     function _select(_begin, _end)
         local a = {}
         for i = _begin, _end do
-            table.insert(a, box.space[0]:select{i})
+            table.insert(a, box.space['tweedledum']:select{i})
         end
         return unpack(a)
     end
@@ -43,18 +43,14 @@ end;
 --# set connection default
 
 -- set begin lsn on master, replica and hot_standby.
+--# set variable replica_port to 'replica.primary_port'
 begin_lsn = box.info.lsn
 
-a = box.net.box.new('127.0.0.1', 33113)
+a = box.net.box.new('127.0.0.1', replica_port)
 a:call('_set_pri_lsn', box.info.lsn)
 a:close()
 
-a = box.net.box.new('127.0.0.1', 33013)
-a:call('_set_pri_lsn', box.info.lsn)
-a:close()
-
-
-space = box.schema.create_space('tweedledum', { id = 0 })
+space = box.schema.create_space('tweedledum')
 space:create_index('primary', { type = 'hash' })
 
 _insert(1, 10)
@@ -67,8 +63,12 @@ _select(1, 10)
 --# stop server default
 box.fiber.sleep(0.2)
 
---# set connection hot_standby
+--# set variable hot_standby_port to 'hot_standby.primary_port'
+a = box.net.box.new('127.0.0.1', hot_standby_port)
+a:call('_set_pri_lsn', box.info.lsn)
+a:close()
 
+--# set connection hot_standby
 _insert(11, 20)
 _select(11, 20)
 
@@ -82,4 +82,4 @@ _select(11, 20)
 --# cleanup server replica
 --# start server default
 --# set connection default
-box.space[0]:drop()
+box.space['tweedledum']:drop()
diff --git a/test/replication/init_storage.test.py b/test/replication/init_storage.test.py
index 5468a189ead86df0fd43dff4967d532e00066ae1..b98bdd0acd7238ff037601600dde161bf1d023ff 100644
--- a/test/replication/init_storage.test.py
+++ b/test/replication/init_storage.test.py
@@ -3,6 +3,7 @@ import glob
 from lib.tarantool_server import TarantoolServer
 
 # master server
+cfgfile_bkp = server.cfgfile_source
 master = server
 
 master.admin("space = box.schema.create_space('test', {id =  42})")
@@ -17,11 +18,11 @@ print '-------------------------------------------------------------'
 print 'replica test 1 (no such space)'
 print '-------------------------------------------------------------'
 
-replica = TarantoolServer()
-replica.deploy("replication/cfg/replica.cfg",
-               replica.find_exe(self.args.builddir),
-               os.path.join(self.args.vardir, "replica"),
-               need_init=False)
+replica = TarantoolServer(server.ini)
+replica.cfgfile_source = 'replication/cfg/replica.cfg'
+replica.vardir = os.path.join(server.vardir, 'replica')
+replica.rpl_master = master
+replica.deploy()
 
 replica.admin('box.space.test')
 
@@ -36,11 +37,11 @@ print '-------------------------------------------------------------'
 print 'replica test 2 (must be ok)'
 print '-------------------------------------------------------------'
 
-replica = TarantoolServer()
-replica.deploy("replication/cfg/replica.cfg",
-               replica.find_exe(self.args.builddir),
-               os.path.join(self.args.vardir, "replica"),
-               need_init=False)
+replica = TarantoolServer(server.ini)
+replica.cfgfile_source = 'replication/cfg/replica.cfg'
+replica.vardir = os.path.join(server.vardir, 'replica')
+replica.rpl_master = master
+replica.deploy()
 
 replica.admin('space = box.space.test');
 replica.wait_lsn(lsn)
@@ -51,5 +52,6 @@ replica.stop()
 replica.cleanup(True)
 
 server.stop()
-server.deploy(self.suite_ini["config"])
+server.cfgfile_source = cfgfile_bkp
+server.deploy()
 
diff --git a/test/replication/suite.ini b/test/replication/suite.ini
index aff13264602430cdc69349428625209c330a90af..b1a8469e73f19023962309d6d6fc2b05b1fbe536 100644
--- a/test/replication/suite.ini
+++ b/test/replication/suite.ini
@@ -3,3 +3,4 @@ core = tarantool
 description = tarantool/box, replication
 disabled = 
 config = cfg/master.cfg
+random_ports = False
diff --git a/test/replication/swap.test.py b/test/replication/swap.test.py
index b08d542edd8704250d9224b1c1e33bb336d74a5b..7a33ed32bf41efd9fe2e0090c4d212ccd9ab4679 100644
--- a/test/replication/swap.test.py
+++ b/test/replication/swap.test.py
@@ -17,12 +17,13 @@ def select_tuples(_server, begin, end, lsn):
 
 # master server
 master = server
-
+cfgfile_bkp = server.cfgfile_source
 # replica server
 replica = TarantoolServer()
-replica.deploy("replication/cfg/replica.cfg",
-               replica.find_exe(self.args.builddir),
-               os.path.join(self.args.vardir, "replica"))
+replica.rpl_master = master
+replica.cfgfile_source = "replication/cfg/replica.cfg"
+replica.vardir = os.path.join(server.vardir, 'replica')
+replica.deploy()
 
 schema = {
     0 : {
@@ -60,8 +61,10 @@ for i in range(REPEAT):
 
     print "swap servers"
     # reconfigure replica to master
+    replica.rpl_master = None
     replica.reconfigure("replication/cfg/replica_to_master.cfg", silent = False)
     # reconfigure master to replica
+    master.rpl_master = replica
     master.reconfigure("replication/cfg/master_to_replica.cfg", silent = False)
 
     # insert to replica
@@ -78,8 +81,10 @@ for i in range(REPEAT):
 
     print "rollback servers configuration"
     # reconfigure replica to master
+    master.rpl_master = None
     master.reconfigure("replication/cfg/master.cfg", silent = False)
     # reconfigure master to replica
+    replica.rpl_master = master
     replica.reconfigure("replication/cfg/replica.cfg", silent = False)
 
 
@@ -87,4 +92,5 @@ for i in range(REPEAT):
 replica.stop()
 replica.cleanup(True)
 server.stop()
-server.deploy(self.suite_ini["config"])
+server.cfgfile_source = cfgfile_bkp
+server.deploy()
diff --git a/test/test-run.py b/test/test-run.py
index b2e1c9f825b272bf5002847a95fb57af700eae5c..a719470b9722dc7590a1ac862495977265778f0b 100755
--- a/test/test-run.py
+++ b/test/test-run.py
@@ -34,6 +34,8 @@ import argparse
 
 from lib.colorer import Colorer
 from lib.test_suite import TestSuite
+from lib.tarantool_server import TarantoolServer
+from lib.unittest_server import UnittestServer
 
 color_stdout = Colorer()
 #
@@ -68,7 +70,7 @@ class Options:
                 metavar = "suite",
                 nargs="*",
                 default = [],
-                help = """List of tests suites to look for tests in. Default: "" -
+                help = """List of test suites to look for tests in. Default: "" -
                 means find all available.""")
 
         parser.add_argument(
@@ -115,16 +117,6 @@ class Options:
                 default = "var",
                 help = """Path to data directory. Default: var.""")
 
-        parser.add_argument(
-                "--mem",
-                dest = "mem",
-                action = "store_true",
-                default = False,
-                help = """Run test suite in memory, using tmpfs or ramdisk.
-                Is used only if vardir is not an absolute path. In that case
-                vardir is sym-linked to /dev/shm/<vardir>.
-                Linux only. Default: false.""")
-
         self.args = parser.parse_args()
         self.check()
 
@@ -183,6 +175,9 @@ def main():
 
         suites = [TestSuite(suite_name, options.args) for suite_name in sorted(suite_names)]
 
+        TarantoolServer.find_exe(options.args.builddir)
+        UnittestServer.find_exe(options.args.builddir)
+
         for suite in suites:
             failed_tests.extend(suite.run_all())
     except RuntimeError as e:
@@ -201,4 +196,4 @@ def main():
     return (-1 if failed_tests else 0)
 
 if __name__ == "__main__":
-  exit(main())
+    exit(main())