Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(576)

Side by Side Diff: media/tools/constrained_network_server/traffic_control.py

Issue 9127009: Constrained Network test does not fail fast under fatal conditions. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: and again... Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Traffic control library for constraining the network configuration on a port. 5 """Traffic control library for constraining the network configuration on a port.
6 6
7 The traffic controller sets up a constrained network configuration on a port. 7 The traffic controller sets up a constrained network configuration on a port.
8 Traffic to the constrained port is forwarded to a specified server port. 8 Traffic to the constrained port is forwarded to a specified server port.
9 """ 9 """
10 10
11 import logging 11 import logging
12 import re 12 import re
13 import subprocess 13 import subprocess
14 14
15 # The maximum bandwidth limit. 15 # The maximum bandwidth limit.
16 _DEFAULT_MAX_BANDWIDTH_KBPS = 1000000 16 _DEFAULT_MAX_BANDWIDTH_KBIT = 1000000
17 17
18 18
19 class TrafficControlError(BaseException): 19 class TrafficControlError(BaseException):
20 """Exception raised for errors in traffic control library. 20 """Exception raised for errors in traffic control library.
21 21
22 Attributes: 22 Attributes:
23 msg: User defined error message. 23 msg: User defined error message.
24 cmd: Command for which the exception was raised. 24 cmd: Command for which the exception was raised.
25 returncode: Return code of running the command. 25 returncode: Return code of running the command.
26 stdout: Output of running the command. 26 stdout: Output of running the command.
(...skipping 16 matching lines...) Expand all
43 Imposes packet level constraints such as bandwidth, latency, and packet loss 43 Imposes packet level constraints such as bandwidth, latency, and packet loss
44 on a given port using the specified configuration dictionary. Traffic to that 44 on a given port using the specified configuration dictionary. Traffic to that
45 port is forwarded to a specified server port. 45 port is forwarded to a specified server port.
46 46
47 Args: 47 Args:
48 config: Constraint configuration dictionary, format: 48 config: Constraint configuration dictionary, format:
49 port: Port to constrain (integer 1-65535). 49 port: Port to constrain (integer 1-65535).
50 server_port: Port to redirect traffic on [port] to (integer 1-65535). 50 server_port: Port to redirect traffic on [port] to (integer 1-65535).
51 interface: Network interface name (string). 51 interface: Network interface name (string).
52 latency: Delay added on each packet sent (integer in ms). 52 latency: Delay added on each packet sent (integer in ms).
53 bandwidth: Maximum allowed upload bandwidth (integer in kbps). 53 bandwidth: Maximum allowed upload bandwidth (integer in kbit/s).
54 loss: Percentage of packets to drop (integer 0-100). 54 loss: Percentage of packets to drop (integer 0-100).
55 55
56 Raises: 56 Raises:
57 TrafficControlError: If any operation fails. The message in the exception 57 TrafficControlError: If any operation fails. The message in the exception
58 describes what failed. 58 describes what failed.
59 """ 59 """
60 _CheckArgsExist(config, 'interface', 'port', 'server_port') 60 _CheckArgsExist(config, 'interface', 'port', 'server_port')
61 _AddRootQdisc(config['interface']) 61 _AddRootQdisc(config['interface'])
62 62
63 try: 63 try:
(...skipping 15 matching lines...) Expand all
79 the constrained port to a specified server port. 79 the constrained port to a specified server port.
80 80
81 The original constrained network configuration used to create the constrained 81 The original constrained network configuration used to create the constrained
82 port must be passed in. 82 port must be passed in.
83 83
84 Args: 84 Args:
85 config: Constraint configuration dictionary, format: 85 config: Constraint configuration dictionary, format:
86 port: Port to constrain (integer 1-65535). 86 port: Port to constrain (integer 1-65535).
87 server_port: Port to redirect traffic on [port] to (integer 1-65535). 87 server_port: Port to redirect traffic on [port] to (integer 1-65535).
88 interface: Network interface name (string). 88 interface: Network interface name (string).
89 bandwidth: Maximum allowed upload bandwidth (integer in kbps). 89 bandwidth: Maximum allowed upload bandwidth (integer in kbit/s).
90 90
91 Raises: 91 Raises:
92 TrafficControlError: If any operation fails. The message in the exception 92 TrafficControlError: If any operation fails. The message in the exception
93 describes what failed. 93 describes what failed.
94 """ 94 """
95 _CheckArgsExist(config, 'interface', 'port', 'server_port') 95 _CheckArgsExist(config, 'interface', 'port', 'server_port')
96 try: 96 try:
97 # Delete filters first so it frees the class. 97 # Delete filters first so it frees the class.
98 _DeleteFilter(config['interface'], config['port']) 98 _DeleteFilter(config['interface'], config['port'])
99 finally: 99 finally:
(...skipping 11 matching lines...) Expand all
111 Args: 111 Args:
112 config: Constraint configuration dictionary, format: 112 config: Constraint configuration dictionary, format:
113 interface: Network interface name (string). 113 interface: Network interface name (string).
114 114
115 Raises: 115 Raises:
116 TrafficControlError: If any operation fails. The message in the exception 116 TrafficControlError: If any operation fails. The message in the exception
117 describes what failed. 117 describes what failed.
118 """ 118 """
119 _CheckArgsExist(config, 'interface') 119 _CheckArgsExist(config, 'interface')
120 120
121 command = ['tc', 'qdisc', 'del', 'dev', config['interface'], 'root'] 121 command = ['sudo', 'tc', 'qdisc', 'del', 'dev', config['interface'], 'root']
122 try: 122 try:
123 _Exec(command, msg='Could not delete root qdisc.') 123 _Exec(command, msg='Could not delete root qdisc.')
124 finally: 124 finally:
125 _DeleteAllIpTableRules() 125 _DeleteAllIpTableRules()
126 126
127 127
128 def _CheckArgsExist(config, *args): 128 def _CheckArgsExist(config, *args):
129 """Check that the args exist in config dictionary and are not None. 129 """Check that the args exist in config dictionary and are not None.
130 130
131 Args: 131 Args:
(...skipping 11 matching lines...) Expand all
143 def _AddRootQdisc(interface): 143 def _AddRootQdisc(interface):
144 """Sets up the default root qdisc. 144 """Sets up the default root qdisc.
145 145
146 Args: 146 Args:
147 interface: Network interface name. 147 interface: Network interface name.
148 148
149 Raises: 149 Raises:
150 TrafficControlError: If adding the root qdisc fails for a reason other than 150 TrafficControlError: If adding the root qdisc fails for a reason other than
151 it already exists. 151 it already exists.
152 """ 152 """
153 command = ['tc', 'qdisc', 'add', 'dev', interface, 'root', 'handle', '1:', 153 command = ['sudo', 'tc', 'qdisc', 'add', 'dev', interface, 'root', 'handle',
154 'htb'] 154 '1:', 'htb']
155 try: 155 try:
156 _Exec(command, msg=('Error creating root qdisc. ' 156 _Exec(command, msg=('Error creating root qdisc. '
157 'Make sure you have root access')) 157 'Make sure you have root access'))
158 except TrafficControlError as e: 158 except TrafficControlError as e:
159 # Ignore the error if root already exists. 159 # Ignore the error if root already exists.
160 if not 'File exists' in e.error: 160 if not 'File exists' in e.error:
161 raise e 161 raise e
162 162
163 163
164 def _ConfigureClass(option, config): 164 def _ConfigureClass(option, config):
165 """Adds or deletes a class and qdisc attached to the root. 165 """Adds or deletes a class and qdisc attached to the root.
166 166
167 The class specifies bandwidth, and qdisc specifies delay and packet loss. The 167 The class specifies bandwidth, and qdisc specifies delay and packet loss. The
168 class ID is based on the config port. 168 class ID is based on the config port.
169 169
170 Args: 170 Args:
171 option: Adds or deletes a class option [add|del]. 171 option: Adds or deletes a class option [add|del].
172 config: Constraint configuration dictionary, format: 172 config: Constraint configuration dictionary, format:
173 port: Port to constrain (integer 1-65535). 173 port: Port to constrain (integer 1-65535).
174 interface: Network interface name (string). 174 interface: Network interface name (string).
175 bandwidth: Maximum allowed upload bandwidth (integer in kbps). 175 bandwidth: Maximum allowed upload bandwidth (integer in kbit/s).
176 """ 176 """
177 # Use constrained port as class ID so we can attach the qdisc and filter to 177 # Use constrained port as class ID so we can attach the qdisc and filter to
178 # it, as well as delete the class, using only the port number. 178 # it, as well as delete the class, using only the port number.
179 class_id = '1:%x' % config['port'] 179 class_id = '1:%x' % config['port']
180 if 'bandwidth' not in config.keys() or not config['bandwidth']: 180 if 'bandwidth' not in config.keys() or not config['bandwidth']:
181 bandwidth = _DEFAULT_MAX_BANDWIDTH_KBPS 181 bandwidth = _DEFAULT_MAX_BANDWIDTH_KBIT
182 else: 182 else:
183 bandwidth = config['bandwidth'] 183 bandwidth = config['bandwidth']
184 184
185 bandwidth = '%dkbps' % bandwidth 185 bandwidth = '%dkbit' % bandwidth
186 command = ['tc', 'class', option, 'dev', config['interface'], 'parent', '1:', 186 command = ['sudo', 'tc', 'class', option, 'dev', config['interface'],
187 'classid', class_id, 'htb', 'rate', bandwidth, 'ceil', bandwidth] 187 'parent', '1:', 'classid', class_id, 'htb', 'rate', bandwidth,
188 'ceil', bandwidth]
188 _Exec(command, msg=('Error configuring class ID %s using "%s" command.' % 189 _Exec(command, msg=('Error configuring class ID %s using "%s" command.' %
189 (class_id, option))) 190 (class_id, option)))
190 191
191 192
192 def _AddSubQdisc(config): 193 def _AddSubQdisc(config):
193 """Adds a qdisc attached to the class identified by the config port. 194 """Adds a qdisc attached to the class identified by the config port.
194 195
195 Args: 196 Args:
196 config: Constraint configuration dictionary, format: 197 config: Constraint configuration dictionary, format:
197 port: Port to constrain (integer 1-65535). 198 port: Port to constrain (integer 1-65535).
198 interface: Network interface name (string). 199 interface: Network interface name (string).
199 latency: Delay added on each packet sent (integer in ms). 200 latency: Delay added on each packet sent (integer in ms).
200 loss: Percentage of packets to drop (integer 0-100). 201 loss: Percentage of packets to drop (integer 0-100).
201 """ 202 """
202 port_hex = '%x' % config['port'] 203 port_hex = '%x' % config['port']
203 class_id = '1:%x' % config['port'] 204 class_id = '1:%x' % config['port']
204 command = ['tc', 'qdisc', 'add', 'dev', config['interface'], 'parent', 205 command = ['sudo', 'tc', 'qdisc', 'add', 'dev', config['interface'], 'parent',
205 class_id, 'handle', port_hex + ':0', 'netem'] 206 class_id, 'handle', port_hex + ':0', 'netem']
206 207
207 # Check if packet-loss is set in the configuration. 208 # Check if packet-loss is set in the configuration.
208 if 'loss' in config.keys() and config['loss']: 209 if 'loss' in config.keys() and config['loss']:
209 loss = '%d%%' % config['loss'] 210 loss = '%d%%' % config['loss']
210 command.extend(['loss', loss]) 211 command.extend(['loss', loss])
211 # Check if latency is set in the configuration. 212 # Check if latency is set in the configuration.
212 if 'latency' in config.keys() and config['latency']: 213 if 'latency' in config.keys() and config['latency']:
213 latency = '%dms' % config['latency'] 214 latency = '%dms' % config['latency']
214 command.extend(['delay', latency]) 215 command.extend(['delay', latency])
215 216
216 _Exec(command, msg='Could not attach qdisc to class ID %s.' % class_id) 217 _Exec(command, msg='Could not attach qdisc to class ID %s.' % class_id)
217 218
218 219
219 def _AddFilter(interface, port): 220 def _AddFilter(interface, port):
220 """Redirects packets coming to a specified port into the constrained class. 221 """Redirects packets coming to a specified port into the constrained class.
221 222
222 Args: 223 Args:
223 interface: Interface name to attach the filter to (string). 224 interface: Interface name to attach the filter to (string).
224 port: Port number to filter packets with (integer 1-65535). 225 port: Port number to filter packets with (integer 1-65535).
225 """ 226 """
226 class_id = '1:%x' % port 227 class_id = '1:%x' % port
227 228
228 command = ['tc', 'filter', 'add', 'dev', interface, 'protocol', 'ip', 229 command = ['sudo', 'tc', 'filter', 'add', 'dev', interface, 'protocol', 'ip',
229 'parent', '1:', 'prio', '1', 'u32', 'match', 'ip', 'sport', port, 230 'parent', '1:', 'prio', '1', 'u32', 'match', 'ip', 'sport', port,
230 '0xffff', 'flowid', class_id] 231 '0xffff', 'flowid', class_id]
231 _Exec(command, msg='Error adding filter on port %d.' % port) 232 _Exec(command, msg='Error adding filter on port %d.' % port)
232 233
233 234
234 def _DeleteFilter(interface, port): 235 def _DeleteFilter(interface, port):
235 """Deletes the filter attached to the configured port. 236 """Deletes the filter attached to the configured port.
236 237
237 Args: 238 Args:
238 interface: Interface name the filter is attached to (string). 239 interface: Interface name the filter is attached to (string).
239 port: Port number being filtered (integer 1-65535). 240 port: Port number being filtered (integer 1-65535).
240 """ 241 """
241 handle_id = _GetFilterHandleId(interface, port) 242 handle_id = _GetFilterHandleId(interface, port)
242 command = ['tc', 'filter', 'del', 'dev', interface, 'protocol', 'ip', 243 command = ['sudo', 'tc', 'filter', 'del', 'dev', interface, 'protocol', 'ip',
243 'parent', '1:0', 'handle', handle_id, 'prio', '1', 'u32'] 244 'parent', '1:0', 'handle', handle_id, 'prio', '1', 'u32']
244 _Exec(command, msg='Error deleting filter on port %d.' % port) 245 _Exec(command, msg='Error deleting filter on port %d.' % port)
245 246
246 247
247 def _GetFilterHandleId(interface, port): 248 def _GetFilterHandleId(interface, port):
248 """Searches for the handle ID of the filter identified by the config port. 249 """Searches for the handle ID of the filter identified by the config port.
249 250
250 Args: 251 Args:
251 interface: Interface name the filter is attached to (string). 252 interface: Interface name the filter is attached to (string).
252 port: Port number being filtered (integer 1-65535). 253 port: Port number being filtered (integer 1-65535).
253 254
254 Returns: 255 Returns:
255 The handle ID. 256 The handle ID.
256 257
257 Raises: 258 Raises:
258 TrafficControlError: If handle ID was not found. 259 TrafficControlError: If handle ID was not found.
259 """ 260 """
260 command = ['tc', 'filter', 'list', 'dev', interface, 'parent', '1:'] 261 command = ['sudo', 'tc', 'filter', 'list', 'dev', interface, 'parent', '1:']
261 output = _Exec(command, msg='Error listing filters.') 262 output = _Exec(command, msg='Error listing filters.')
262 # Search for the filter handle ID associated with class ID '1:port'. 263 # Search for the filter handle ID associated with class ID '1:port'.
263 handle_id_re = re.search( 264 handle_id_re = re.search(
264 '([0-9a-fA-F]{3}::[0-9a-fA-F]{3}).*(?=flowid 1:%x\s)' % port, output) 265 '([0-9a-fA-F]{3}::[0-9a-fA-F]{3}).*(?=flowid 1:%x\s)' % port, output)
265 if handle_id_re: 266 if handle_id_re:
266 return handle_id_re.group(1) 267 return handle_id_re.group(1)
267 raise TrafficControlError(('Could not find filter handle ID for class ID ' 268 raise TrafficControlError(('Could not find filter handle ID for class ID '
268 '1:%x.') % port) 269 '1:%x.') % port)
269 270
270 271
271 def _AddIptableRule(interface, port, server_port): 272 def _AddIptableRule(interface, port, server_port):
272 """Forwards traffic from constrained port to a specified server port. 273 """Forwards traffic from constrained port to a specified server port.
273 274
274 Args: 275 Args:
275 interface: Interface name to attach the filter to (string). 276 interface: Interface name to attach the filter to (string).
276 port: Port of incoming packets (integer 1-65535). 277 port: Port of incoming packets (integer 1-65535).
277 server_port: Server port to forward the packets to (integer 1-65535). 278 server_port: Server port to forward the packets to (integer 1-65535).
278 """ 279 """
279 # Preroute rules for accessing the port through external connections. 280 # Preroute rules for accessing the port through external connections.
280 command = ['iptables', '-t', 'nat', '-A', 'PREROUTING', '-i', interface, '-p', 281 command = ['sudo', 'iptables', '-t', 'nat', '-A', 'PREROUTING', '-i',
281 'tcp', '--dport', port, '-j', 'REDIRECT', '--to-port', server_port] 282 interface, '-p', 'tcp', '--dport', port, '-j', 'REDIRECT',
283 '--to-port', server_port]
282 _Exec(command, msg='Error adding iptables rule for port %d.' % port) 284 _Exec(command, msg='Error adding iptables rule for port %d.' % port)
283 285
284 # Output rules for accessing the rule through localhost or 127.0.0.1 286 # Output rules for accessing the rule through localhost or 127.0.0.1
285 command = ['iptables', '-t', 'nat', '-A', 'OUTPUT', '-p', 'tcp', '--dport', 287 command = ['sudo', 'iptables', '-t', 'nat', '-A', 'OUTPUT', '-p', 'tcp',
286 port, '-j', 'REDIRECT', '--to-port', server_port] 288 '--dport', port, '-j', 'REDIRECT', '--to-port', server_port]
287 _Exec(command, msg='Error adding iptables rule for port %d.' % port) 289 _Exec(command, msg='Error adding iptables rule for port %d.' % port)
288 290
289 291
290 def _DeleteIptableRule(interface, port, server_port): 292 def _DeleteIptableRule(interface, port, server_port):
291 """Deletes the iptable rule associated with specified port number. 293 """Deletes the iptable rule associated with specified port number.
292 294
293 Args: 295 Args:
294 interface: Interface name to attach the filter to (string). 296 interface: Interface name to attach the filter to (string).
295 port: Port of incoming packets (integer 1-65535). 297 port: Port of incoming packets (integer 1-65535).
296 server_port: Server port packets are forwarded to (integer 1-65535). 298 server_port: Server port packets are forwarded to (integer 1-65535).
297 """ 299 """
298 command = ['iptables', '-t', 'nat', '-D', 'PREROUTING', '-i', interface, '-p', 300 command = ['sudo', 'iptables', '-t', 'nat', '-D', 'PREROUTING', '-i',
299 'tcp', '--dport', port, '-j', 'REDIRECT', '--to-port', server_port] 301 interface, '-p', 'tcp', '--dport', port, '-j', 'REDIRECT',
302 '--to-port', server_port]
300 _Exec(command, msg='Error deleting iptables rule for port %d.' % port) 303 _Exec(command, msg='Error deleting iptables rule for port %d.' % port)
301 304
302 command = ['iptables', '-t', 'nat', '-D', 'OUTPUT', '-p', 'tcp', '--dport', 305 command = ['sudo', 'iptables', '-t', 'nat', '-D', 'OUTPUT', '-p', 'tcp',
303 port, '-j', 'REDIRECT', '--to-port', server_port] 306 '--dport', port, '-j', 'REDIRECT', '--to-port', server_port]
304 _Exec(command, msg='Error adding iptables rule for port %d.' % port) 307 _Exec(command, msg='Error adding iptables rule for port %d.' % port)
305 308
306 309
307 def _DeleteAllIpTableRules(): 310 def _DeleteAllIpTableRules():
308 """Deletes all iptables rules.""" 311 """Deletes all iptables rules."""
309 command = ['iptables', '-t', 'nat', '-F'] 312 command = ['sudo', 'iptables', '-t', 'nat', '-F']
310 _Exec(command, msg='Error deleting all iptables rules.') 313 _Exec(command, msg='Error deleting all iptables rules.')
311 314
312 315
313 def _Exec(command, msg=None): 316 def _Exec(command, msg=None):
314 """Executes a command. 317 """Executes a command.
315 318
316 Args: 319 Args:
317 command: Command list to execute. 320 command: Command list to execute.
318 msg: Message describing the error in case the command fails. 321 msg: Message describing the error in case the command fails.
319 322
320 Returns: 323 Returns:
321 The standard output from running the command. 324 The standard output from running the command.
322 325
323 Raises: 326 Raises:
324 TrafficControlError: If command fails. Message is set by the msg parameter. 327 TrafficControlError: If command fails. Message is set by the msg parameter.
325 """ 328 """
326 cmd_list = [str(x) for x in command] 329 cmd_list = [str(x) for x in command]
327 cmd = ' '.join(cmd_list) 330 cmd = ' '.join(cmd_list)
328 logging.debug('Running command: %s', cmd) 331 logging.debug('Running command: %s', cmd)
329 332
330 p = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 333 p = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
331 output, error = p.communicate() 334 output, error = p.communicate()
332 if p.returncode != 0: 335 if p.returncode != 0:
333 raise TrafficControlError(msg, cmd, p.returncode, output, error) 336 raise TrafficControlError(msg, cmd, p.returncode, output, error)
334 return output.strip() 337 return output.strip()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698