OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 | |
3 # Copyright 2016 The Chromium Authors. All rights reserved. | |
4 # Use of this source code is governed by a BSD-style license that can be | |
5 # found in the LICENSE file. | |
6 | |
7 import argparse | |
8 import multiprocessing | |
9 import os | |
10 import pwd | |
11 import subprocess | |
12 | |
13 from twisted.internet import reactor | |
14 | |
15 from packet_queue import monitoring | |
16 from packet_queue import nfqueue | |
17 from packet_queue import simulation | |
18 | |
19 | |
20 ENGINE_PORT = 25467 | |
21 MTU = 2048 | |
22 | |
23 | |
24 def parse_args(): | |
25 parser = argparse.ArgumentParser() | |
26 | |
27 parser.add_argument( | |
28 '--target', type=str, required=True, | |
29 help='path of the test binary to run') | |
30 parser.add_argument( | |
31 '--gtest_filter', type=str, default='EngineBrowserTest.LoadUrl', | |
32 help='which specific test to run') | |
33 parser.add_argument( | |
34 '-w', '--bandwidth', type=int, default=-1, | |
35 help='bandwidth cap in bytes/second, unlimited by default') | |
36 parser.add_argument( | |
37 '-b', '--buffer', type=int, default=-1, | |
38 help='buffer size in bytes, unlimited by default') | |
39 parser.add_argument( | |
40 '-d', '--delay', type=float, default=0.0, | |
41 help='one-way packet delay in seconds') | |
42 parser.add_argument( | |
43 '-l', '--loss', type=float, default=0.0, | |
44 help='probability of random packet loss') | |
45 | |
46 return parser.parse_args() | |
47 | |
48 | |
49 def run_packet_queue(params, ready_event): | |
50 """Runs the packet queue with parameters from command-line args. | |
51 | |
52 This function runs the Twisted reactor, which blocks until the process is | |
53 interrupted. | |
54 """ | |
55 event_log = monitoring.EventLog() | |
56 pipes = simulation.PipePair(params, event_log) | |
57 | |
58 nfqueue.configure('tcp', ENGINE_PORT, pipes, 'lo') | |
59 | |
60 reactor.callWhenRunning(ready_event.set) | |
61 reactor.addSystemEventTrigger( | |
62 'before', 'shutdown', report_stats, event_log) | |
63 reactor.run() | |
64 | |
65 | |
66 def report_stats(event_log): | |
67 """Prints accumulated network stats to stdout.""" | |
68 dropped = 0 | |
69 delivered = 0 | |
70 | |
71 for event in event_log.get_pending(): | |
72 if event['type'] == 'drop': | |
73 dropped += event['value'] | |
74 elif event['type'] == 'deliver': | |
75 delivered += event['value'] | |
76 | |
77 print '%i bytes delivered' % delivered | |
78 print '%i bytes dropped' % dropped | |
79 | |
80 | |
81 def set_chrome_test_user(): | |
82 """If the SUDO_USER environment variable exists, sets the user to that. | |
83 | |
84 This is required because Chrome browser tests won't run as root. | |
85 | |
86 If SUDO_USER isn't present, we might be running as a non-root user with | |
87 the CAP_NET_ADMIN capability, so the user won't change. | |
88 """ | |
89 sudo_user = os.getenv('SUDO_USER') | |
90 if sudo_user: | |
91 uid = pwd.getpwnam(sudo_user).pw_uid | |
92 os.setuid(uid) | |
93 | |
94 | |
95 def main(): | |
96 """Runs a Blimp test target with network impairment. | |
97 | |
98 This works by forking to run the packet queue, and then running the test | |
99 binary in a subprocess when the packet queue is ready. | |
100 """ | |
101 args = parse_args() | |
102 | |
103 simulation_params = { | |
104 'bandwidth': args.bandwidth, | |
105 'buffer': args.buffer, | |
106 'delay': args.delay, | |
107 'loss': args.loss, | |
108 } | |
109 | |
110 ready_event = multiprocessing.Event() | |
111 packet_queue = multiprocessing.Process( | |
112 target=run_packet_queue, args=(simulation_params, ready_event)) | |
113 | |
114 if subprocess.call(['ip', 'link', 'set', 'dev', 'lo', 'mtu', str(MTU)]): | |
115 raise OSError('Failed to set MTU') | |
116 | |
117 packet_queue.start() | |
118 ready_event.wait(timeout=3) | |
119 | |
120 subprocess.call([ | |
121 args.target, | |
122 '--disable-setuid-sandbox', | |
123 '--single_process', | |
124 '--engine-port=%i' % ENGINE_PORT, | |
125 '--gtest_filter=%s' % args.gtest_filter, | |
126 ], preexec_fn=set_chrome_test_user) | |
127 | |
128 packet_queue.terminate() | |
129 | |
130 | |
131 if __name__ == '__main__': | |
132 main() | |
OLD | NEW |