diff options
Diffstat (limited to 'python/netlink/route/tc.py')
-rw-r--r-- | python/netlink/route/tc.py | 459 |
1 files changed, 377 insertions, 82 deletions
diff --git a/python/netlink/route/tc.py b/python/netlink/route/tc.py index ebe25e1..bae9bca 100644 --- a/python/netlink/route/tc.py +++ b/python/netlink/route/tc.py @@ -1,5 +1,3 @@ - - # # Copyright (c) 2011 Thomas Graf <tgraf@suug.ch> # @@ -8,7 +6,9 @@ __all__ = [ 'TcCache', 'Tc', 'QdiscCache', - 'Qdisc'] + 'Qdisc', + 'TcClassCache', + 'TcClass'] import socket import sys @@ -17,7 +17,7 @@ import netlink.capi as core_capi import netlink.route.capi as capi import netlink.util as util -from netlink.route.link import Link +import netlink.route.link as Link TC_PACKETS = 0 TC_BYTES = 1 @@ -32,7 +32,31 @@ TC_OVERLIMITS = 9 TC_H_ROOT = 0xFFFFFFFF TC_H_INGRESS = 0xFFFFFFF1 +STAT_PACKETS = 0 +STAT_BYTES = 1 +STAT_RATE_BPS = 2 +STAT_RATE_PPS = 3 +STAT_QLEN = 4 +STAT_BACKLOG = 5 +STAT_DROPS = 6 +STAT_REQUEUES = 7 +STAT_OVERLIMITS = 8 +STAT_MAX = STAT_OVERLIMITS + + +########################################################################### +# Handle class Handle(object): + """ Traffic control handle + + Representation of a traffic control handle which uniquely identifies + each traffic control object in its link namespace. + + handle = tc.Handle('10:20') + handle = tc.handle('root') + print int(handle) + print str(handle) + """ def __init__(self, val=None): if type(val) is str: val = capi.tc_str2handle(val) @@ -41,6 +65,17 @@ class Handle(object): self._val = int(val) + def __cmp__(self, other): + if other is None: + other = 0 + + if isinstance(other, Handle): + return int(self) - int(other) + elif isinstance(other, int): + return int(self) - other + else: + raise TypeError() + def __int__(self): return self._val @@ -62,9 +97,18 @@ class TcCache(netlink.Cache): # Tc Object class Tc(netlink.Object): def __cmp__(self, other): - return self.ifindex - other.ifindex - - def isroot(self): + diff = self.ifindex - other.ifindex + if diff == 0: + diff = int(self.handle) - int(other.handle) + return diff + + def _tc_module_lookup(self): + self._module_lookup(self._module_path + self.kind, + 'init_' + self._name) + + @property + def root(self): + """True if tc object is a root object""" return self.parent.isroot() ##################################################################### @@ -72,120 +116,140 @@ class Tc(netlink.Object): @property def ifindex(self): """interface index""" - return capi.rtnl_tc_get_ifindex(self._tc) + return capi.rtnl_tc_get_ifindex(self._rtnl_tc) @ifindex.setter def ifindex(self, value): - capi.rtnl_tc_set_ifindex(self._tc, int(value)) + capi.rtnl_tc_set_ifindex(self._rtnl_tc, int(value)) ##################################################################### # link @property def link(self): - link = capi.rtnl_tc_get_link(self._tc) + link = capi.rtnl_tc_get_link(self._rtnl_tc) if not link: return None - else: - return Link._from_capi(link) + + return Link.Link.from_capi(link) @link.setter def link(self, value): - capi.rtnl_tc_set_link(self._tc, value._link) + capi.rtnl_tc_set_link(self._rtnl_tc, value._link) ##################################################################### # mtu @property def mtu(self): - return capi.rtnl_tc_get_mtu(self._tc) + return capi.rtnl_tc_get_mtu(self._rtnl_tc) @mtu.setter def mtu(self, value): - capi.rtnl_tc_set_mtu(self._tc, int(value)) + capi.rtnl_tc_set_mtu(self._rtnl_tc, int(value)) ##################################################################### # mpu @property def mpu(self): - return capi.rtnl_tc_get_mpu(self._tc) + return capi.rtnl_tc_get_mpu(self._rtnl_tc) @mpu.setter def mpu(self, value): - capi.rtnl_tc_set_mpu(self._tc, int(value)) + capi.rtnl_tc_set_mpu(self._rtnl_tc, int(value)) ##################################################################### # overhead @property def overhead(self): - return capi.rtnl_tc_get_overhead(self._tc) + return capi.rtnl_tc_get_overhead(self._rtnl_tc) @overhead.setter def overhead(self, value): - capi.rtnl_tc_set_overhead(self._tc, int(value)) + capi.rtnl_tc_set_overhead(self._rtnl_tc, int(value)) ##################################################################### # linktype @property def linktype(self): - return capi.rtnl_tc_get_linktype(self._tc) + return capi.rtnl_tc_get_linktype(self._rtnl_tc) @linktype.setter def linktype(self, value): - capi.rtnl_tc_set_linktype(self._tc, int(value)) + capi.rtnl_tc_set_linktype(self._rtnl_tc, int(value)) ##################################################################### # handle @property def handle(self): - return Handle(capi.rtnl_tc_get_handle(self._tc)) + return Handle(capi.rtnl_tc_get_handle(self._rtnl_tc)) @handle.setter def handle(self, value): - capi.rtnl_tc_set_handle(self._tc, int(value)) + capi.rtnl_tc_set_handle(self._rtnl_tc, int(value)) ##################################################################### # parent @property def parent(self): - return Handle(capi.rtnl_tc_get_parent(self._tc)) + return Handle(capi.rtnl_tc_get_parent(self._rtnl_tc)) @parent.setter def parent(self, value): - capi.rtnl_tc_set_parent(self._tc, int(value)) + capi.rtnl_tc_set_parent(self._rtnl_tc, int(value)) ##################################################################### # kind @property def kind(self): - return capi.rtnl_tc_get_kind(self._tc) + return capi.rtnl_tc_get_kind(self._rtnl_tc) @kind.setter def kind(self, value): - capi.rtnl_tc_set_kind(self._tc, value) + capi.rtnl_tc_set_kind(self._rtnl_tc, value) + self._tc_module_lookup() def get_stat(self, id): - return capi.rtnl_tc_get_stat(self._tc, id) + return capi.rtnl_tc_get_stat(self._rtnl_tc, id) -class TcTree(object): - def __init__(self, link, sock): - self._qdisc_cache = QdiscCache().refill(sock) + @property + def _dev(self): + buf = util.kw('dev') + ' ' - def __getitem__(self, key): - pass -# if type(key) is int: -# link = capi.rtnl_link_get(self._this, key) -# elif type(key) is str: -# link = capi.rtnl_link_get_by_name(self._this, key) -# -# if qdisc is None: -# raise KeyError() -# else: -# return Qdisc._from_capi(capi.qdisc2obj(qdisc)) + if self.link: + return buf + util.string(self.link.name) + else: + return buf + util.num(self.ifindex) - + def brief(self, title, nodev=False, noparent=False): + ret = title + ' {a|kind} {a|handle}' + + if not nodev: + ret += ' {a|_dev}' + + if not noparent: + ret += ' {t|parent}' + + return ret + self._module_brief() + + def details(self): + return '{t|mtu} {t|mpu} {t|overhead} {t|linktype}' + + @property + def packets(self): + return self.get_stat(STAT_PACKETS) + + @property + def bytes(self): + return self.get_stat(STAT_BYTES) + @property + def qlen(self): + return self.get_stat(STAT_QLEN) + def stats(self, fmt): + return fmt.nl('{t|packets} {t|bytes} {t|qlen}') + ########################################################################### -# Link Cache +# Queueing discipline cache class QdiscCache(netlink.Cache): """Cache of qdiscs""" @@ -193,7 +257,8 @@ class QdiscCache(netlink.Cache): if not cache: cache = self._alloc_cache_name("route/qdisc") - self._c_cache = cache + self._protocol = netlink.NETLINK_ROUTE + self._nl_cache = cache # def __getitem__(self, key): # if type(key) is int: @@ -215,33 +280,48 @@ class QdiscCache(netlink.Cache): ########################################################################### # Qdisc Object class Qdisc(Tc): - """Network link""" + """Queueing discipline""" def __init__(self, obj=None): - self._name = "qdisc" - self._abbr = "qdisc" + netlink.Object.__init__(self, "route/qdisc", "qdisc", obj) + self._module_path = 'netlink.route.qdisc.' + self._rtnl_qdisc = self._obj2type(self._nl_object) + self._rtnl_tc = capi.obj2tc(self._nl_object) - if not obj: - self._qdisc = capi.rtnl_qdisc_alloc() - else: - self._qdisc = capi.obj2qdisc(obj) + netlink.add_attr('qdisc.handle', fmt=util.handle) + netlink.add_attr('qdisc.parent', fmt=util.handle) + netlink.add_attr('qdisc.kind', fmt=util.bold) - self._obj = capi.qdisc2obj(self._qdisc) - self._orig = capi.obj2qdisc(core_capi.nl_object_clone(self._obj)) + if self.kind: + self._tc_module_lookup() - Tc.__init__(self) + @classmethod + def from_capi(cls, obj): + return cls(capi.qdisc2obj(obj)) - netlink.attr('qdisc.handle', fmt=util.handle) - netlink.attr('qdisc.parent', fmt=util.handle) - netlink.attr('qdisc.kind', fmt=util.bold) - - def __cmp__(self, other): - return self.handle - other.handle + def _obj2type(self, obj): + return capi.obj2qdisc(obj) def _new_instance(self, obj): - if not obj: raise ValueError() + if not obj: + raise ValueError() + return Qdisc(obj) + @property + def childs(self): + ret = [] + + if int(self.handle): + ret += get_cls(self.ifindex, parent=self.handle) + + if self.root: + ret += get_class(self.ifindex, parent=TC_H_ROOT) + + ret += get_class(self.ifindex, parent=self.handle) + + return ret + # ##################################################################### # # add() # def add(self, socket, flags=None): @@ -270,33 +350,22 @@ class Qdisc(Tc): # if ret < 0: # raise netlink.KernelError(ret) - @property - def _dev(self): - buf = util.kw('dev') + ' ' - - if self.link: - return buf + util.string(self.link.name) - else: - return buf + util.num(self.ifindex) - - @property - def _parent(self): - return util.kw('parent') + ' ' + str(self.parent) - ################################################################### # # format(details=False, stats=False) # - def format(self, details=False, stats=False): + def format(self, details=False, stats=False, nodev=False, + noparent=False, indent=''): """Return qdisc as formatted text""" - fmt = util.BriefFormatter(self) - - buf = fmt.format('qdisc {kind} {handle} {_dev} {_parent}') + fmt = util.MyFormatter(self, indent) + buf = fmt.format(self.brief('qdisc', nodev, noparent)) + if details: - fmt = util.DetailFormatter(self) - buf += fmt.format('\n'\ - '\t{mtu} {mpu} {overhead}\n') + buf += fmt.nl('\t' + self.details()) + + if stats: + buf += self.stats(fmt) # if stats: # l = [['Packets', RX_PACKETS, TX_PACKETS], @@ -355,3 +424,229 @@ class Qdisc(Tc): # buf += '\t{0:27} {1:>16} {2:>16}\n'.format(*row) return buf + +########################################################################### +# Traffic class cache +class TcClassCache(netlink.Cache): + """Cache of traffic classes""" + + def __init__(self, ifindex, cache=None): + if not cache: + cache = self._alloc_cache_name("route/class") + + self._protocol = netlink.NETLINK_ROUTE + self._nl_cache = cache + self._set_arg1(ifindex) + + def _new_object(self, obj): + return TcClass(obj) + + def _new_cache(self, cache): + return TcClassCache(self.arg1, cache=cache) + +########################################################################### +# Traffic Class Object +class TcClass(Tc): + """Traffic Class""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, "route/class", "class", obj) + self._module_path = 'netlink.route.qdisc.' + self._rtnl_class = self._obj2type(self._nl_object) + self._rtnl_tc = capi.obj2tc(self._nl_object) + + netlink.add_attr('class.handle', fmt=util.handle) + netlink.add_attr('class.parent', fmt=util.handle) + netlink.add_attr('class.kind', fmt=util.bold) + + if self.kind: + self._tc_module_lookup() + + @classmethod + def from_capi(cls, obj): + return cls(capi.class2obj(obj)) + + def _obj2type(self, obj): + return capi.obj2class(obj) + + def _new_instance(self, obj): + if not obj: + raise ValueError() + + return TcClass(obj) + + @property + def childs(self): + ret = [] + + # classes can have classifiers, child classes and leaf + # qdiscs + ret += get_cls(self.ifindex, parent=self.handle) + ret += get_class(self.ifindex, parent=self.handle) + ret += get_qdisc(self.ifindex, parent=self.handle) + + return ret + + ################################################################### + # + # format(details=False, stats=False) + # + def format(self, details=False, stats=False, nodev=False, + noparent=False, indent=''): + """Return class as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format(self.brief('class', nodev, noparent)) + + if details: + buf += fmt.nl('\t' + self.details()) + + return buf + +########################################################################### +# Classifier Cache +class ClassifierCache(netlink.Cache): + """Cache of traffic classifiers objects""" + + def __init__(self, ifindex, parent, cache=None): + if not cache: + cache = self._alloc_cache_name("route/cls") + + self._protocol = netlink.NETLINK_ROUTE + self._nl_cache = cache + self._set_arg1(ifindex) + self._set_arg2(int(parent)) + + def _new_object(self, obj): + return Classifier(obj) + + def _new_cache(self, cache): + return ClassifierCache(self.arg1, self.arg2, cache=cache) + +########################################################################### +# Classifier Object +class Classifier(Tc): + """Classifier""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, "route/cls", "cls", obj) + self._module_path = 'netlink.route.cls.' + self._rtnl_cls = self._obj2type(self._nl_object) + self._rtnl_tc = capi.obj2tc(self._nl_object) + + netlink.add_attr('cls.handle', fmt=util.handle) + netlink.add_attr('cls.parent', fmt=util.handle) + netlink.add_attr('cls.kind', fmt=util.bold) + + @classmethod + def from_capi(cls, obj): + return cls(capi.cls2obj(obj)) + + def _obj2type(self, obj): + return capi.obj2cls(obj) + + def _new_instance(self, obj): + if not obj: + raise ValueError() + + return Classifier(obj) + + ##################################################################### + # priority + @property + def priority(self): + return capi.rtnl_cls_get_prio(self._rtnl_cls) + + @priority.setter + def priority(self, value): + capi.rtnl_cls_set_prio(self._rtnl_cls, int(value)) + + ##################################################################### + # protocol + @property + def protocol(self): + return capi.rtnl_cls_get_protocol(self._rtnl_cls) + + @protocol.setter + def protocol(self, value): + capi.rtnl_cls_set_protocol(self._rtnl_cls, int(value)) + + @property + def childs(self): + return [] + + ################################################################### + # + # format(details=False, stats=False) + # + def format(self, details=False, stats=False, nodev=False, + noparent=False, indent=''): + """Return class as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format(self.brief('classifier', nodev, noparent)) + buf += fmt.format(' {t|priority} {t|protocol}') + + if details: + buf += fmt.nl('\t' + self.details()) + + return buf + +_qdisc_cache = QdiscCache() + +def get_qdisc(ifindex, handle=None, parent=None): + l = [] + + _qdisc_cache.refill() + + for qdisc in _qdisc_cache: + if qdisc.ifindex == ifindex and \ + (handle == None or qdisc.handle == handle) and \ + (parent == None or qdisc.parent == parent): + l.append(qdisc) + + return l + +_class_cache = {} + +def get_class(ifindex, parent, handle=None): + l = [] + + try: + cache = _class_cache[ifindex] + except KeyError: + cache = TcClassCache(ifindex) + _class_cache[ifindex] = cache + + cache.refill() + + for cl in cache: + if (parent == None or cl.parent == parent) and \ + (handle == None or cl.handle == handle): + l.append(cl) + + return l + +_cls_cache = {} + +def get_cls(ifindex, parent, handle=None): + l = [] + + try: + chain = _cls_cache[ifindex] + except KeyError: + _cls_cache[ifindex] = {} + + try: + cache = _cls_cache[ifindex][parent] + except KeyError: + cache = ClassifierCache(ifindex, parent) + _cls_cache[ifindex][parent] = cache + + cache.refill() + + for cls in cache: + if handle == None or cls.handle == handle: + l.append(cls) + + return l |