aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rumba/elements/topology.py208
1 files changed, 197 insertions, 11 deletions
diff --git a/rumba/elements/topology.py b/rumba/elements/topology.py
index 1e2c1a6..3d11761 100644
--- a/rumba/elements/topology.py
+++ b/rumba/elements/topology.py
@@ -231,6 +231,50 @@ class EthDixLayer(Layer):
new_quality = LinkQuality.clone(self.link_quality, rate=rate)
self.link_quality = new_quality
+ def set_duplicate(self, duplicate=0, correlation=None):
+ """
+ Set the duplication parameter of the underlying link.
+ Parameters as in :py:class:`.Duplicate`
+
+ :param duplicate: duplication in percentage
+ :type duplicate: :py:class:`int` or :py:class:`float`
+ :param correlation: correlation in percentage
+ :type correlation: :py:class:`int` or :py:class:`float`
+ """
+ new_dup = Duplicate(duplicate, correlation)
+ new_quality = LinkQuality.clone(self.link_quality, duplicate=new_dup)
+ self.link_quality = new_quality
+
+ def set_reorder(self, reorder=0, correlation=None):
+ """
+ Set the reorder parameter of the underlying link.
+ Parameters as in :py:class:`.Reorder`
+
+ :param reorder: reorder probability in percentage
+ :type reorder: :py:class:`int` or :py:class:`float`
+ :param correlation: correlation in percentage
+ :type correlation: :py:class:`int` or :py:class:`float`
+ """
+ new_reorder = Reorder(reorder, correlation)
+ new_quality = LinkQuality.clone(
+ self.link_quality, reorder=new_reorder)
+ self.link_quality = new_quality
+
+ def set_corrupt(self, corrupt=0, correlation=None):
+ """
+ Set the corruption parameter of the underlying link.
+ Parameters as in :py:class:`.Corrupt`
+
+ :param corrupt: corruption probability in percentage
+ :type corrupt: :py:class:`int` or :py:class:`float`
+ :param correlation: correlation in percentage
+ :type correlation: :py:class:`int` or :py:class:`float`
+ """
+ new_corrupt = Corrupt(corrupt, correlation)
+ new_quality = LinkQuality.clone(
+ self.link_quality, corrupt=new_corrupt)
+ self.link_quality = new_quality
+
def set_quality(self, delay, loss, rate):
"""
Configure the basic quality parameters of the underlying link.
@@ -492,6 +536,111 @@ class Loss(object):
return " ".join(opts)
+class Duplicate(object):
+ """
+ A class representing packet duplication on a link.
+ """
+ def __init__(self, duplicate, correlation=None):
+ """
+ Configure link duplication.
+
+ :param duplicate: duplication in percentage
+ :type duplicate: :py:class:`int` or :py:class:`float`
+ :param correlation: correlation in percentage
+ :type correlation: :py:class:`int` or :py:class:`float`
+ """
+ if duplicate and (duplicate < 0 or duplicate > 100):
+ raise ValueError("Duplicate needs to be between 0 and 100")
+ if correlation and (correlation < 0 or correlation > 100):
+ raise ValueError("Correlation needs to be between 0 and 100")
+ self._duplicate = duplicate
+ self._correlation = correlation
+
+ @property
+ def duplicate(self):
+ return self._duplicate
+
+ @property
+ def correlation(self):
+ return self._correlation
+
+ def build_command(self):
+ opts = ["duplicate %f%%" % self.duplicate]
+ if self.correlation:
+ opts.append("%f%%" % self.correlation)
+ return " ".join(opts)
+
+
+class Reorder(object):
+ """
+ A class representing packet reordering on a link.
+ """
+ def __init__(self, reorder, correlation=None):
+ """
+ Configure link reordering.
+
+ :param reorder: reorder probability in percentage
+ :type reorder: :py:class:`int` or :py:class:`float`
+ :param correlation: correlation in percentage
+ :type correlation: :py:class:`int` or :py:class:`float`
+ """
+ if reorder and (reorder < 0 or reorder > 100):
+ raise ValueError("Reorder needs to be between 0 and 100")
+ if correlation and (correlation < 0 or correlation > 100):
+ raise ValueError("Correlation needs to be between 0 and 100")
+ self._reorder = reorder
+ self._correlation = correlation
+
+ @property
+ def reorder(self):
+ return self._reorder
+
+ @property
+ def correlation(self):
+ return self._correlation
+
+ def build_command(self):
+ opts = ["reorder %f%%" % self.reorder]
+ if self.correlation:
+ opts.append("%f%%" % self.correlation)
+ return " ".join(opts)
+
+
+class Corrupt(object):
+ """
+ A class representing packet corruption on a link.
+ """
+ def __init__(self, corrupt, correlation=None):
+ """
+ Configure link corruption.
+
+ :param corrupt: corruption probability in percentage
+ :type corrupt: :py:class:`int` or :py:class:`float`
+ :param correlation: correlation in percentage
+ :type correlation: :py:class:`int` or :py:class:`float`
+ """
+ if corrupt and (corrupt < 0 or corrupt > 100):
+ raise ValueError("Corrupt needs to be between 0 and 100")
+ if correlation and (correlation < 0 or correlation > 100):
+ raise ValueError("Correlation needs to be between 0 and 100")
+ self._corrupt = corrupt
+ self._correlation = correlation
+
+ @property
+ def corrupt(self):
+ return self._corrupt
+
+ @property
+ def correlation(self):
+ return self._correlation
+
+ def build_command(self):
+ opts = ["corrupt %f%%" % self.corrupt]
+ if self.correlation:
+ opts.append("%f%%" % self.correlation)
+ return " ".join(opts)
+
+
class LinkQuality(object):
"""
A class representing the link quality.
@@ -499,7 +648,8 @@ class LinkQuality(object):
_active = set()
@classmethod
- def clone(cls, old_quality, delay=None, loss=None, rate=None):
+ def clone(cls, old_quality, delay=None, loss=None, rate=None,
+ duplicate=None, reorder=None, corrupt=None):
"""
Clone old_quality, updating with provided parameters.
@@ -507,6 +657,9 @@ class LinkQuality(object):
:param delay: Delay object or number (ms)
:param loss: Loss object or number (%)
:param rate: Rate in mbit
+ :param duplicate: Duplicate object or number (%)
+ :param reorder: Reorder object or number (%)
+ :param corrupt: Corrupt object or number (%)
:return: a new :py:class:`.LinkQuality` instance.
"""
if delay is None:
@@ -515,25 +668,44 @@ class LinkQuality(object):
loss = old_quality.loss
if rate is None:
rate = old_quality.rate
- return LinkQuality(delay, loss, rate)
+ if duplicate is None:
+ duplicate = old_quality.duplicate
+ if reorder is None:
+ reorder = old_quality.reorder
+ if corrupt is None:
+ corrupt = old_quality.corrupt
+ return LinkQuality(delay, loss, rate, duplicate, reorder, corrupt)
- def __init__(self, delay=None, loss=None, rate=None):
+ def __init__(self, delay=None, loss=None, rate=None,
+ duplicate=None, reorder=None, corrupt=None):
"""
Link quality configuration.
:param delay: Delay object or number (ms)
:param loss: Loss object or number (%)
:param rate: Rate in mbit
+ :param duplicate: Duplicate object or number (%)
+ :param reorder: Reorder object or number (%)
+ :param corrupt: Corrupt object or number (%)
"""
if rate and not rate > 0:
raise ValueError("Rate needs to be higher than 0")
- if isinstance(delay, int) or isinstance(delay, float):
+ if isinstance(delay, (int, float)):
delay = Delay(delay)
- if isinstance(loss, int) or isinstance(loss, float):
+ if isinstance(loss, (int, float)):
loss = Loss(loss)
+ if isinstance(duplicate, (int, float)):
+ duplicate = Duplicate(duplicate)
+ if isinstance(reorder, (int, float)):
+ reorder = Reorder(reorder)
+ if isinstance(corrupt, (int, float)):
+ corrupt = Corrupt(corrupt)
self._delay = delay
self._loss = loss
self._rate = rate
+ self._duplicate = duplicate
+ self._reorder = reorder
+ self._corrupt = corrupt
@property
def delay(self):
@@ -547,6 +719,18 @@ class LinkQuality(object):
def rate(self):
return self._rate
+ @property
+ def duplicate(self):
+ return self._duplicate
+
+ @property
+ def reorder(self):
+ return self._reorder
+
+ @property
+ def corrupt(self):
+ return self._corrupt
+
def build_commands(self, ipcp):
netem_cmd = []
cmds = []
@@ -564,20 +748,22 @@ class LinkQuality(object):
% (ipcp.ifname, self.rate))
qref = "parent 1:1"
- if self.delay or self.loss:
+ netem_parts = [self.delay, self.loss,
+ self.duplicate, self.reorder, self.corrupt]
+ if any(netem_parts):
netem_cmd.append(
"tc qdisc add dev %s %s netem"
% (ipcp.ifname, qref))
- if self.delay:
- netem_cmd.append(self.delay.build_command())
- if self.loss:
- netem_cmd.append(self.loss.build_command())
+ for part in netem_parts:
+ if part:
+ netem_cmd.append(part.build_command())
cmds.append(" ".join(netem_cmd))
return cmds
def apply(self, layer):
- if not (self.delay or self.loss or self.rate):
+ if not (self.delay or self.loss or self.rate
+ or self.duplicate or self.reorder or self.corrupt):
self.deactivate(layer)
else:
for ipcp in layer.ipcps: