summaryrefslogtreecommitdiff
path: root/python/netlink/route/tc.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/netlink/route/tc.py')
-rw-r--r--python/netlink/route/tc.py459
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