Esempio n. 1
0
class Delay(NumericOption):
    _basetype = PQ
    _maximum = PQ("3000ms")
    _attributes = (
        ("jitter", PQ, PQ(0.0, "us")),
        ("correlation", Percent, Percent(0.0)),
    )

    def __float__(self):
        return float(self._value.inUnitsOf("ms"))
Esempio n. 2
0
def parse_report(bigstring):
    imps = []
    for line in bigstring.splitlines():
        if line.startswith("qdisc"):
            if "netem" in line:
                if "delay" in line:
                    delay = PQ(0.0, "ms")
                    jitter = PQ(0.0, "ms")
                    correlation = 0.0
                    mo = re.search(
                        r"delay ([0-9.]+\s*\D+)\s+([0-9.]+\s*\D+)\s+([0-9.]+)%",  # noqa
                        line)
                    if mo:
                        delay = PQ(mo.group(1))
                        jitter = PQ(mo.group(2))
                        correlation = Percent(mo.group(3))
                    else:
                        mo = re.search(
                            r"delay ([0-9.]+\s*\D+)\s+([0-9.]+\s*\D+)", line)
                        if mo:
                            delay = PQ(mo.group(1))
                            jitter = PQ(mo.group(2))
                        else:
                            mo = re.search(r"delay ([0-9.]+\s*\D+)", line)
                            if mo:
                                delay = PQ(mo.group(1))
                    imps.append(Delay(delay, jitter=jitter,
                                      correlation=correlation))
                if "distribution" in line:
                    mo = re.search(r"distribution (\w+)", line)
                    dist = mo.group(1)
                    imps.append(Distribution(dist))
                if "loss" in line:
                    correlation = 0.0
                    mo = re.search(r"loss\s+([0-9.]+)%\s+([0-9.]+)%", line)
                    if mo:
                        pstr = mo.group(1)
                        correlation = mo.group(2)
                    else:
                        mo = re.search(r"loss\s+([0-9.]+)%", line)
                        if mo:
                            pstr = mo.group(1)
                    imps.append(Drop(pstr, correlation=correlation))
                if "corrupt" in line:
                    correlation = 0.0
                    mo = re.search(r"corrupt\s+([0-9.]+)%\s+([0-9.]+)%", line)
                    if mo:
                        pstr = mo.group(1)
                        correlation = mo.group(2)
                    else:
                        mo = re.search(r"corrupt\s+([0-9.]+)%", line)
                        if mo:
                            pstr = mo.group(1)
                    imps.append(Corrupt(pstr, correlation=correlation))
                if "duplicate" in line:
                    correlation = 0.0
                    mo = re.search(r"duplicate\s+([0-9.]+)%\s+([0-9.]+)%",
                                   line)
                    if mo:
                        pstr = mo.group(1)
                        correlation = mo.group(2)
                    else:
                        mo = re.search(r"duplicate\s+([0-9.]+)%", line)
                        if mo:
                            pstr = mo.group(1)
                    imps.append(Duplicate(pstr, correlation=correlation))
                if "reorder" in line:
                    correlation = 0.0
                    mo = re.search(r"reorder\s+([0-9.]+)%\s+([0-9.]+)%", line)
                    if mo:
                        pstr = mo.group(1)
                        correlation = mo.group(2)
                    else:
                        mo = re.search(r"reorder\s+([0-9.]+)%", line)
                        if mo:
                            pstr = mo.group(1)
                    imps.append(Reorder(pstr, correlation=correlation))
                if "gap" in line:
                    mo = re.search(r"gap\s+([0-9]+)", line)
                    pstr = mo.group(1)
                    imps.append(Gap(pstr))
    return Impairment(*imps)
