15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved. 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file. 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""A script for configuring constraint networks. 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Sets up a constrained network configuration on a specific port. Traffic on this 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)port will be redirected to another local server port. 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)The configuration includes bandwidth, latency, and packet loss. 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)""" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import collections 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import logging 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import optparse 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import traffic_control 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Default logging is ERROR. Use --verbose to enable DEBUG logging. 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)_DEFAULT_LOG_LEVEL = logging.ERROR 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Dispatcher = collections.namedtuple('Dispatcher', ['dispatch', 'requires_ports', 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'desc']) 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Map of command names to traffic_control functions. 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)COMMANDS = { 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Adds a new constrained network configuration. 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'add': Dispatcher(traffic_control.CreateConstrainedPort, 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) requires_ports=True, desc='Add a new constrained port.'), 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Deletes an existing constrained network configuration. 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'del': Dispatcher(traffic_control.DeleteConstrainedPort, 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) requires_ports=True, desc='Delete a constrained port.'), 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Deletes all constrained network configurations. 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'teardown': Dispatcher(traffic_control.TearDown, 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) requires_ports=False, 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) desc='Teardown all constrained ports.') 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def _ParseArgs(): 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Define and parse command-line arguments. 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tuple as (command, configuration): 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) command: one of the possible commands to setup, delete or teardown the 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) constrained network. 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) configuration: a map of constrained network properties to their values. 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser = optparse.OptionParser() 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) indent_first = parser.formatter.indent_increment 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opt_width = parser.formatter.help_position - indent_first 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cmd_usage = [] 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for s in COMMANDS: 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cmd_usage.append('%*s%-*s%s' % 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (indent_first, '', opt_width, s, COMMANDS[s].desc)) 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.usage = ('usage: %%prog {%s} [options]\n\n%s' % 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('|'.join(COMMANDS.keys()), '\n'.join(cmd_usage))) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.add_option('--port', type='int', 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) help='The port to apply traffic control constraints to.') 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.add_option('--server-port', type='int', 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) help='Port to forward traffic on --port to.') 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.add_option('--bandwidth', type='int', 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) help='Bandwidth of the network in kbit/s.') 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.add_option('--latency', type='int', 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) help=('Latency (delay) added to each outgoing packet in ' 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'ms.')) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.add_option('--loss', type='int', 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) help='Packet-loss percentage on outgoing packets. ') 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.add_option('--interface', type='string', 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) help=('Interface to setup constraints on. Use "lo" for a ' 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'local client.')) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.add_option('-v', '--verbose', action='store_true', dest='verbose', 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) default=False, help='Turn on verbose output.') 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) options, args = parser.parse_args() 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) _SetLogger(options.verbose) 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Check a valid command was entered 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not args or args[0].lower() not in COMMANDS: 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.error('Please specify a command {%s}.' % '|'.join(COMMANDS.keys())) 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user_cmd = args[0].lower() 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Check if required options are available 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if COMMANDS[user_cmd].requires_ports: 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not (options.port and options.server_port): 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.error('Please provide port and server-port values.') 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) config = { 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'port': options.port, 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'server_port': options.server_port, 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'interface': options.interface, 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'latency': options.latency, 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'bandwidth': options.bandwidth, 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'loss': options.loss 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return user_cmd, config 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def _SetLogger(verbose): 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) log_level = _DEFAULT_LOG_LEVEL 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if verbose: 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) log_level = logging.DEBUG 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.basicConfig(level=log_level, format='%(message)s') 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Main(): 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Get the command and configuration of the network to set up.""" 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user_cmd, config = _ParseArgs() 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) COMMANDS[user_cmd].dispatch(config) 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except traffic_control.TrafficControlError as e: 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.error('Error: %s\n\nOutput: %s', e.msg, e.error) 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__': 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Main() 124