Added ability to filter and classify packets based on the containers interface.
[nodemanager.git] / bwlimit.py
1 #!/usr/bin/python
2
3 # Bandwidth limit module for PlanetLab nodes. The intent is to use the
4 # Hierarchical Token Bucket (HTB) queueing discipline (qdisc) to allow
5 # slices to fairly share access to available node bandwidth. We
6 # currently define three classes of "available node bandwidth":
7 #
8 # 1. Available hardware bandwidth (bwmax): The maximum rate of the
9 # hardware.
10 #
11 # 2. Available capped bandwidth (bwcap): The maximum rate allowed to
12 # non-exempt destinations. By default, equal to bwmax, but may be
13 # lowered by PIs.
14 #
15 # 3. Available uncapped ("exempt") bandwidth: The difference between
16 # bwmax and what is currently being used of bwcap, or the maximum rate
17 # allowed to destinations exempt from caps (e.g., Internet2).
18 #
19 # All three classes of bandwidth are fairly shared according to the
20 # notion of "shares". For instance, if the node is capped at 5 Mbps,
21 # there are N slices, and each slice has 1 share, then each slice
22 # should get at least 5/N Mbps of bandwidth. How HTB is implemented
23 # makes this statement a little too simplistic. What it really means
24 # is that during any single time period, only a certain number of
25 # bytes can be sent onto the wire. Each slice is guaranteed that at
26 # least some small number of its bytes will be sent. Whatever is left
27 # over from the budget, is split in proportion to the number of shares
28 # each slice has.
29 #
30 # Even if the node is not capped at a particular limit (bwcap ==
31 # bwmax), this module enforces fair share access to bwmax. Also, if
32 # the node is capped at a particular limit, rules may optionally be
33 # defined that classify certain packets into the "exempt" class. This
34 # class receives whatever bandwidth is leftover between bwcap and
35 # bwmax; slices fairly share this bandwidth as well.
36 #
37 # The root context is exempt from sharing and can send as much as it
38 # needs to.
39 #
40 # Some relevant URLs:
41 #
42 # 1. http://lartc.org/howto               for how to use tc
43 # 2. http://luxik.cdi.cz/~devik/qos/htb/  for info on HTB
44 #
45 # Andy Bavier <acb@cs.princeton.edu>
46 # Mark Huang <mlhuang@cs.princeton.edu>
47 # Copyright (C) 2006 The Trustees of Princeton University
48 #
49 # $Id: bwlimit.py,v 1.15 2007/02/07 04:21:11 mlhuang Exp $
50 #
51
52 import sys, os, re, getopt
53 import pwd
54
55
56 # Where the tc binary lives
57 TC = "/sbin/tc"
58
59 # Where the ebtables binary lives
60 EBTABLES = "/sbin/ebtables"
61
62 # Default interface
63 dev = "eth0"
64
65 # Verbosity level
66 verbose = 0
67
68 # bwmin should be small enough that it can be considered negligibly
69 # slow compared to the hardware. 8 bits/second appears to be the
70 # smallest value supported by tc.
71 bwmin = 1000
72
73 # bwmax should be large enough that it can be considered at least as
74 # fast as the hardware.
75 bwmax = 1000*1000*1000
76
77 # quantum is the maximum number of bytes that can be borrowed by a
78 # share (or slice, if each slice gets 1 share) in one time period
79 # (with HZ=1000, 1 ms). If multiple slices are competing for bandwidth
80 # above their guarantees, and each is attempting to borrow up to the
81 # node bandwidth cap, quantums control how the excess bandwidth is
82 # distributed. Slices with 2 shares will borrow twice the amount in
83 # one time period as slices with 1 share, so averaged over time, they
84 # will get twice as much of the excess bandwidth. The value should be
85 # as small as possible and at least 1 MTU. By default, it would be
86 # calculated as bwmin/10, but since we use such small a value for
87 # bwmin, it's better to just set it to a value safely above 1 Ethernet
88 # MTU.
89 quantum = 1600
90
91 # cburst is the maximum number of bytes that can be burst onto the
92 # wire in one time period (with HZ=1000, 1 ms). If multiple slices
93 # have data queued for transmission, cbursts control how long each
94 # slice can have the wire for. If not specified, it is set to the
95 # smallest possible value that would enable the slice's "ceil" rate
96 # (usually the node bandwidth cap), to be reached if a slice was able
97 # to borrow enough bandwidth to do so. For now, it's unclear how or if
98 # to relate this to the notion of shares, so just let tc set the
99 # default.
100 cburst = None
101
102 # There is another parameter that controls how bandwidth is allocated
103 # between slices on nodes that is outside the scope of HTB. We enforce
104 # a 16 GByte/day total limit on each slice, which works out to about
105 # 1.5mbit. If a slice exceeds this byte limit before the day finishes,
106 # it is capped at (i.e., its "ceil" rate is set to) the smaller of the
107 # node bandwidth cap or 1.5mbit. pl_mom is in charge of enforcing this
108 # rule and executes this script to override "ceil".
109
110 # We support multiple bandwidth limits, by reserving the top nibble of
111 # the minor classid to be the "subclassid". Theoretically, we could
112 # support up to 15 subclasses, but for now, we only define two: the
113 # "default" subclass 1:10 that is capped at the node bandwidth cap (in
114 # this example, 5mbit) and the "exempt" subclass 1:20 that is capped
115 # at bwmax (i.e., not capped). The 1:1 parent class exists only to
116 # make the borrowing model work. All bandwidth above minimum
117 # guarantees is fairly shared (in this example, slice 2 is guaranteed
118 # at least 1mbit in addition to fair access to the rest), subject to
119 # the restrictions of the class hierarchy: namely, that the total
120 # bandwidth to non-exempt destinations should not exceed the node
121 # bandwidth cap.
122 #
123 #                         1:
124 #                         |
125 #                    1:1 (1gbit)
126 #           ______________|_____________
127 #          |                            |
128 #   1:10 (8bit, 5mbit)           1:20 (8bit, 1gbit)
129 #          |                            |
130 #  1:100 (8bit, 5mbit)                  |
131 #          |                            |
132 # 1:1000 (8bit, 5mbit),        1:2000 (8bit, 1gbit),
133 # 1:1001 (8bit, 5mbit),        1:2001 (8bit, 1gbit),
134 # 1:1002 (1mbit, 5mbit),       1:2002 (1mbit, 1gbit),
135 # ...                          ...
136 # 1:1FFF (8bit, 5mbit)         1:2FFF (8bit, 1gbit)
137 #
138 default_minor = 0x1000
139 exempt_minor = 0x2000
140
141 # root_xid is for the root context. The root context is exempt from
142 # fair sharing in both the default and exempt subclasses. The root
143 # context gets 5 shares by default.
144 root_xid = 0x0000
145 root_share = 5
146
147 # default_xid is for unclassifiable packets. Packets should not be
148 # classified here very often. They can be if a slice's HTB classes are
149 # deleted before its processes are. Each slice gets 1 share by
150 # default.
151 default_xid = 0x0FFF
152 default_share = 1
153
154 # See tc_util.c and http://physics.nist.gov/cuu/Units/binary.html. Be
155 # warned that older versions of tc interpret "kbps", "mbps", "mbit",
156 # and "kbit" to mean (in this system) "kibps", "mibps", "mibit", and
157 # "kibit" and that if an older version is installed, all rates will
158 # be off by a small fraction.
159 suffixes = {
160     "":         1,
161     "bit":      1,
162     "kibit":    1024,
163     "kbit":     1000,
164     "mibit":    1024*1024,
165     "mbit":     1000000,
166     "gibit":    1024*1024*1024,
167     "gbit":     1000000000,
168     "tibit":    1024*1024*1024*1024,
169     "tbit":     1000000000000,
170     "bps":      8,
171     "kibps":    8*1024,
172     "kbps":     8000,
173     "mibps":    8*1024*1024,
174     "mbps":     8000000,
175     "gibps":    8*1024*1024*1024,
176     "gbps":     8000000000,
177     "tibps":    8*1024*1024*1024*1024,
178     "tbps":     8000000000000
179 }
180
181
182 def get_tc_rate(s):
183     """
184     Parses an integer or a tc rate string (e.g., 1.5mbit) into bits/second
185     """
186
187     if type(s) == int:
188         return s
189     m = re.match(r"([0-9.]+)(\D*)", s)
190     if m is None:
191         return -1
192     suffix = m.group(2).lower()
193     if suffixes.has_key(suffix):
194         return int(float(m.group(1)) * suffixes[suffix])
195     else:
196         return -1
197
198 def format_bytes(bytes, si = True):
199     """
200     Formats bytes into a string
201     """
202     if si:
203         kilo = 1000.
204     else:
205         # Officially, a kibibyte
206         kilo = 1024.
207
208     if bytes >= (kilo * kilo * kilo):
209         return "%.1f GB" % (bytes / (kilo * kilo * kilo))
210     elif bytes >= 1000000:
211         return "%.1f MB" % (bytes / (kilo * kilo))
212     elif bytes >= 1000:
213         return "%.1f KB" % (bytes / kilo)
214     else:
215         return "%.0f bytes" % bytes
216
217 def format_tc_rate(rate):
218     """
219     Formats a bits/second rate into a tc rate string
220     """
221
222     if rate >= 1000000000 and (rate % 1000000000) == 0:
223         return "%.0fgbit" % (rate / 1000000000.)
224     elif rate >= 1000000 and (rate % 1000000) == 0:
225         return "%.0fmbit" % (rate / 1000000.)
226     elif rate >= 1000:
227         return "%.0fkbit" % (rate / 1000.)
228     else:
229         return "%.0fbit" % rate
230
231
232 # Parse /etc/planetlab/bwcap (or equivalent)
233 def read_bwcap(bwcap_file):
234     bwcap = bwmax
235     try:
236         fp = open(bwcap_file, "r")
237         line = fp.readline().strip()
238         if line:
239             bwcap = get_tc_rate(line)
240     except:
241         pass
242     if bwcap == -1:
243         bwcap = bwmax
244     return bwcap
245
246
247 def get_bwcap(dev = dev):
248     """
249     Get the current (live) value of the node bandwidth cap
250     """
251
252     state = tc("-d class show dev %s" % dev)
253     base_re = re.compile(r"class htb 1:10 parent 1:1 .*ceil ([^ ]+) .*")
254     base_classes = filter(None, map(base_re.match, state))
255     if not base_classes:
256         return -1
257     if len(base_classes) > 1:
258         raise Exception, "unable to get current bwcap"
259     return get_tc_rate(base_classes[0].group(1))
260
261
262 def get_slice(xid):
263     """
264     Get slice name ("princeton_mlh") from slice xid (500)
265     """
266
267     if xid == root_xid:
268         return "root"
269     if xid == default_xid:
270         return "default"
271     try:
272         return pwd.getpwuid(xid).pw_name
273     except KeyError:
274         pass
275
276     return None
277
278 def get_xid(slice):
279     """
280     Get slice xid ("500") from slice name ("princeton_mlh")
281     """
282
283     if slice == "root":
284         return root_xid
285     if slice == "default":
286         return default_xid
287     try:
288         try:
289             return int(slice)
290         except ValueError:
291             pass
292         return pwd.getpwnam(slice).pw_uid
293     except KeyError:
294         pass
295
296     return None
297
298 def run(cmd, input = None):
299     """
300     Shortcut for running a shell command
301     """
302
303     try:
304         if verbose:
305             sys.stderr.write("Executing: " + cmd + "\n")
306         if input is None:
307             fileobj = os.popen(cmd, "r")
308             output = fileobj.readlines()
309         else:
310             fileobj = os.popen(cmd, "w")
311             fileobj.write(input)
312             output = None
313         if fileobj.close() is None:
314             return output
315     except Exception, e:
316         pass
317     return None
318
319
320 def tc(cmd):
321     """
322     Shortcut for running a tc command
323     """
324
325     return run(TC + " " + cmd)
326
327 def ebtables(cmd):
328     """
329     Shortcut for running a ebtables command
330     """
331
332     return run(EBTABLES + " " + cmd)
333
334
335 def stop(dev = dev):
336     '''
337     Turn off all queing.  Stops all slice HTBS and reverts to pfifo_fast (the default).
338     '''
339     try:
340         for i in range(0,2):
341             tc("qdisc del dev %s root" % dev)
342     except: pass
343
344
345 def init(dev = dev, bwcap = bwmax):
346     """
347     (Re)initialize the bandwidth limits on this node
348     """
349
350     # Load the module used to manage exempt classes
351     #run("/sbin/modprobe ip_set_iphash")
352     # Test the new module included in kernel 3 series
353     run("/sbin/modprobe ip_set_hash_ip")
354
355     # Save current settings
356     paramslist = get(None, dev)
357
358     # Delete root qdisc 1: if it exists. This will also automatically
359     # delete any child classes.
360     for line in tc("qdisc show dev %s" % dev):
361         # Search for the root qdisc 1:
362         m = re.match(r"qdisc htb 1:", line)
363         if m is not None:
364             tc("qdisc del dev %s root handle 1:" % dev)
365             break
366
367     # Initialize HTB. The "default" clause specifies that if a packet
368     # fails classification, it should go into the class with handle
369     # 1FFF.
370     tc("qdisc add dev %s root handle 1: htb default %x" % \
371        (dev, default_minor | default_xid))
372
373     # Set up a parent class from which all subclasses borrow.
374     tc("class add dev %s parent 1: classid 1:1 htb rate %dbit" % \
375        (dev, bwmax))
376
377     # Set up a subclass that represents the node bandwidth cap. We
378     # allow each slice to borrow up to this rate, so it is also
379     # usually the "ceil" rate for each slice.
380     tc("class add dev %s parent 1:1 classid 1:10 htb rate %dbit ceil %dbit" % \
381        (dev, bwmin, bwcap))
382
383     # Set up a subclass for DRL(Distributed Rate Limiting). 
384     # DRL will directly modify that subclass implementing the site limits.
385     tc("class add dev %s parent 1:10 classid 1:100 htb rate %dbit ceil %dbit" % \
386        (dev, bwmin, bwcap))
387
388
389     # Set up a subclass that represents "exemption" from the node
390     # bandwidth cap. Once the node bandwidth cap is reached, bandwidth
391     # to exempt destinations can still be fairly shared up to bwmax.
392     tc("class add dev %s parent 1:1 classid 1:20 htb rate %dbit ceil %dbit" % \
393        (dev, bwmin, bwmax))
394
395     # Set up the root class (and tell VNET what it is). Packets sent
396     # by root end up here and are capped at the node bandwidth
397     # cap.
398     #on(root_xid, dev, share = root_share)
399     #try:
400     #    file("/proc/sys/vnet/root_class", "w").write("%d" % ((1 << 16) | default_minor | root_xid))
401     #except:
402     #    pass
403
404     # Set up the default class. Packets that fail classification end
405     # up here.
406     on(default_xid, dev, share = default_share)
407
408     # Restore old settings
409     for (xid, share,
410          minrate, maxrate,
411          minexemptrate, maxexemptrate,
412          bytes, exemptbytes) in paramslist:
413         if xid not in (root_xid, default_xid):
414             on(xid, dev, share, minrate, maxrate, minexemptrate, maxexemptrate)
415
416
417 def get(xid = None, dev = dev):
418     """
419     Get the bandwidth limits and current byte totals for a
420     particular slice xid as a tuple (xid, share, minrate, maxrate,
421     minexemptrate, maxexemptrate, bytes, exemptbytes), or all classes
422     as a list of such tuples.
423     """
424
425     if xid is None:
426         ret = []
427     else:
428         ret = None
429
430     rates = {}
431     rate = None
432
433     # ...
434     # class htb 1:1000 parent 1:10 leaf 1000: prio 0 quantum 8000 rate 8bit ceil 10000Kbit ...
435     #  Sent 6851486 bytes 49244 pkt (dropped 0, overlimits 0 requeues 0)
436     # ...
437     # class htb 1:2000 parent 1:20 leaf 2000: prio 0 quantum 8000 rate 8bit ceil 1000Mbit ...
438     #  Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) 
439     # ...
440     for line in tc("-s -d class show dev %s" % dev):
441         # Rate parameter line
442         params = re.match(r"class htb 1:([0-9a-f]+) parent 1:(10|20)", line)
443         # Statistics line
444         stats = re.match(r".* Sent ([0-9]+) bytes", line)
445         # Another class
446         ignore = re.match(r"class htb", line)
447
448         if params is not None:
449             # Which class
450             if params.group(2) == "10":
451                 min = 'min'
452                 max = 'max'
453                 bytes = 'bytes'
454             else:
455                 min = 'minexempt'
456                 max = 'maxexempt'
457                 bytes = 'exemptbytes'
458
459             # Slice ID
460             id = int(params.group(1), 16) & 0x0FFF;
461
462             if rates.has_key(id):
463                 rate = rates[id]
464             else:
465                 rate = {'id': id}
466
467             # Parse share
468             rate['share'] = 1
469             m = re.search(r"quantum (\d+)", line)
470             if m is not None:
471                 rate['share'] = int(m.group(1)) / quantum
472
473             # Parse minrate
474             rate[min] = bwmin
475             m = re.search(r"rate (\w+)", line)
476             if m is not None:
477                 rate[min] = get_tc_rate(m.group(1))
478
479             # Parse maxrate
480             rate[max] = bwmax
481             m = re.search(r"ceil (\w+)", line)
482             if m is not None:
483                 rate[max] = get_tc_rate(m.group(1))
484
485             # Which statistics to parse
486             rate['stats'] = bytes
487
488             rates[id] = rate
489
490         elif stats is not None:
491             if rate is not None:
492                 rate[rate['stats']] = int(stats.group(1))
493
494         elif ignore is not None:
495             rate = None
496
497         # Keep parsing until we get everything
498         if rate is not None and \
499            rate.has_key('min') and rate.has_key('minexempt') and \
500            rate.has_key('max') and rate.has_key('maxexempt') and \
501            rate.has_key('bytes') and rate.has_key('exemptbytes'):
502             params = (rate['id'], rate['share'],
503                       rate['min'], rate['max'],
504                       rate['minexempt'], rate['maxexempt'],
505                       rate['bytes'], rate['exemptbytes'])
506             if xid is None:
507                 # Return a list of parameters
508                 ret.append(params)
509                 rate = None
510             elif xid == rate['id']:
511                 # Return the parameters for this class
512                 ret = params
513                 break
514
515     return ret
516
517
518 def on(xid, dev = dev, share = None, minrate = None, maxrate = None, minexemptrate = None, maxexemptrate = None):
519     """
520     Apply specified bandwidth limit to the specified slice xid
521     """
522
523     # Get defaults from current state if available
524     cap = get(xid, dev)
525     if cap is not None:
526         if share is None:
527             share = cap[1]
528         if minrate is None:
529             minrate = cap[2]
530         if maxrate is None:
531             maxrate = cap[3]
532         if minexemptrate is None:
533             minexemptrate = cap[4]
534         if maxexemptrate is None:
535             maxexemptrate = cap[5]
536
537     # Figure out what the current node bandwidth cap is
538     bwcap = get_bwcap(dev)
539
540     # Set defaults
541     if share is None:
542         share = default_share
543     if minrate is None:
544         minrate = bwmin
545     else:
546         minrate = get_tc_rate(minrate)
547     if maxrate is None:
548         maxrate = bwcap
549     else:
550         maxrate = get_tc_rate(maxrate)
551     if minexemptrate is None:
552         minexemptrate = minrate
553     else:
554         minexemptrate = get_tc_rate(minexemptrate)
555     if maxexemptrate is None:
556         maxexemptrate = bwmax
557     else:
558         maxexemptrate = get_tc_rate(maxexemptrate)
559
560     # Sanity checks
561     if maxrate < bwmin:
562         maxrate = bwmin
563     if maxrate > bwcap:
564         maxrate = bwcap
565     if minrate < bwmin:
566         minrate = bwmin
567     if minrate > maxrate:
568         minrate = maxrate
569     if maxexemptrate < bwmin:
570         maxexemptrate = bwmin
571     if maxexemptrate > bwmax:
572         maxexemptrate = bwmax
573     if minexemptrate < bwmin:
574         minexemptrate = bwmin
575     if minexemptrate > maxexemptrate:
576         minexemptrate = maxexemptrate
577
578     # Set up subclasses for the slice
579     tc("class replace dev %s parent 1:100 classid 1:%x htb rate %dbit ceil %dbit quantum %d" % \
580        (dev, default_minor | xid, minrate, maxrate, share * quantum))
581
582     tc("class replace dev %s parent 1:20 classid 1:%x htb rate %dbit ceil %dbit quantum %d" % \
583        (dev, exempt_minor | xid, minexemptrate, maxexemptrate, share * quantum))
584     
585     # Attach a FIFO to each subclass, which helps to throttle back
586     # processes that are sending faster than the token buckets can
587     # support.
588     tc("qdisc replace dev %s parent 1:%x handle %x pfifo" % \
589        (dev, default_minor | xid, default_minor | xid))
590
591     tc("qdisc replace dev %s parent 1:%x handle %x pfifo" % \
592        (dev, exempt_minor | xid, exempt_minor | xid))
593
594     # Setup a filter rule to the root class so each packet originated by a
595     # container interface is classified to it corresponding class
596     # The handle number is a mark created by ebtables with the xid
597     tc("filter replace dev %s parent 1:1 protocol ip prio 1 handle %d fw flowid 1:%x" % \
598         (dev, default_minor | xid, default_minor | xid))
599
600     # Create the ebtables rule to mark the packets going out from the virtual
601     # interface to the actual device so the filter canmatch against the mark
602     # We remove and readd the rule because this method is called each time the
603     # bandwidth limit is changed
604     ebtables("-D INPUT -i veth%d -j mark --set-mark %d" % \
605         (xid, default_minor | xid))
606     ebtables("-A INPUT -i veth%d -j mark --set-mark %d" % \
607         (xid, default_minor | xid))
608
609
610 def set(xid, share = None, minrate = None, maxrate = None, minexemptrate = None, maxexemptrate = None, dev = dev ):
611     on(xid = xid, dev = dev, share = share,
612        minrate = minrate, maxrate = maxrate,
613        minexemptrate = minexemptrate, maxexemptrate = maxexemptrate)
614
615
616 # Remove class associated with specified slice xid. If further packets
617 # are seen from this slice, they will be classified into the default
618 # class 1:1FFF.
619 def off(xid, dev = dev):
620     """
621     Remove class associated with specified slice xid. If further
622     packets are seen from this slice, they will be classified into the
623     default class 1:1FFF.
624     """
625
626     cap = get(xid, dev)
627     if cap is not None:
628         tc("class del dev %s classid 1:%x" % (dev, default_minor | xid))
629         tc("class del dev %s classid 1:%x" % (dev, exempt_minor | xid))
630
631
632 def exempt_init(group_name, node_ips):
633     """
634     Initialize the list of destinations exempt from the node bandwidth
635     (burst) cap.
636     """
637
638     # Check of set exists
639     set = run("/sbin/ipset -S " + group_name)
640     if set == None:
641         # Create a hashed IP set of all of these destinations
642         lines = ["-N %s iphash" % group_name]
643         add_cmd = "-A %s " % group_name
644         lines += [(add_cmd + ip) for ip in node_ips]
645         lines += ["COMMIT"]
646         restore = "\n".join(lines) + "\n"
647         run("/sbin/ipset -R", restore)
648     else: # set exists
649         # Check all hosts and add missing.
650         for nodeip in node_ips:
651             if not run("/sbin/ipset -T %s %s" % (group_name, nodeip)):
652                run("/sbin/ipset -A %s %s" % (group_name, nodeip))
653
654
655 def usage():
656     bwcap_description = format_tc_rate(get_bwcap())
657         
658     print """
659 Usage:
660
661 %s [OPTION]... [COMMAND] [ARGUMENT]...
662
663 Options:
664         -d device   Network interface (default: %s)
665         -r rate         Node bandwidth cap (default: %s)
666         -q quantum      Share multiplier (default: %d bytes)
667         -n              Print rates in numeric bits per second
668         -v              Enable verbose debug messages
669         -h              This message
670
671 Commands:
672         init
673                 (Re)initialize all bandwidth parameters
674         on slice [share|-] [minrate|-] [maxrate|-] [minexemptrate|-] [maxexemptrate|-]
675                 Set bandwidth parameter(s) for the specified slice
676         off slice
677                 Remove all bandwidth parameters for the specified slice
678         get
679                 Get all bandwidth parameters for all slices
680         get slice
681                 Get bandwidth parameters for the specified slice
682 """ % (sys.argv[0], dev, bwcap_description, quantum)
683     sys.exit(1)
684     
685
686 def main():
687     global dev, quantum, verbose
688
689     # Defaults
690     numeric = False
691     bwcap = None
692
693     (opts, argv) = getopt.getopt(sys.argv[1:], "d:nr:q:vh")
694     for (opt, optval) in opts:
695         if opt == '-d':
696             dev = optval
697         elif opt == '-n':
698             numeric = True
699         elif opt == '-r':
700             bwcap = get_tc_rate(optval)
701         elif opt == '-q':
702             quantum = int(optval)
703         elif opt == '-v':
704             verbose += 1
705         elif opt == '-h':
706             usage()
707
708     if not bwcap:
709         bwcap = get_bwcap(dev)
710
711     if bwcap == -1:
712         return 0
713
714     if len(argv):
715         if argv[0] == "init" or (argv[0] == "on" and len(argv) == 1):
716             # (Re)initialize
717             init(dev, get_tc_rate(bwcap))
718
719         elif argv[0] == "get" or argv[0] == "show":
720             # Show
721             if len(argv) >= 2:
722                 # Show a particular slice
723                 xid = get_xid(argv[1])
724                 if xid is None:
725                     sys.stderr.write("Error: Invalid slice name or context '%s'\n" % argv[1])
726                     usage()
727                 params = get(xid, dev)
728                 if params is None:
729                     paramslist = []
730                 else:
731                     paramslist = [params]
732             else:
733                 # Show all slices
734                 paramslist = get(None, dev)
735
736             for (xid, share,
737                  minrate, maxrate,
738                  minexemptrate, maxexemptrate,
739                  bytes, exemptbytes) in paramslist:
740                 slice = get_slice(xid)
741                 if slice is None:
742                     # Orphaned (not associated with a slice) class
743                     slice = "%d?" % xid
744                 if numeric:
745                     print "%s %d %d %d %d %d %d %d" % \
746                           (slice, share,
747                            minrate, maxrate,
748                            minexemptrate, maxexemptrate,
749                            bytes, exemptbytes)
750                 else:
751                     print "%s %d %s %s %s %s %s %s" % \
752                           (slice, share,
753                            format_tc_rate(minrate), format_tc_rate(maxrate),
754                            format_tc_rate(minexemptrate), format_tc_rate(maxexemptrate),
755                            format_bytes(bytes), format_bytes(exemptbytes))
756
757         elif len(argv) >= 2:
758             # slice, ...
759             xid = get_xid(argv[1])
760             if xid is None:
761                 sys.stderr.write("Error: Invalid slice name or context '%s'\n" % argv[1])
762                 usage()
763
764             if argv[0] == "on" or argv[0] == "add" or argv[0] == "replace" or argv[0] == "set":
765                 # Enable cap
766                 args = []
767                 if len(argv) >= 3:
768                     # ... share, minrate, maxrate, minexemptrate, maxexemptrate
769                     casts = [int, get_tc_rate, get_tc_rate, get_tc_rate, get_tc_rate]
770                     for i, arg in enumerate(argv[2:]):
771                         if i >= len(casts):
772                             break
773                         if arg == "-":
774                             args.append(None)
775                         else:
776                             args.append(casts[i](arg))
777                 on(xid, dev, *args)
778
779             elif argv[0] == "off" or argv[0] == "del":
780                 # Disable cap
781                 off(xid, dev)
782
783             else:
784                 usage()
785
786         else:
787             usage()
788
789
790 if __name__ == '__main__':
791     main()