Got rid of most using directives. Ported tests to python.
[invirt/third/libt4.git] / rsm_tester.py
1 #!/usr/bin/env python
2
3 import subprocess as sp
4 import signal
5 import os
6 import sys
7 import time
8 import getopt
9 import random
10
11 pid = []
12 logs = []
13 views = [] # expected views
14 in_views = {} # the number of views a node is expected to be present
15 p = []
16 t = None
17 always_kill = 0
18 quit = False
19
20 def killprocess(num, frame):
21     print "killprocess: forcestop all spawned processes...%s" % (str(pid),)
22     global quit
23     quit = True
24     for p in pid:
25         os.kill(p, signal.SIGKILL)
26
27 for sig in ['HUP', 'INT', 'ABRT', 'QUIT', 'TERM']:
28     num = getattr(signal, 'SIG'+sig)
29     signal.signal(num, killprocess)
30
31 def paxos_log(port):
32   return "paxos-%d.log" % port
33
34 def die(*s):
35     print >>sys.stderr, ''.join(s)
36     exit(1)
37
38 def mydie(*s):
39     if always_kill:
40         killprocess()
41     die(*s)
42
43 def usleep(us):
44     time.sleep(us/1e6)
45
46 def cleanup():
47     for p in pid:
48         os.kill(p, signal.SIGKILL)
49     for l in logs:
50         try:
51             os.unlink(l)
52         except OSError:
53             pass
54     usleep(200000)
55
56 def spawn(p, *a):
57     sa = map(str, a)
58     aa = '-'.join(sa)
59     try:
60         pid = os.fork()
61     except OSError, e:
62         mydie("Cannot fork: %s" % (repr(e),))
63     if pid:
64         # parent
65         logs.append("%s-%s.log" % (p, aa))
66         if 'lock_server' in p:
67             logs.append(paxos_log(a[1]))
68         return pid
69     else:
70         # child
71         os.close(1)
72         sys.stdout = open("%s-%s.log" % (p, aa), 'w')
73         os.close(2)
74         os.dup(1)
75         sys.stderr = sys.stdout
76         print "%s %s" % (p, ' '.join(sa))
77         try:
78             os.execv(p, [p] + sa)
79         except OSError, e:
80             mydie("Cannot start new %s %s %s", (p, repr(sa), repr(e)))
81
82 def randports(num):
83     return sorted([random.randint(0, 54000/2)*2+10000 for i in xrange(num)])
84
85 def print_config(ports):
86     try:
87         config = open("config", 'w')
88     except IOError:
89         mydie("Couldn't open config for writing")
90     for p in ports:
91         print >>config, "%05d" % (p,)
92     config.close()
93
94 def spawn_ls(master, port):
95     return spawn("./lock_server", master, port)
96
97 def check_views(l, vs, last_v=None):
98     try:
99         f = open(l, 'r')
100         log = f.readlines()
101         f.close()
102     except IOError:
103         mydie("Failed: couldn't read %s" % (l,))
104     i = 0
105     last_view = None
106     for line in log:
107         if not line.startswith('done'):
108             continue
109         words = line.split(' ')
110         num = int(words[1])
111         view = map(int, words[2:])
112         last_view = view
113         if i >= len(vs):
114             # let there be extra views
115             continue
116         expected = vs[i]
117         if tuple(expected) != tuple(view):
118             mydie("Failed: In log %s at view %s is (%s), but expected %s (%s)" %
119                   (l, str(num), repr(view), str(i), repr(expected)))
120         i+=1
121     if i < len(vs):
122         mydie("Failed: In log %s, not enough views seen!" % (l,))
123     if last_v is not None and tuple(last_v) != tuple(last_view):
124         mydie("Failed: In log %s last view didn't match, got view %s, but expected %s" %
125               (l, repr(last_view), repr(last_v)))
126
127 def get_num_views(log, including):
128     try:
129         f = open(log, 'r')
130     except IOError:
131         return 0
132     log = f.readlines()
133     f.close()
134     return len([x for x in log if 'done ' in x and str(including) in x])
135
136 def wait_for_view_change(log, num_views, including, timeout):
137     start = time.time()
138     while get_num_views(log, including) < num_views and (start + timeout > time.time()) and not quit:
139         try:
140             f = open(log, 'r')
141             loglines = f.readlines()
142             f.close()
143             lastv = [x for x in loglines if 'done' in x][-1].strip()
144             print "   Waiting for %s to be present in >=%s views in %s (Last view: %s)" % \
145                   (including, str(num_views), log, lastv)
146             usleep(100000)
147         except IOError:
148             continue
149
150     if get_num_views(log, including) < num_views:
151         mydie("Failed: Timed out waiting for %s to be in >=%s in log %s" %
152               (including, str(num_views), log))
153     else:
154         print "   Done: %s is in >=%s views in %s" % (including, str(num_views), log)
155
156 def waitpid_to(pid, to):
157     start = time.time()
158     done_pid = -1
159     while done_pid <= 0 and (time.time() - start) < to:
160       usleep(100000)
161       done_pid = os.waitpid(pid, os.WNOHANG)
162
163     if done_pid <= 0:
164         os.kill(pid, signal.SIGKILL)
165         mydie("Failed: Timed out waiting for process %s" % (str(pid),))
166     else:
167         return 1
168
169 def wait_and_check_expected_view(v):
170     views.append(v)
171     for vv in v:
172         in_views[vv] += 1
173     for port in v:
174         wait_for_view_change(paxos_log(port), in_views[port], port, 20)
175     for port in v:
176         log = paxos_log(port)
177         check_views(log, views)
178
179 def start_nodes(n, command):
180     global pid, logs, views
181     pid = []
182     logs = []
183     views = []
184     for pp in p:
185         in_views[pp] = 0
186
187     for i in xrange(n):
188         if command == "ls":
189             pid.append(spawn_ls(p[0],p[i]))
190             print "Start lock_server on %s" % (str(p[i]),)
191         usleep(100000)
192
193         wait_and_check_expected_view(p[:i+1])
194
195 options, arguments = getopt.getopt(sys.argv[1:], "s:k")
196 options = dict(options)
197
198 if 's' in options:
199     random.seed(options[s])
200
201 if 'k' in options:
202     always_kill = 1
203
204 # get a sorted list of random ports
205 p = randports(5)
206 print_config(p)
207
208 NUM_TESTS = 17
209 do_run = [0] * NUM_TESTS
210
211 # see which tests are set
212 if len(arguments):
213     for t in arguments:
214         t = int(t)
215         if t < NUM_TESTS and t >= 0:
216             do_run[t] = 1
217 else:
218     # turn on all tests
219     for i in xrange(NUM_TESTS):
220         do_run[i] = 1
221
222 if do_run[0]:
223     print "test0: start 3-process lock server"
224     start_nodes(3,"ls")
225     cleanup()
226     usleep(200000)
227
228 if do_run[1]:
229     print "test1: start 3-process lock server, kill third server"
230     start_nodes(3,"ls")
231     print "Kill third server (PID: %s) on port %s" % (str(pid[2]), str(p[2]))
232     os.kill(pid[2], signal.SIGTERM)
233     usleep(500000)
234     # it should go through 4 views
235     v4 = [p[0], p[1]]
236     wait_and_check_expected_view(v4)
237     cleanup()
238     usleep(200000)
239
240 if do_run[2]:
241     print "test2: start 3-process lock server, kill first server"
242     start_nodes(3,"ls")
243     print "Kill first (PID: $pid[0]) on port $p[0]"
244     os.kill(pid[0], signal.SIGTERM)
245     usleep(500000)
246     # it should go through 4 views
247     v4 = [p[1], p[2]]
248     wait_and_check_expected_view(v4)
249     cleanup()
250     usleep(200000)
251
252 if do_run[3]:
253     print "test3: start 3-process lock_server, kill a server, restart a server"
254     start_nodes(3,"ls")
255     print "Kill server (PID: $pid[2]) on port $p[2]"
256     os.kill(pid[2], signal.SIGTERM)
257     usleep(500000)
258     v4 = (p[0], p[1])
259     wait_and_check_expected_view(v4)
260     print "Restart killed server on port $p[2]"
261     pid[2] = spawn_ls (p[0], p[2])
262     usleep(500000)
263     v5 = (p[0], p[1], p[2])
264     wait_and_check_expected_view(v5)
265     cleanup()
266     usleep(200000)
267
268 if do_run[4]:
269     print "test4: 3-process lock_server, kill third server, kill second server, restart third server, kill third server again, restart second server, re-restart third server, check logs"
270     start_nodes(3,"ls")
271     print "Kill server (PID: $pid[2]) on port $p[2]"
272     os.kill(pid[2], signal.SIGTERM)
273     usleep(500000)
274     v4 = (p[0], p[1])
275     wait_and_check_expected_view(v4)
276     print "Kill server (PID: $pid[1]) on port $p[1]"
277     os.kill(pid[1], signal.SIGTERM)
278     usleep(500000)
279     #no view change can happen because of a lack of majority
280     print "Restarting server on port $p[2]"
281     pid[2] = spawn_ls(p[0], p[2])
282     usleep(500000)
283     #no view change can happen because of a lack of majority
284     for port in p[0:1+2]:
285         num_v = get_num_views(paxos_log(port), port)
286         if num_v != in_views[port]:
287             die("$num_v views in ", paxos_log(port), " : no new views should be formed due to the lack of majority")
288     # kill node 3 again,
289     print "Kill server (PID: $pid[2]) on port $p[2]"
290     os.kill(pid[2], signal.SIGTERM)
291     usleep(500000)
292     print "Restarting server on port $p[1]"
293     pid[1] = spawn_ls(p[0], p[1])
294     usleep(700000)
295     for port in p[0:1+1]:
296         in_views[port] = get_num_views(paxos_log(port), port)
297         print "   Node $port is present in ", in_views[port], " views in ", paxos_log(port), ""
298     print "Restarting server on port $p[2]"
299     pid[2] = spawn_ls(p[0], p[2])
300     lastv = (p[0],p[1],p[2])
301     for port in lastv:
302         wait_for_view_change(paxos_log(port), in_views[port]+1, port, 20)
303     # now check the paxos logs and make sure the logs go through the right
304     # views
305     for port in lastv:
306         check_views(paxos_log(port), views, lastv)
307     cleanup()
308
309 if do_run[5]:
310     print "test5: 3-process lock_server, send signal 1 to first server, kill third server, restart third server, check logs"
311     start_nodes(3,"ls")
312     print "Sending paxos breakpoint 1 to first server on port $p[0]"
313     spawn("./rsm_tester", p[0]+1, "breakpoint", 3)
314     usleep(100000)
315     print "Kill third server (PID: $pid[2]) on port $p[2]"
316     os.kill(pid[2], signal.SIGTERM)
317     usleep(500000)
318     for port in p[0:1+2]:
319         num_v = get_num_views(paxos_log(port), port)
320         if num_v != in_views[port]:
321             die("$num_v views in ", paxos_log(port), " : no new views should be formed due to the lack of majority")
322     print "Restarting third server on port $p[2]"
323     pid[2]= spawn_ls(p[0], p[2])
324     lastv = (p[1],p[2])
325     for port in lastv:
326         wait_for_view_change(paxos_log(port), in_views[port]+1, port, 20)
327     usleep(1000000)
328     # now check the paxos logs and make sure the logs go through the right
329     # views
330     for port in lastv:
331         check_views(paxos_log(port), views, lastv)
332     cleanup()
333
334 if do_run[6]:
335     print "test6: 4-process lock_server, send signal 2 to first server, kill fourth server, restart fourth server, check logs"
336     start_nodes(4,"ls")
337     print "Sending paxos breakpoint 2 to first server on port $p[0]"
338     spawn("./rsm_tester", p[0]+1, "breakpoint", 4)
339     usleep(100000)
340     print "Kill fourth server (PID: $pid[3]) on port $p[3]"
341     os.kill(pid[3], signal.SIGTERM)
342     usleep(500000)
343     for port in (p[1],p[2]):
344         num_v = get_num_views(paxos_log(port), port)
345         if num_v != in_views[port]:
346             die("$num_v views in ", paxos_log(port), " : no new views should be formed due to the lack of majority")
347     usleep(500000)
348     print "Restarting fourth server on port $p[3]"
349     pid[3] = spawn_ls(p[1], p[3])
350     usleep(500000)
351     v5 = (p[0],p[1],p[2])
352     for port in v5:
353         in_views[port]+=1
354     views.append(v5)
355     usleep(1000000)
356     # the 6th view will be (2,3)  or (1,2,3,4)
357     v6 = (p[1],p[2])
358     for port in v6:
359         in_views[port]+=1
360     for port in v6:
361         wait_for_view_change(paxos_log(port), in_views[port]+1, port, 30)
362     # final will be (2,3,4)
363     lastv = (p[1],p[2],p[3])
364     for port in lastv:
365         wait_for_view_change(paxos_log(port), in_views[port]+1, port, 20)
366     for port in lastv:
367         check_views(paxos_log(port), views, lastv)
368     cleanup()
369
370 if do_run[7]:
371     print "test7: 4-process lock_server, send signal 2 to first server, kill fourth server, kill other servers, restart other servers, restart fourth server, check logs"
372     start_nodes(4,"ls")
373     print "Sending paxos breakpoint 2 to first server on port $p[0]"
374     spawn("./rsm_tester", p[0]+1, "breakpoint", 4)
375     usleep(300000)
376     print "Kill fourth server (PID: $pid[3]) on port $p[3]"
377     os.kill(pid[3], signal.SIGTERM)
378     usleep(500000)
379     print "Kill third server (PID: $pid[2]) on port $p[2]"
380     os.kill(pid[2], signal.SIGTERM)
381     print "Kill second server (PID: $pid[1]) on port $p[1]"
382     os.kill(pid[1], signal.SIGTERM)
383     usleep(500000)
384     print "Restarting second server on port $p[1]"
385     pid[1] = spawn_ls(p[0], p[1])
386     usleep(500000)
387     print "Restarting third server on port $p[2]"
388     pid[2] = spawn_ls(p[0], p[2])
389     usleep(500000)
390     #no view change is possible by now because there is no majority
391     for port in (p[1],p[2]):
392         num_v = get_num_views(paxos_log(port), port)
393         if num_v != in_views[port]:
394             die("$num_v views in ", paxos_log(port), " : no new views should be formed due to the lack of majority")
395     print "Restarting fourth server on port $p[3]"
396     pid[3] = spawn_ls(p[1], p[3])
397     usleep(500000)
398     v5 = (p[0], p[1], p[2])
399     views.append(v5)
400     for port in v5:
401         in_views[port]+=1
402     usleep(1500000)
403     lastv = (p[1],p[2],p[3])
404     for port in lastv:
405         wait_for_view_change(paxos_log(port), in_views[port]+1, port, 20)
406     for port in lastv:
407         check_views(paxos_log(port), views, lastv)
408     cleanup()
409
410 if do_run[8]:
411     print "test8: start 3-process lock service"
412     start_nodes(3,"ls")
413     print "Start lock_tester $p[0]"
414     t = spawn("./lock_tester", p[0])
415     print "   Wait for lock_tester to finish (waitpid $t)"
416     waitpid_to(t, 600)
417     if os.system("grep \"passed all tests successfully\" lock_tester-$p[0].log"):
418         mydie("Failed lock tester for test 8")
419     cleanup()
420     usleep(200000)
421
422 if do_run[9]:
423     print "test9: start 3-process rsm, kill second slave while lock_tester is running"
424     start_nodes(3,"ls")
425     print "Start lock_tester $p[0]"
426     t = spawn("./lock_tester", p[0])
427     usleep(random.randint(1,1000000))
428     print "Kill slave (PID: $pid[2]) on port $p[2]"
429     os.kill(pid[2], signal.SIGTERM)
430     usleep(300000)
431     # it should go through 4 views
432     v4 = (p[0], p[1])
433     wait_and_check_expected_view(v4)
434     print "   Wait for lock_tester to finish (waitpid $t)"
435     waitpid_to(t, 600)
436     if os.system("grep \"passed all tests successfully\" lock_tester-$p[0].log"):
437         mydie("Failed lock tester for test 9")
438     cleanup()
439     usleep(200000)
440
441 if do_run[10]:
442     print "test10: start 3-process rsm, kill second slave and restarts it later while lock_tester is running"
443     start_nodes(3,"ls")
444     print "Start lock_tester $p[0]"
445     t = spawn("./lock_tester", p[0])
446     usleep(random.randint(1,1000000))
447     print "Kill slave (PID: $pid[2]) on port $p[2]"
448     os.kill(pid[2], signal.SIGTERM)
449     usleep(300000)
450     # it should go through 4 views
451     v4 = (p[0], p[1])
452     wait_and_check_expected_view(v4)
453     usleep(300000)
454     print "Restarting killed lock_server on port $p[2]"
455     pid[2] = spawn_ls(p[0], p[2])
456     v5 = (p[0],p[1],p[2])
457     wait_and_check_expected_view(v5)
458     print "   Wait for lock_tester to finish (waitpid $t)"
459     waitpid_to(t, 600)
460     if os.system("grep \"passed all tests successfully\" lock_tester-$p[0].log"):
461         mydie("Failed lock tester for test 10")
462     cleanup()
463     usleep(200000)
464
465 if do_run[11]:
466     print "test11: start 3-process rsm, kill primary while lock_tester is running"
467     start_nodes(3,"ls")
468     print "Start lock_tester $p[0]"
469     t = spawn("./lock_tester", p[0])
470     usleep(random.randint(1,1000000))
471     print "Kill primary (PID: $pid[0]) on port $p[0]"
472     os.kill(pid[0], signal.SIGTERM)
473     usleep(300000)
474     # it should go through 4 views
475     v4 = (p[1], p[2])
476     wait_and_check_expected_view(v4)
477     print "   Wait for lock_tester to finish (waitpid $t)"
478     waitpid_to(t, 600)
479     if os.system("grep \"passed all tests successfully\" lock_tester-$p[0].log"):
480         mydie("Failed lock tester for test 11")
481     cleanup()
482     usleep(200000)
483
484 if do_run[12]:
485     print "test12: start 3-process rsm, kill master at break1 and restart it while lock_tester is running"
486     start_nodes(3, "ls")
487     print "Start lock_tester $p[0]"
488     t = spawn("./lock_tester", p[0])
489     usleep(100000)
490     print "Kill master (PID: $pid[0]) on port $p[0] at breakpoint 1"
491     spawn("./rsm_tester", p[0]+1, "breakpoint", 1)
492     usleep(100000)
493     # it should go through 5 views
494     v4 = (p[1], p[2])
495     wait_and_check_expected_view(v4)
496     print "Restarting killed lock_server on port $p[0]"
497     pid[0] = spawn_ls(p[1], p[0])
498     usleep(300000)
499     # the last view should include all nodes
500     lastv = (p[0],p[1],p[2])
501     for port in lastv:
502         wait_for_view_change(paxos_log(port), in_views[port]+1, port, 20)
503     for port in lastv:
504         check_views(paxos_log(port), views, lastv)
505     print "   Wait for lock_tester to finish (waitpid $t)"
506     waitpid_to(t, 600)
507     if os.system("grep \"passed all tests successfully\" lock_tester-$p[0].log"):
508         mydie("Failed lock tester for test 12")
509     cleanup()
510     usleep(200000)
511
512 if do_run[13]:
513     print "test13: start 3-process rsm, kill slave at break1 and restart it while lock_tester is running"
514     start_nodes(3, "ls")
515     print "Start lock_tester $p[0]"
516     t = spawn("./lock_tester", p[0])
517     usleep(100000)
518     print "Kill slave (PID: $pid[2]) on port $p[2] at breakpoint 1"
519     spawn("./rsm_tester", p[2]+1, "breakpoint", 1)
520     usleep(100000)
521     # it should go through 4 views
522     v4 = (p[0], p[1])
523     wait_and_check_expected_view(v4)
524     print "Restarting killed lock_server on port $p[2]"
525     pid[2] = spawn_ls(p[0], p[2])
526     usleep(300000)
527     # the last view should include all nodes
528     lastv = (p[0],p[1],p[2])
529     for port in lastv:
530         wait_for_view_change(paxos_log(port), in_views[port]+1, port, 20)
531     for port in lastv:
532         check_views(paxos_log(port), views, lastv)
533     print "   Wait for lock_tester to finish (waitpid $t)"
534     waitpid_to(t, 600)
535     if os.system("grep \"passed all tests successfully\" lock_tester-$p[0].log"):
536         mydie("Failed lock tester for test 13")
537     cleanup()
538     usleep(200000)
539
540 if do_run[14]:
541     print "test14: start 5-process rsm, kill slave break1, kill slave break2"
542     start_nodes(5, "ls")
543     print "Start lock_tester $p[0]"
544     t = spawn("./lock_tester", p[0])
545     usleep(100000)
546     print "Kill slave (PID: $pid[4]) on port $p[4] at breakpoint 1"
547     spawn("./rsm_tester", p[4]+1, "breakpoint", 1)
548     print "Kill slave (PID: $pid[3]) on port $p[3] at breakpoint 2"
549     spawn("./rsm_tester", p[3]+1, "breakpoint", 2)
550     usleep(100000)
551     # two view changes:
552     print "first view change wait"
553     lastv = (p[0],p[1],p[2],p[3])
554     for port in lastv:
555         wait_for_view_change(paxos_log(port), in_views[port]+1, port, 20)
556     print "second view change wait"
557     lastv = (p[0],p[1],p[2])
558     for port in lastv:
559         wait_for_view_change(paxos_log(port), in_views[port]+1, port, 20)
560     print "   Wait for lock_tester to finish (waitpid $t)"
561     waitpid_to(t, 600)
562     if os.system("grep \"passed all tests successfully\" lock_tester-$p[0].log"):
563         mydie("Failed lock tester for test 14")
564     cleanup()
565     usleep(200000)
566
567 if do_run[15]:
568     print "test15: start 5-process rsm, kill slave break1, kill primary break2"
569     start_nodes(5, "ls")
570     print "Start lock_tester $p[0]"
571     t = spawn("./lock_tester", p[0])
572     usleep(100000)
573     print "Kill slave (PID: $pid[4]) on port $p[4] at breakpoint 1"
574     spawn("./rsm_tester", p[4]+1, "breakpoint", 1)
575     print "Kill primary (PID: $pid[0]) on port $p[0] at breakpoint 2"
576     spawn("./rsm_tester", p[0]+1, "breakpoint", 2)
577     usleep(100000)
578     # two view changes:
579     print "first view change wait"
580     lastv = (p[0],p[1],p[2],p[3])
581     for port in lastv:
582         wait_for_view_change(paxos_log(port), in_views[port]+1, port, 20)
583     print "second view change wait"
584     lastv = (p[1],p[2],p[3])
585     for port in lastv:
586         wait_for_view_change(paxos_log(port), in_views[port]+1, port, 20)
587     print "   Wait for lock_tester to finish (waitpid $t)"
588     waitpid_to(t, 600)
589     if os.system("grep \"passed all tests successfully\" lock_tester-$p[0].log"):
590         mydie("Failed lock tester for test 15")
591     cleanup()
592     usleep(200000)
593
594 if do_run[16]:
595     print "test16: start 3-process rsm, partition primary, heal it"
596     start_nodes(3, "ls")
597     print "Start lock_tester $p[0]"
598     t = spawn("./lock_tester", p[0])
599     usleep(100000)
600     print "Partition primary (PID: $pid[0]) on port $p[0] at breakpoint"
601     spawn("./rsm_tester", p[0]+1, "partition", 0)
602     usleep(300000)
603     print "first view change wait"
604     lastv = (p[1],p[2])
605     for port in lastv:
606         wait_for_view_change(paxos_log(port), in_views[port]+1, port, 20)
607     usleep(100000)
608     print "Heal partition primary (PID: $pid[0]) on port $p[0] at breakpoint"
609     spawn("./rsm_tester", p[0]+1, "partition", 1)
610     usleep(100000)
611     # xxx it should test that this is the 5th view!
612     print "second view change wait"
613     lastv = (p[0], p[1],p[2])
614     for port in lastv:
615         wait_for_view_change(paxos_log(port), in_views[port]+1, port, 20)
616     print "   Wait for lock_tester to finish (waitpid $t)"
617     waitpid_to(t, 600)
618     if os.system("grep \"passed all tests successfully\" lock_tester-$p[0].log"):
619         mydie("Failed lock tester for test 16")
620     cleanup()
621     usleep(200000)
622
623 print "tests done OK"
624
625 try:
626     os.unlink("config")
627 except OSError:
628     pass