Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tc reorder and tc duplicate support #67

Merged
merged 2 commits into from
May 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions tcconfig/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,9 @@ def parse(self, text):
self.__parse_netem_param(line, "delay", pp.nums + ".")
self.__parse_netem_delay_distro(line)
self.__parse_netem_param(line, "loss", pp.nums + ".")
self.__parse_netem_param(line, "duplicate", pp.nums + ".")
self.__parse_netem_param(line, "corrupt", pp.nums + ".")
self.__parse_netem_param(line, "reorder", pp.nums + ".")
self.__parse_tbf_rate(line)

yield self.__parsed_param
Expand Down
10 changes: 8 additions & 2 deletions tcconfig/shaper/_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

@six.add_metaclass(abc.ABCMeta)
class ShaperInterface(object):

@abc.abstractproperty
def algorithm_name(self): # pragma: no cover
pass
Expand Down Expand Up @@ -54,7 +53,6 @@ def set_shaping(self): # pragma: no cover


class AbstractShaper(ShaperInterface):

@property
def tc_device(self):
return "{:s}".format(self._tc_obj.get_tc_device())
Expand Down Expand Up @@ -83,6 +81,10 @@ def set_netem(self):
command_item_list.append(
"loss {:f}%".format(self._tc_obj.packet_loss_rate))

if self._tc_obj.packet_duplicate_rate > 0:
command_item_list.append(
"duplicate {:f}%".format(self._tc_obj.packet_duplicate_rate))

if self._tc_obj.latency_ms > 0:
command_item_list.append(
"delay {}ms".format(self._tc_obj.latency_ms))
Expand All @@ -95,6 +97,10 @@ def set_netem(self):
command_item_list.append(
"corrupt {:f}%".format(self._tc_obj.corruption_rate))

if self._tc_obj.reordering_rate > 0:
command_item_list.append(
"reorder {:f}%".format(self._tc_obj.reordering_rate))

