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