Lines 1-266
Link Here
|
1 |
--- test/lib/tarantool_silverbox_server.py.orig 2011-05-14 12:16:32.000000000 +0000 |
|
|
2 |
+++ test/lib/tarantool_silverbox_server.py 2011-12-13 00:23:04.673107891 +0000 |
3 |
@@ -1,35 +1,234 @@ |
4 |
+import os |
5 |
+import stat |
6 |
import shutil |
7 |
import subprocess |
8 |
-import yaml |
9 |
-import ConfigParser |
10 |
-from tarantool_server import TarantoolServer, TarantoolConfigFile |
11 |
-from tarantool_admin import TarantoolAdmin |
12 |
-from silverbox import Silverbox |
13 |
- |
14 |
-class TarantoolSilverboxServer(TarantoolServer): |
15 |
- def __new__(cls, core="tarantool", module="silverbox"): |
16 |
- return TarantoolServer.__new__(cls) |
17 |
- |
18 |
- def __init__(self, core="tarantool", module="silverbox"): |
19 |
- TarantoolServer.__init__(self, core, module) |
20 |
- |
21 |
- def configure(self, config): |
22 |
- TarantoolServer.configure(self, config) |
23 |
- with open(self.config) as fp: |
24 |
- dummy_section_name = "tarantool" |
25 |
- config = ConfigParser.ConfigParser() |
26 |
- config.readfp(TarantoolConfigFile(fp, dummy_section_name)) |
27 |
- self.primary_port = int(config.get(dummy_section_name, "primary_port")) |
28 |
- self.admin_port = int(config.get(dummy_section_name, "admin_port")) |
29 |
- self.port = self.admin_port |
30 |
- self.admin = TarantoolAdmin("localhost", self.admin_port) |
31 |
- self.sql = Silverbox("localhost", self.primary_port) |
32 |
- |
33 |
- def init(self): |
34 |
-# init storage |
35 |
- subprocess.check_call([self.binary, "--init_storage"], |
36 |
- cwd = self.vardir, |
37 |
+import pexpect |
38 |
+import sys |
39 |
+import signal |
40 |
+import time |
41 |
+import socket |
42 |
+import daemon |
43 |
+import glob |
44 |
+ |
45 |
+def wait_until_connected(host, port): |
46 |
+ """Wait until the server is started and accepting connections""" |
47 |
+ is_connected = False |
48 |
+ while not is_connected: |
49 |
+ try: |
50 |
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
51 |
+ sock.connect((host, port)) |
52 |
+ is_connected = True |
53 |
+ sock.close() |
54 |
+ except socket.error as e: |
55 |
+ time.sleep(0.001) |
56 |
+ |
57 |
+ |
58 |
+def prepare_gdb(args): |
59 |
+ """Prepare server startup arguments to run under gdb.""" |
60 |
+ if "TERM" in os.environ: |
61 |
+ term = os.environ["TERM"] |
62 |
+ else: |
63 |
+ term = "xterm" |
64 |
+ |
65 |
+ if term not in ["xterm", "rxvt", "urxvt", "gnome-terminal", "konsole"]: |
66 |
+ raise RuntimeError("--gdb: unsupported terminal {0}".format(term)) |
67 |
+ |
68 |
+ args = [ term, "-e ", "gdb", "-ex", "break main", "-ex", "run"] + args |
69 |
+ return args |
70 |
+ |
71 |
+ |
72 |
+def prepare_valgrind(args, valgrind_opts): |
73 |
+ "Prepare server startup arguments to run under valgrind." |
74 |
+ args = ([ "valgrind", "--log-file=valgrind.log", "--quiet" ] + |
75 |
+ valgrind_opts.split(" ") + args) |
76 |
+ return args |
77 |
+ |
78 |
+ |
79 |
+def check_tmpfs_exists(): |
80 |
+ return os.uname()[0] in 'Linux' and os.path.isdir("/dev/shm") |
81 |
+ |
82 |
+def create_tmpfs_vardir(vardir): |
83 |
+ os.mkdir(os.path.join("/dev/shm", vardir)) |
84 |
+ os.symlink(os.path.join("/dev/shm", vardir), vardir) |
85 |
+ |
86 |
+class TarantoolSilverboxServer: |
87 |
+ """Server represents a single server instance. Normally, the |
88 |
+ program operates with only one server, but in future we may add |
89 |
+ replication slaves. The server is started once at the beginning |
90 |
+ of each suite, and stopped at the end.""" |
91 |
+ |
92 |
+ def __init__(self, args, suite_ini): |
93 |
+ """Set server options: path to configuration file, pid file, exe, etc.""" |
94 |
+ self.args = args |
95 |
+ self.suite_ini = suite_ini |
96 |
+ self.path_to_pidfile = os.path.join(args.vardir, suite_ini["pidfile"]) |
97 |
+ self.path_to_exe = None |
98 |
+ self.abspath_to_exe = None |
99 |
+ self.is_started = False |
100 |
+ |
101 |
+ def install(self, silent = False): |
102 |
+ """Start server instance: check if the old one exists, kill it |
103 |
+ if necessary, create necessary directories and files, start |
104 |
+ the server. The server working directory is taken from 'vardir', |
105 |
+ specified in the prgoram options. |
106 |
+ Currently this is implemented for tarantool_silverbox only.""" |
107 |
+ |
108 |
+ vardir = self.args.vardir |
109 |
+ |
110 |
+ if not silent: |
111 |
+ print "Installing the server..." |
112 |
+ |
113 |
+ if self.path_to_exe == None: |
114 |
+ self.path_to_exe = self.find_exe() |
115 |
+ self.abspath_to_exe = os.path.abspath(self.path_to_exe) |
116 |
+ |
117 |
+ if not silent: |
118 |
+ print " Found executable at " + self.path_to_exe + "." |
119 |
+ |
120 |
+ if not silent: |
121 |
+ print " Creating and populating working directory in " +\ |
122 |
+ vardir + "..." |
123 |
+ |
124 |
+ if os.access(vardir, os.F_OK): |
125 |
+ if not silent: |
126 |
+ print " Found old vardir, deleting..." |
127 |
+ self.kill_old_server() |
128 |
+ for filename in (glob.glob(os.path.join(vardir, "*.snap")) + |
129 |
+ glob.glob(os.path.join(vardir, "*.inprogress")) + |
130 |
+ glob.glob(os.path.join(vardir, "*.xlog")) + |
131 |
+ glob.glob(os.path.join(vardir, "*.cfg")) + |
132 |
+ glob.glob(os.path.join(vardir, "*.log")) + |
133 |
+ glob.glob(os.path.join(vardir, "*.core.*")) + |
134 |
+ glob.glob(os.path.join(vardir, "core"))): |
135 |
+ os.remove(filename) |
136 |
+ else: |
137 |
+ if (self.args.mem == True and check_tmpfs_exists() and |
138 |
+ os.path.basename(vardir) == vardir): |
139 |
+ create_tmpfs_vardir(vardir) |
140 |
+ else: |
141 |
+ os.mkdir(vardir) |
142 |
+ |
143 |
+ shutil.copy(self.suite_ini["config"], self.args.vardir) |
144 |
+ |
145 |
+ subprocess.check_call([self.abspath_to_exe, "--init_storage"], |
146 |
+ cwd = self.args.vardir, |
147 |
# catch stdout/stderr to not clutter output |
148 |
stdout = subprocess.PIPE, |
149 |
stderr = subprocess.PIPE) |
150 |
|
151 |
+ p = subprocess.Popen([self.abspath_to_exe, "--version"], |
152 |
+ cwd = self.args.vardir, |
153 |
+ stdout = subprocess.PIPE) |
154 |
+ version = p.stdout.read().rstrip() |
155 |
+ p.wait() |
156 |
+ |
157 |
+ if not silent: |
158 |
+ print "Starting {0} {1}.".format(os.path.basename(self.abspath_to_exe), |
159 |
+ version) |
160 |
+ |
161 |
+ def start(self, silent = False): |
162 |
+ |
163 |
+ if self.is_started: |
164 |
+ if not silent: |
165 |
+ print "The server is already started." |
166 |
+ return |
167 |
+ |
168 |
+ if not silent: |
169 |
+ print "Starting the server..." |
170 |
+ |
171 |
+ args = [self.abspath_to_exe] |
172 |
+ |
173 |
+ if (self.args.start_and_exit and |
174 |
+ not self.args.valgrind and not self.args.gdb): |
175 |
+ args.append("--daemonize") |
176 |
+ if self.args.gdb: |
177 |
+ args = prepare_gdb(args) |
178 |
+ elif self.args.valgrind: |
179 |
+ args = prepare_valgrind(args, self.args.valgrind_opts) |
180 |
+ |
181 |
+ if self.args.start_and_exit and self.args.valgrind: |
182 |
+ pid = os.fork() |
183 |
+ if pid > 0: |
184 |
+ os.wait() |
185 |
+ else: |
186 |
+ with daemon.DaemonContext(working_directory = self.args.vardir): |
187 |
+ os.execvp(args[0], args) |
188 |
+ else: |
189 |
+ self.server = pexpect.spawn(args[0], args[1:], cwd = self.args.vardir) |
190 |
+ if self.args.start_and_exit: |
191 |
+ self.server.wait() |
192 |
+ |
193 |
+# wait until the server is connectedk |
194 |
+ if self.args.gdb and self.args.start_and_exit: |
195 |
+ time.sleep(1) |
196 |
+ elif not self.args.start_and_exit and not self.args.gdb: |
197 |
+ self.server.expect_exact("entering event loop") |
198 |
+ else: |
199 |
+ wait_until_connected(self.suite_ini["host"], self.suite_ini["port"]) |
200 |
+ |
201 |
+# Set is_started flag, to nicely support cleanup during an exception. |
202 |
+ self.is_started = True |
203 |
+ |
204 |
+ |
205 |
+ def stop(self, silent = False): |
206 |
+ """Stop server instance. Do nothing if the server is not started, |
207 |
+ to properly shut down the server in case of an exception during |
208 |
+ start up.""" |
209 |
+ if self.is_started: |
210 |
+ if not silent: |
211 |
+ print "Stopping the server..." |
212 |
+ if self.args.gdb: |
213 |
+ self.kill_old_server(True) |
214 |
+ self.server.terminate() |
215 |
+ self.server.expect(pexpect.EOF) |
216 |
+ self.is_started = False |
217 |
+ elif not silent: |
218 |
+ print "The server is not started." |
219 |
+ |
220 |
+ def restart(self): |
221 |
+ self.stop(True) |
222 |
+ self.start(True) |
223 |
+ |
224 |
+ def test_option(self, option_list_str): |
225 |
+ args = [self.abspath_to_exe] + option_list_str.split() |
226 |
+ print " ".join([os.path.basename(self.abspath_to_exe)] + args[1:]) |
227 |
+ output = subprocess.Popen(args, |
228 |
+ cwd = self.args.vardir, |
229 |
+ stdout = subprocess.PIPE, |
230 |
+ stderr = subprocess.STDOUT).stdout.read() |
231 |
+ print output |
232 |
+ |
233 |
+ |
234 |
+ def find_exe(self): |
235 |
+ """Locate server executable in the bindir. We just take |
236 |
+ the first thing looking like an exe in there.""" |
237 |
+ |
238 |
+ print " Looking for server binary in {0}...".format(self.args.bindir) |
239 |
+ if (os.access(self.args.bindir, os.F_OK) == False or |
240 |
+ stat.S_ISDIR(os.stat(self.args.bindir).st_mode) == False): |
241 |
+ raise RuntimeError("Directory " + self.args.bindir + " doesn't exist") |
242 |
+ |
243 |
+ for f in os.listdir(self.args.bindir): |
244 |
+ f = os.path.join(self.args.bindir, f) |
245 |
+ st_mode = os.stat(f).st_mode |
246 |
+ if stat.S_ISREG(st_mode) and st_mode & stat.S_IXUSR: |
247 |
+ return f |
248 |
+ |
249 |
+ raise RuntimeError("Can't find server executable in " + self.args.bindir) |
250 |
+ |
251 |
+ def kill_old_server(self, silent = False): |
252 |
+ """Kill old server instance if it exists.""" |
253 |
+ if os.access(self.path_to_pidfile, os.F_OK) == False: |
254 |
+ return # Nothing to do |
255 |
+ pid = 0 |
256 |
+ with open(self.path_to_pidfile) as f: |
257 |
+ pid = int(f.read()) |
258 |
+ if not silent: |
259 |
+ print " Found old server, pid {0}, killing...".format(pid) |
260 |
+ try: |
261 |
+ os.kill(pid, signal.SIGTERM) |
262 |
+ while os.kill(pid, 0) != -1: |
263 |
+ time.sleep(0.001) |
264 |
+ except OSError: |
265 |
+ pass |
266 |
+ |