return run_command_helper(
" ".join(command_item_list),
self._tc_obj.REGEXP_FILE_EXISTS,
Expand Down
1 change: 0 additions & 1 deletion tcconfig/shaper/htb.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@


class HtbShaper(AbstractShaper):

__DEFAULT_CLASS_MINOR_ID = 1

class MinQdiscMinorId(object):
Expand Down
33 changes: 23 additions & 10 deletions tcconfig/tcset.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@
import sys

import logbook
import pyparsing as pp
import six
import subprocrunner
import typepy

import pyparsing as pp

from ._argparse_wrapper import ArgparseWrapper
from ._common import write_tc_script
from ._const import (
Expand All @@ -37,7 +36,6 @@
from ._traffic_direction import TrafficDirection
from .traffic_control import TrafficControl


logbook.StderrHandler(
level=logbook.DEBUG, format_string=LOG_FORMAT_STRING).push_application()

Expand Down Expand Up @@ -96,6 +94,14 @@ def parse_option():
""".format(
TrafficControl.MIN_PACKET_LOSS_RATE,
TrafficControl.MAX_PACKET_LOSS_RATE))
group.add_argument(
"--duplicate", dest="packet_duplicate_rate", type=float, default=0,
help="""
round trip packet duplicate rate [%%]. the valid range is {:d} to {:d}.
(default=%(default)s)
""".format(
TrafficControl.MIN_PACKET_DUPLICATE_RATE,
TrafficControl.MAX_PACKET_DUPLICATE_RATE))
group.add_argument(
"--corrupt", dest="corruption_rate", type=float, default=0,
help="""
Expand All @@ -105,6 +111,14 @@ def parse_option():
""".format(
TrafficControl.MIN_CORRUPTION_RATE,
TrafficControl.MAX_CORRUPTION_RATE))
group.add_argument(
"--reordering", dest="reordering_rate", type=float, default=0,
help="""
packet reordering rate [%%]. the valid range is {:d} to {:d}.
(default=%(default)s)
""".format(
TrafficControl.MIN_REORDERING_RATE,
TrafficControl.MAX_REORDERING_RATE))
group.add_argument(
"--network", "--dst-network",
help="target IP address/network to control traffic")
Expand Down Expand Up @@ -152,7 +166,6 @@ def verify_netem_module():


class TcConfigLoader(object):

def __init__(self, logger):
self.__logger = logger
self.__config_table = None
Expand Down Expand Up @@ -199,12 +212,10 @@ def get_tcconfig_command_list(self):
if not filter_table:
continue

option_list = [
device_option, "--direction={:s}".format(direction),
] + [
"--{:s}={:s}".format(k, v)
for k, v in six.iteritems(filter_table)
]
option_list = [device_option,
"--direction={:s}".format(direction), ] + [
"--{:s}={:s}".format(k, v) for k, v in
six.iteritems(filter_table)]

try:
network = self.__parse_tc_filter_network(tc_filter)
Expand Down Expand Up @@ -298,7 +309,9 @@ def main():
latency_ms=options.network_latency,
latency_distro_ms=options.latency_distro_ms,
packet_loss_rate=options.packet_loss_rate,
packet_duplicate_rate=options.packet_duplicate_rate,
corruption_rate=options.corruption_rate,
reordering_rate=options.reordering_rate,
network=options.network,
src_network=options.src_network,
src_port=options.src_port,
Expand Down
6 changes: 2 additions & 4 deletions tcconfig/tcshow.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
TcClassParser,
)


logbook.StderrHandler(
level=logbook.DEBUG, format_string=LOG_FORMAT_STRING).push_application()

Expand All @@ -68,7 +67,6 @@ def parse_option():


class TcShapingRuleParser(object):

@property
def device(self):
return self.__device
Expand Down Expand Up @@ -243,8 +241,8 @@ def main():
logger.debug(str(e))
continue

tc_param.update(TcShapingRuleParser(
device, options.ip_version, logger).get_tc_parameter())
tc_param.update(TcShapingRuleParser(device, options.ip_version,
logger).get_tc_parameter())

command_history = "\n".join(SubprocessRunner.get_history())

Expand Down
56 changes: 44 additions & 12 deletions tcconfig/traffic_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,21 @@ def _validate_within_min_max(param_name, value, min_value, max_value, unit):


class TrafficControl(object):

MIN_PACKET_LOSS_RATE = 0 # [%]
MAX_PACKET_LOSS_RATE = 100 # [%]

MIN_PACKET_DUPLICATE_RATE = 0 # [%]
MAX_PACKET_DUPLICATE_RATE = 100 # [%]

MIN_LATENCY_MS = 0 # [millisecond]
MAX_LATENCY_MS = 3600000 # [millisecond] (60 minutes)

MIN_CORRUPTION_RATE = 0 # [%]
MAX_CORRUPTION_RATE = 100 # [%]

MIN_REORDERING_RATE = 0 # [%]
MAX_REORDERING_RATE = 100 # [%]

__MIN_PORT = 0
__MAX_PORT = 65535

Expand Down Expand Up @@ -111,10 +116,18 @@ def latency_distro_ms(self):
def packet_loss_rate(self):
return self.__packet_loss_rate

@property
def packet_duplicate_rate(self):
return self.__packet_duplicate_rate

@property
def corruption_rate(self):
return self.__corruption_rate

@property
def reordering_rate(self):
return self.__reordering_rate

@property
def network(self):
return self.__network
Expand Down Expand Up @@ -171,7 +184,8 @@ def __init__(
self, device,
direction=None, bandwidth_rate=None,
latency_ms=None, latency_distro_ms=None,
packet_loss_rate=None, corruption_rate=None,
packet_loss_rate=None, packet_duplicate_rate=None,
corruption_rate=None, reordering_rate=None,
network=None, src_port=None, dst_port=None, is_ipv6=False,
src_network=None,
is_add_shaper=False,
Expand All @@ -185,7 +199,9 @@ def __init__(
self.__latency_ms = latency_ms # [milliseconds]
self.__latency_distro_ms = latency_distro_ms # [milliseconds]
self.__packet_loss_rate = packet_loss_rate # [%]
self.__packet_duplicate_rate = packet_duplicate_rate # [%]
self.__corruption_rate = corruption_rate # [%]
self.__reordering_rate = reordering_rate
self.__network = network
self.__src_network = src_network
self.__src_port = src_port
Expand Down Expand Up @@ -327,11 +343,27 @@ def __validate_packet_loss_rate(self):
"loss (packet loss rate)", self.packet_loss_rate,
self.MIN_PACKET_LOSS_RATE, self.MAX_PACKET_LOSS_RATE, unit="%")

def __validate_packet_duplicate_rate(self):
_validate_within_min_max(
"duplicate (packet duplicate rate)", self.packet_duplicate_rate,
self.MIN_PACKET_DUPLICATE_RATE, self.MAX_PACKET_DUPLICATE_RATE,
unit="%")

def __validate_corruption_rate(self):
_validate_within_min_max(
"corruption (packet corruption rate)", self.corruption_rate,
self.MIN_CORRUPTION_RATE, self.MAX_CORRUPTION_RATE, unit="%")

def __validate_reordering_rate(self):
_validate_within_min_max(
"reordering (packet reordering rate)", self.reordering_rate,
self.MIN_REORDERING_RATE, self.MAX_REORDERING_RATE, unit="%")

def __validate_reordering_and_delay(self):
if self.reordering_rate and not self.latency_ms:
raise ValueError(
'reordering needs latency to be specified: set latency > 0')

def __validate_netem_parameter(self):
try:
self.validate_bandwidth_rate()
Expand All @@ -340,20 +372,23 @@ def __validate_netem_parameter(self):

self.__validate_network_delay()
self.__validate_packet_loss_rate()
self.__validate_packet_duplicate_rate()
self.__validate_corruption_rate()
self.__validate_reordering_rate()
self.__validate_reordering_and_delay()

netem_param_value_list = [
self.bandwidth_rate,
self.latency_ms,
self.packet_loss_rate,
self.packet_duplicate_rate,
self.corruption_rate,
self.reordering_rate
]

if all([
not RealNumber(
netem_param_value).is_type() or netem_param_value == 0
for netem_param_value in netem_param_value_list
]):
if all([not RealNumber(
netem_param_value).is_type() or netem_param_value == 0 for
netem_param_value in netem_param_value_list]):
raise ValueError(
"there is no valid net emulation parameter value."
"at least one or more following parameters are required: "
Expand All @@ -371,7 +406,6 @@ def __validate_port(self):

def __get_device_qdisc_major_id(self):
import hashlib

base_device_hash = hashlib.md5(six.b(self.__device)).hexdigest()[:3]
device_hash_prefix = "1"

Expand Down Expand Up @@ -424,10 +458,8 @@ def __delete_ifb_device(self):
"ip link delete {:s} type ifb".format(self.ifb_device),
]

if all([
spr.SubprocessRunner(command).run() != 0
for command in command_list
]):
if all([spr.SubprocessRunner(command).run() != 0 for command in
command_list]):
return 2

return 0