Esempio n. 3
0
class Impairment:
    # name of maximum values and default values
    _max_values = {
        "maxlatency": PQ("3000ms"),
        "maxdrop": Percent(90.0),
        "maxcorruption": Percent(90.0),
        "maxduplicate": Percent(90.0),
        "maxreorder": Percent(90.0),
        "maxgap": Gap,
    }

    def __init__(self, *args, **kwargs):
        self._parent = None
        self._children = []
        self._nodeid = "root"
        self._impairments = list(args)
        for maxname, default in list(Impairment._max_values.items()):
            value = kwargs.pop(maxname, default)
            setattr(self, maxname, value)
        if kwargs:
            raise ValueError(
                "Impairment got extra keyword arguments: {!r}".format(kwargs))

    def add_delay(self, value, **kwargs):
        return self._impairments.append(Delay(value, **kwargs))

    def add_distribution(self, value):
        return self._impairments.append(Distribution(value))

    def add_drop(self, value, **kwargs):
        return self._impairments.append(Drop(value, **kwargs))

    def add_corrupt(self, value, **kwargs):
        return self._impairments.append(Corrupt(value, **kwargs))

    def add_duplicate(self, value, **kwargs):
        return self._impairments.append(Duplicate(value, **kwargs))

    def add_reorder(self, value, **kwargs):
        return self._impairments.append(Reorder(value, **kwargs))

    def add_gap(self, value):
        return self._impairments.append(Gap(value))

    def add_limit(self, value):
        return self._impairments.append(Limit(value))

    def over_maximum(self):
        primary = self._impairments[0]
        return primary.over_maximum()

    def __str__(self):
        s = [self._nodeid if
             isinstance(self._nodeid, str) else "{:04x}".format(self._nodeid)]
        s.append("netem")
        s.extend(str(imp) for imp in self._impairments)
        return " ".join(s)

    def copy(self):
        return self.__class__(**self.__dict__)

    def clear(self):
        self._impairments = []

    def __repr__(self):
        return "{0}({1})".format(
            self.__class__.__name__,
            ", ".join(repr(o) for o in self._impairments))

    def __iter__(self):
        return iter(self._impairments)

    def __getitem__(self, idx):
        return self._impairments[idx]

    def __bool__(self):
        return bool(self._impairments)

    def __add__(self, other):
        return self._perform_op(other, operator.add)

    def __sub__(self, other):
        return self._perform_op(other, operator.sub)

    def __truediv__(self, other):
        return self._perform_op(other, operator.truediv)
    __div__ = __truediv__

    def __mul__(self, other):
        return self._perform_op(other, operator.mul)

    def _perform_op(self, other, op):
        new = []
        if not self._impairments:
            return self.__class__()
        imp = self._impairments[0]
        if isinstance(imp, NumericOption):
            new.append(op(imp, other[0]))
        else:
            new.append(imp.copy())
        for imp in self._impairments[1:]:
            new.append(imp.copy())
        return self.__class__(*new)

    def applyto(self, router):
        """Apply impairments from this object using given router instance.

        The impairments values are spread over all of the router interfaces
        to make it symetrical. Typically, this will be two interfaces.
        """
        # e.g.: tc qdisc add dev {0} root netem delay {1}ms {2} {3} {4}
        if self._parent is not None:
            raise RouterConstructorError("May only apply to root impairment.")
        oldimp = router.current()
        router.reset()
        if not self._impairments:
            return
        n = len(router.interfaces)
        parms = self.netem_command(n)
        if self._nodeid == "root":
            change = "add"
        else:
            change = "change"
        for intf in router.interfaces:
            cmd = "tc qdisc {} dev {} {} {}".format(change, intf,
                                                    self._nodeid, parms)
            router._perform(cmd)
        # TODO apply child impairments
        return oldimp

    def netem_command(self, n=1):
        s = ["netem"]
        for imp in self._impairments:
            if isinstance(imp, NumericOption):
                s.append(str(imp / n))
            else:
                s.append(str(imp))
        return " ".join(s)

    def get_direction(self, oldvalue):
        if not oldvalue:
            return Unknown
        assert type(oldvalue) is type(self)
        oldvalue = oldvalue._impairments[0]
        value = self._impairments[0]
        if oldvalue > value:
            return Down
        elif oldvalue < value:
            return Up
        else:
            return Level

    # tree management
    @property
    def parent(self):
        return self._parent

    def _check_obj(self, other):
        if not isinstance(other, self.__class__):
            raise ValueError("Must operate on same Impairment type.")

    def replace(self, imp):
        self._check_obj(imp)
        if self._parent:
            p = self._parent
            i = self._parent.index(self)
            del self._parent[i]
            self._parent = None
            p.insert(i, imp)
        return self

    def detach(self):
        if self._parent:
            try:
                i = self._parent.index(self)
                del self._parent[i]
            except ValueError:
                pass
        self._parent = None
        return self

    def destroy(self):
        if self._parent:
            i = self._parent.index(self)
            del self._parent[i]
        self._parent = None
        for n in self._children:
            n._parent = None
        self._children = None

    def get_children(self):
        return self._children[:]

    def _find_index(self, index):
        if type(index) is str:
            for i, ch in enumerate(self._children):
                if ch.matchpath(index):
                    return i
            raise IndexError("no impairments match")
        else:
            return index

    _path_re = re.compile(r'(\w+)\[(.*)]')

    def matchpath(self, pathelement):
        if "[" not in pathelement:
            return pathelement == self._name
        else:
            mo = Impairment._path_re.match(pathelement)
            if mo:
                name, match = mo.groups()
                if name != self._name:
                    return False
                mp = match.split("=")
                attr = getattr(self, mp[0], None)
                if attr is None:
                    return False
                if len(mp) > 1:
                    return mp[1][1:-1] == attr
                else:
                    return True
            else:
                raise ValueError(
                    "Path element {!r} not found.".format(pathelement))