Add packaging directory
[platform/upstream/libnl3.git] / python / netlink / route / tc.py
1 #
2 # Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
3 #
4 from __future__ import absolute_import
5
6 __all__ = [
7     'TcCache',
8     'Tc',
9     'QdiscCache',
10     'Qdisc',
11     'TcClassCache',
12     'TcClass',
13 ]
14
15 from .. import core as netlink
16 from .  import capi as capi
17 from .. import util as util
18 from .  import link as Link
19
20 TC_PACKETS = 0
21 TC_BYTES = 1
22 TC_RATE_BPS = 2
23 TC_RATE_PPS = 3
24 TC_QLEN = 4
25 TC_BACKLOG = 5
26 TC_DROPS = 6
27 TC_REQUEUES = 7
28 TC_OVERLIMITS = 9
29
30 TC_H_ROOT = 0xFFFFFFFF
31 TC_H_INGRESS = 0xFFFFFFF1
32
33 STAT_PACKETS = 0
34 STAT_BYTES = 1
35 STAT_RATE_BPS = 2
36 STAT_RATE_PPS = 3
37 STAT_QLEN = 4
38 STAT_BACKLOG = 5
39 STAT_DROPS = 6
40 STAT_REQUEUES = 7
41 STAT_OVERLIMITS = 8
42 STAT_MAX = STAT_OVERLIMITS
43
44
45 class Handle(object):
46     """ Traffic control handle
47
48     Representation of a traffic control handle which uniquely identifies
49     each traffic control object in its link namespace.
50
51     handle = tc.Handle('10:20')
52     handle = tc.handle('root')
53     print int(handle)
54     print str(handle)
55     """
56     def __init__(self, val=None):
57         if type(val) is str:
58             val = capi.tc_str2handle(val)
59         elif not val:
60             val = 0
61
62         self._val = int(val)
63
64     def __cmp__(self, other):
65         if other is None:
66             other = 0
67
68         if isinstance(other, Handle):
69             return int(self) - int(other)
70         elif isinstance(other, int):
71             return int(self) - other
72         else:
73             raise TypeError()
74
75     def __int__(self):
76         return self._val
77
78     def __str__(self):
79         return capi.rtnl_tc_handle2str(self._val, 64)[0]
80
81     def isroot(self):
82         return self._val == TC_H_ROOT or self._val == TC_H_INGRESS
83
84 class TcCache(netlink.Cache):
85     """Cache of traffic control object"""
86
87     def __getitem__(self, key):
88         raise NotImplementedError()
89
90 class Tc(netlink.Object):
91     def __cmp__(self, other):
92         diff = self.ifindex - other.ifindex
93         if diff == 0:
94             diff = int(self.handle) - int(other.handle)
95         return diff
96
97     def _tc_module_lookup(self):
98         self._module_lookup(self._module_path + self.kind,
99                     'init_' + self._name)
100
101     @property
102     def root(self):
103         """True if tc object is a root object"""
104         return self.parent.isroot()
105
106     @property
107     def ifindex(self):
108         """interface index"""
109         return capi.rtnl_tc_get_ifindex(self._rtnl_tc)
110
111     @ifindex.setter
112     def ifindex(self, value):
113         capi.rtnl_tc_set_ifindex(self._rtnl_tc, int(value))
114
115     @property
116     def link(self):
117         link = capi.rtnl_tc_get_link(self._rtnl_tc)
118         if not link:
119             return None
120
121         return Link.Link.from_capi(link)
122
123     @link.setter
124     def link(self, value):
125         capi.rtnl_tc_set_link(self._rtnl_tc, value._link)
126
127     @property
128     def mtu(self):
129         return capi.rtnl_tc_get_mtu(self._rtnl_tc)
130
131     @mtu.setter
132     def mtu(self, value):
133         capi.rtnl_tc_set_mtu(self._rtnl_tc, int(value))
134
135     @property
136     def mpu(self):
137         return capi.rtnl_tc_get_mpu(self._rtnl_tc)
138
139     @mpu.setter
140     def mpu(self, value):
141         capi.rtnl_tc_set_mpu(self._rtnl_tc, int(value))
142
143     @property
144     def overhead(self):
145         return capi.rtnl_tc_get_overhead(self._rtnl_tc)
146
147     @overhead.setter
148     def overhead(self, value):
149         capi.rtnl_tc_set_overhead(self._rtnl_tc, int(value))
150
151     @property
152     def linktype(self):
153         return capi.rtnl_tc_get_linktype(self._rtnl_tc)
154
155     @linktype.setter
156     def linktype(self, value):
157         capi.rtnl_tc_set_linktype(self._rtnl_tc, int(value))
158
159     @property
160     @netlink.nlattr(fmt=util.handle)
161     def handle(self):
162         return Handle(capi.rtnl_tc_get_handle(self._rtnl_tc))
163
164     @handle.setter
165     def handle(self, value):
166         capi.rtnl_tc_set_handle(self._rtnl_tc, int(value))
167
168     @property
169     @netlink.nlattr(fmt=util.handle)
170     def parent(self):
171         return Handle(capi.rtnl_tc_get_parent(self._rtnl_tc))
172
173     @parent.setter
174     def parent(self, value):
175         capi.rtnl_tc_set_parent(self._rtnl_tc, int(value))
176
177     @property
178     @netlink.nlattr(fmt=util.bold)
179     def kind(self):
180         return capi.rtnl_tc_get_kind(self._rtnl_tc)
181
182     @kind.setter
183     def kind(self, value):
184         capi.rtnl_tc_set_kind(self._rtnl_tc, value)
185         self._tc_module_lookup()
186
187     def get_stat(self, id):
188         return capi.rtnl_tc_get_stat(self._rtnl_tc, id)
189
190     @property
191     def _dev(self):
192         buf = util.kw('dev') + ' '
193
194         if self.link:
195             return buf + util.string(self.link.name)
196         else:
197             return buf + util.num(self.ifindex)
198
199     def brief(self, title, nodev=False, noparent=False):
200         ret = title + ' {a|kind} {a|handle}'
201
202         if not nodev:
203             ret += ' {a|_dev}'
204
205         if not noparent:
206             ret += ' {t|parent}'
207
208         return ret + self._module_brief()
209
210     @staticmethod
211     def details():
212         return '{t|mtu} {t|mpu} {t|overhead} {t|linktype}'
213
214     @property
215     def packets(self):
216         return self.get_stat(STAT_PACKETS)
217
218     @property
219     def bytes(self):
220         return self.get_stat(STAT_BYTES)
221
222     @property
223     def qlen(self):
224         return self.get_stat(STAT_QLEN)
225
226     @staticmethod
227     def stats(fmt):
228         return fmt.nl('{t|packets} {t|bytes} {t|qlen}')
229
230 class QdiscCache(netlink.Cache):
231     """Cache of qdiscs"""
232
233     def __init__(self, cache=None):
234         if not cache:
235             cache = self._alloc_cache_name('route/qdisc')
236
237         self._protocol = netlink.NETLINK_ROUTE
238         self._nl_cache = cache
239
240 #       def __getitem__(self, key):
241 #               if type(key) is int:
242 #                        link = capi.rtnl_link_get(self._this, key)
243 #                elif type(key) is str:
244 #                        link = capi.rtnl_link_get_by_name(self._this, key)
245 #
246 #               if qdisc is None:
247 #                        raise KeyError()
248 #               else:
249 #                        return Qdisc._from_capi(capi.qdisc2obj(qdisc))
250
251     @staticmethod
252     def _new_object(obj):
253         return Qdisc(obj)
254
255     @staticmethod
256     def _new_cache(cache):
257         return QdiscCache(cache=cache)
258
259 class Qdisc(Tc):
260     """Queueing discipline"""
261
262     def __init__(self, obj=None):
263         netlink.Object.__init__(self, 'route/qdisc', 'qdisc', obj)
264         self._module_path = 'netlink.route.qdisc.'
265         self._rtnl_qdisc = self._obj2type(self._nl_object)
266         self._rtnl_tc = capi.obj2tc(self._nl_object)
267
268         if self.kind:
269             self._tc_module_lookup()
270
271     @classmethod
272     def from_capi(cls, obj):
273         return cls(capi.qdisc2obj(obj))
274
275     @staticmethod
276     def _obj2type(obj):
277         return capi.obj2qdisc(obj)
278
279     @staticmethod
280     def _new_instance(obj):
281         if not obj:
282             raise ValueError()
283
284         return Qdisc(obj)
285
286     @property
287     def childs(self):
288         ret = []
289
290         if int(self.handle):
291             ret += get_cls(self.ifindex, parent=self.handle)
292
293             if self.root:
294                 ret += get_class(self.ifindex, parent=TC_H_ROOT)
295
296             ret += get_class(self.ifindex, parent=self.handle)
297
298         return ret
299
300 #       def add(self, socket, flags=None):
301 #               if not flags:
302 #                        flags = netlink.NLM_F_CREATE
303 #
304 #               ret = capi.rtnl_link_add(socket._sock, self._link, flags)
305 #               if ret < 0:
306 #                       raise netlink.KernelError(ret)
307 #
308 #       def change(self, socket, flags=0):
309 #               """Commit changes made to the link object"""
310 #               if not self._orig:
311 #                       raise NetlinkError('Original link not available')
312 #               ret = capi.rtnl_link_change(socket._sock, self._orig, self._link, flags)
313 #                if ret < 0:
314 #                        raise netlink.KernelError(ret)
315 #
316 #       def delete(self, socket):
317 #               """Attempt to delete this link in the kernel"""
318 #               ret = capi.rtnl_link_delete(socket._sock, self._link)
319 #                if ret < 0:
320 #                        raise netlink.KernelError(ret)
321
322     def format(self, details=False, stats=False, nodev=False,
323            noparent=False, indent=''):
324         """Return qdisc as formatted text"""
325         fmt = util.MyFormatter(self, indent)
326
327         buf = fmt.format(self.brief('qdisc', nodev, noparent))
328
329         if details:
330             buf += fmt.nl('\t' + self.details())
331
332         if stats:
333             buf += self.stats(fmt)
334
335 #               if stats:
336 #                       l = [['Packets', RX_PACKETS, TX_PACKETS],
337 #                            ['Bytes', RX_BYTES, TX_BYTES],
338 #                            ['Errors', RX_ERRORS, TX_ERRORS],
339 #                            ['Dropped', RX_DROPPED, TX_DROPPED],
340 #                            ['Compressed', RX_COMPRESSED, TX_COMPRESSED],
341 #                            ['FIFO Errors', RX_FIFO_ERR, TX_FIFO_ERR],
342 #                            ['Length Errors', RX_LEN_ERR, None],
343 #                            ['Over Errors', RX_OVER_ERR, None],
344 #                            ['CRC Errors', RX_CRC_ERR, None],
345 #                            ['Frame Errors', RX_FRAME_ERR, None],
346 #                            ['Missed Errors', RX_MISSED_ERR, None],
347 #                            ['Abort Errors', None, TX_ABORT_ERR],
348 #                            ['Carrier Errors', None, TX_CARRIER_ERR],
349 #                            ['Heartbeat Errors', None, TX_HBEAT_ERR],
350 #                            ['Window Errors', None, TX_WIN_ERR],
351 #                            ['Collisions', None, COLLISIONS],
352 #                            ['Multicast', None, MULTICAST],
353 #                            ['', None, None],
354 #                            ['Ipv6:', None, None],
355 #                            ['Packets', IP6_INPKTS, IP6_OUTPKTS],
356 #                            ['Bytes', IP6_INOCTETS, IP6_OUTOCTETS],
357 #                            ['Discards', IP6_INDISCARDS, IP6_OUTDISCARDS],
358 #                            ['Multicast Packets', IP6_INMCASTPKTS, IP6_OUTMCASTPKTS],
359 #                            ['Multicast Bytes', IP6_INMCASTOCTETS, IP6_OUTMCASTOCTETS],
360 #                            ['Broadcast Packets', IP6_INBCASTPKTS, IP6_OUTBCASTPKTS],
361 #                            ['Broadcast Bytes', IP6_INBCASTOCTETS, IP6_OUTBCASTOCTETS],
362 #                            ['Delivers', IP6_INDELIVERS, None],
363 #                            ['Forwarded', None, IP6_OUTFORWDATAGRAMS],
364 #                            ['No Routes', IP6_INNOROUTES, IP6_OUTNOROUTES],
365 #                            ['Header Errors', IP6_INHDRERRORS, None],
366 #                            ['Too Big Errors', IP6_INTOOBIGERRORS, None],
367 #                            ['Address Errors', IP6_INADDRERRORS, None],
368 #                            ['Unknown Protocol', IP6_INUNKNOWNPROTOS, None],
369 #                            ['Truncated Packets', IP6_INTRUNCATEDPKTS, None],
370 #                            ['Reasm Timeouts', IP6_REASMTIMEOUT, None],
371 #                            ['Reasm Requests', IP6_REASMREQDS, None],
372 #                            ['Reasm Failures', IP6_REASMFAILS, None],
373 #                            ['Reasm OK', IP6_REASMOKS, None],
374 #                            ['Frag Created', None, IP6_FRAGCREATES],
375 #                            ['Frag Failures', None, IP6_FRAGFAILS],
376 #                            ['Frag OK', None, IP6_FRAGOKS],
377 #                            ['', None, None],
378 #                            ['ICMPv6:', None, None],
379 #                            ['Messages', ICMP6_INMSGS, ICMP6_OUTMSGS],
380 #                            ['Errors', ICMP6_INERRORS, ICMP6_OUTERRORS]]
381 #
382 #                       buf += '\n\t%s%s%s%s\n' % (33 * ' ', util.title('RX'),
383 #                                                  15 * ' ', util.title('TX'))
384 #
385 #                       for row in l:
386 #                               row[0] = util.kw(row[0])
387 #                                row[1] = self.get_stat(row[1]) if row[1] else ''
388 #                                row[2] = self.get_stat(row[2]) if row[2] else ''
389 #                               buf += '\t{0:27} {1:>16} {2:>16}\n'.format(*row)
390
391         return buf
392
393 class TcClassCache(netlink.Cache):
394     """Cache of traffic classes"""
395
396     def __init__(self, ifindex, cache=None):
397         if not cache:
398             cache = self._alloc_cache_name('route/class')
399
400         self._protocol = netlink.NETLINK_ROUTE
401         self._nl_cache = cache
402         self._set_arg1(ifindex)
403
404     @staticmethod
405     def _new_object(obj):
406         return TcClass(obj)
407
408     def _new_cache(self, cache):
409         return TcClassCache(self.arg1, cache=cache)
410
411 class TcClass(Tc):
412     """Traffic Class"""
413
414     def __init__(self, obj=None):
415         netlink.Object.__init__(self, 'route/class', 'class', obj)
416         self._module_path = 'netlink.route.qdisc.'
417         self._rtnl_class = self._obj2type(self._nl_object)
418         self._rtnl_tc = capi.obj2tc(self._nl_object)
419
420         if self.kind:
421             self._tc_module_lookup()
422
423     @classmethod
424     def from_capi(cls, obj):
425         return cls(capi.class2obj(obj))
426
427     @staticmethod
428     def _obj2type(obj):
429         return capi.obj2class(obj)
430
431     @staticmethod
432     def _new_instance(obj):
433         if not obj:
434             raise ValueError()
435
436         return TcClass(obj)
437
438     @property
439     def childs(self):
440         ret = []
441
442         # classes can have classifiers, child classes and leaf
443         # qdiscs
444         ret += get_cls(self.ifindex, parent=self.handle)
445         ret += get_class(self.ifindex, parent=self.handle)
446         ret += get_qdisc(self.ifindex, parent=self.handle)
447
448         return ret
449
450     def format(self, details=False, _stats=False, nodev=False,
451            noparent=False, indent=''):
452         """Return class as formatted text"""
453         fmt = util.MyFormatter(self, indent)
454
455         buf = fmt.format(self.brief('class', nodev, noparent))
456
457         if details:
458             buf += fmt.nl('\t' + self.details())
459
460         return buf
461
462 class ClassifierCache(netlink.Cache):
463     """Cache of traffic classifiers objects"""
464
465     def __init__(self, ifindex, parent, cache=None):
466         if not cache:
467             cache = self._alloc_cache_name('route/cls')
468
469         self._protocol = netlink.NETLINK_ROUTE
470         self._nl_cache = cache
471         self._set_arg1(ifindex)
472         self._set_arg2(int(parent))
473
474     @staticmethod
475     def _new_object(obj):
476         return Classifier(obj)
477
478     def _new_cache(self, cache):
479         return ClassifierCache(self.arg1, self.arg2, cache=cache)
480
481 class Classifier(Tc):
482     """Classifier"""
483
484     def __init__(self, obj=None):
485         netlink.Object.__init__(self, 'route/cls', 'cls', obj)
486         self._module_path = 'netlink.route.cls.'
487         self._rtnl_cls = self._obj2type(self._nl_object)
488         self._rtnl_tc = capi.obj2tc(self._nl_object)
489
490     @classmethod
491     def from_capi(cls, obj):
492         return cls(capi.cls2obj(obj))
493
494     @staticmethod
495     def _obj2type(obj):
496         return capi.obj2cls(obj)
497
498     @staticmethod
499     def _new_instance(obj):
500         if not obj:
501             raise ValueError()
502
503         return Classifier(obj)
504
505     @property
506     def priority(self):
507         return capi.rtnl_cls_get_prio(self._rtnl_cls)
508
509     @priority.setter
510     def priority(self, value):
511         capi.rtnl_cls_set_prio(self._rtnl_cls, int(value))
512
513     @property
514     def protocol(self):
515         return capi.rtnl_cls_get_protocol(self._rtnl_cls)
516
517     @protocol.setter
518     def protocol(self, value):
519         capi.rtnl_cls_set_protocol(self._rtnl_cls, int(value))
520
521     @property
522     def childs(self):
523         return []
524
525     def format(self, details=False, _stats=False, nodev=False,
526            noparent=False, indent=''):
527         """Return class as formatted text"""
528         fmt = util.MyFormatter(self, indent)
529
530         buf = fmt.format(self.brief('classifier', nodev, noparent))
531         buf += fmt.format(' {t|priority} {t|protocol}')
532
533         if details:
534             buf += fmt.nl('\t' + self.details())
535
536         return buf
537
538 _qdisc_cache = QdiscCache()
539
540 def get_qdisc(ifindex, handle=None, parent=None):
541     l = []
542
543     _qdisc_cache.refill()
544
545     for qdisc in _qdisc_cache:
546         if qdisc.ifindex != ifindex:
547             continue
548         if (handle is not None) and (qdisc.handle != handle):
549             continue
550         if (parent is not None) and (qdisc.parent != parent):
551             continue
552         l.append(qdisc)
553
554     return l
555
556 _class_cache = {}
557
558 def get_class(ifindex, parent, handle=None):
559     l = []
560
561     try:
562         cache = _class_cache[ifindex]
563     except KeyError:
564         cache = TcClassCache(ifindex)
565         _class_cache[ifindex] = cache
566
567     cache.refill()
568
569     for cl in cache:
570         if (parent is not None) and (cl.parent != parent):
571             continue
572         if (handle is not None) and (cl.handle != handle):
573             continue
574         l.append(cl)
575
576     return l
577
578 _cls_cache = {}
579
580 def get_cls(ifindex, parent, handle=None):
581
582     chain = _cls_cache.get(ifindex, dict())
583
584     try:
585         cache = chain[parent]
586     except KeyError:
587         cache = ClassifierCache(ifindex, parent)
588         chain[parent] = cache
589
590     cache.refill()
591
592     if handle is None:
593         return [ cls for cls in cache ]
594
595     return [ cls for cls in cache if cls.handle == handle ]