Tweaks
[nodemanager.git] / bwmon.py
index ccde6fe..91a79f3 100644 (file)
--- a/bwmon.py
+++ b/bwmon.py
@@ -14,7 +14,7 @@
 # Faiyaz Ahmed <faiyaza@cs.princeton.edu>
 # Copyright (C) 2004-2008 The Trustees of Princeton University
 #
 # Faiyaz Ahmed <faiyaza@cs.princeton.edu>
 # Copyright (C) 2004-2008 The Trustees of Princeton University
 #
-# $Id: bwmon.py,v 1.1.2.11 2007/06/26 18:03:55 faiyaza Exp $
+# $Id$
 #
 
 import os
 #
 
 import os
@@ -32,39 +32,50 @@ import database
 
 from sets import Set
 
 
 from sets import Set
 
+# Defaults
+# Set DEBUG to True if you don't want to send emails
+DEBUG = False
+# Set ENABLE to False to setup buckets, but not limit.
+ENABLE = True
+
+datafile = "/var/lib/misc/bwmon.dat"
+
 try:
     sys.path.append("/etc/planetlab")
     from plc_config import *
 except:
 try:
     sys.path.append("/etc/planetlab")
     from plc_config import *
 except:
-    logger.log("bwmon:  Warning: Configuration file /etc/planetlab/plc_config.py not found")
-    PLC_NAME = "PlanetLab"
-    PLC_SLICE_PREFIX = "pl"
-    PLC_MAIL_SUPPORT_ADDRESS = "support@planet-lab.org"
-    PLC_MAIL_SLICE_ADDRESS = "SLICE@slices.planet-lab.org"
+    DEBUG = True
+    logger.log("bwmon:  Warning: Configuration file /etc/planetlab/plc_config.py not found", 2)
+    logger.log("bwmon:  Running in DEBUG mode.  Logging to file and not emailing.", 1)
 
 # Constants
 seconds_per_day = 24 * 60 * 60
 bits_per_byte = 8
 
 
 # Constants
 seconds_per_day = 24 * 60 * 60
 bits_per_byte = 8
 
-# Defaults
-debug = False
-verbose = False
-datafile = "/var/lib/misc/bwmon.dat"
-#nm = None
-
+dev_default = tools.get_default_if()
 # Burst to line rate (or node cap).  Set by NM. in KBit/s
 # Burst to line rate (or node cap).  Set by NM. in KBit/s
-default_MaxRate = int(bwlimit.get_bwcap() / 1000)
+default_MaxRate = int(bwlimit.get_bwcap(dev_default) / 1000)
 default_Maxi2Rate = int(bwlimit.bwmax / 1000)
 default_Maxi2Rate = int(bwlimit.bwmax / 1000)
-# Min rate 8 bits/s 
-default_MinRate = 0
-default_Mini2Rate = 0
 # 5.4 Gbyte per day. 5.4 * 1024 k * 1024M * 1024G 
 # 5.4 Gbyte per day max allowed transfered per recording period
 # 5.4 Gbyte per day. 5.4 * 1024 k * 1024M * 1024G 
 # 5.4 Gbyte per day max allowed transfered per recording period
-default_MaxKByte = 5662310
-default_ThreshKByte = int(.8 * default_MaxKByte) 
+# 5.4 Gbytes per day is aprox 512k/s for 24hrs (approx because original math was wrong
+# but its better to keep a higher byte total and keep people happy than correct
+# the problem and piss people off.
+# default_MaxKByte = 5662310
+
+# -- 6/1/09 
+# llp wants to double these, so we use the following
+# 1mbit * 24hrs * 60mins * 60secs = bits/day
+# 1000000 * 24 * 60 * 60 / (1024 * 8)
+default_MaxKByte = 10546875
+
 # 16.4 Gbyte per day max allowed transfered per recording period to I2
 # 16.4 Gbyte per day max allowed transfered per recording period to I2
-default_Maxi2KByte = 17196646
-default_Threshi2KByte = int(.8 * default_Maxi2KByte) 
+# default_Maxi2KByte = 17196646
+
+# -- 6/1/09
+# 3Mb/s for 24hrs a day (30.17 gigs)
+default_Maxi2KByte = 31640625
+
 # Default share quanta
 default_Share = 1
 
 # Default share quanta
 default_Share = 1
 
@@ -135,11 +146,8 @@ def slicemail(slice, subject, body):
 
     sendmail = os.popen("/usr/sbin/sendmail -N never -t -f%s" % PLC_MAIL_SUPPORT_ADDRESS, "w")
 
 
     sendmail = os.popen("/usr/sbin/sendmail -N never -t -f%s" % PLC_MAIL_SUPPORT_ADDRESS, "w")
 
-    # PLC has a separate list for pl_mom messages
-    if PLC_MAIL_SUPPORT_ADDRESS == "support@planet-lab.org":
-        to = ["pl-mom@planet-lab.org"]
-    else:
-        to = [PLC_MAIL_SUPPORT_ADDRESS]
+    # Parsed from MyPLC config
+    to = [PLC_MAIL_MOM_LIST_ADDRESS]
 
     if slice is not None and slice != "root":
         to.append(PLC_MAIL_SLICE_ADDRESS.replace("SLICE", slice))
 
     if slice is not None and slice != "root":
         to.append(PLC_MAIL_SLICE_ADDRESS.replace("SLICE", slice))
@@ -195,18 +203,19 @@ class Slice:
         self.bytes = 0
         self.i2bytes = 0
         self.MaxRate = default_MaxRate
         self.bytes = 0
         self.i2bytes = 0
         self.MaxRate = default_MaxRate
-        self.MinRate = default_MinRate 
+        self.MinRate = bwlimit.bwmin / 1000
         self.Maxi2Rate = default_Maxi2Rate
         self.Maxi2Rate = default_Maxi2Rate
-        self.Mini2Rate = default_Mini2Rate
+        self.Mini2Rate = bwlimit.bwmin / 1000
         self.MaxKByte = default_MaxKByte
         self.MaxKByte = default_MaxKByte
-        self.ThreshKByte = default_ThreshKByte
+        self.ThreshKByte = int(.8 * self.MaxKByte)
         self.Maxi2KByte = default_Maxi2KByte
         self.Maxi2KByte = default_Maxi2KByte
-        self.Threshi2KByte = default_Threshi2KByte
+        self.Threshi2KByte = int(.8 * self.Maxi2KByte)
         self.Share = default_Share
         self.Sharei2 = default_Share
         self.emailed = False
         self.Share = default_Share
         self.Sharei2 = default_Share
         self.emailed = False
+        self.capped = False
 
 
-        self.updateSliceAttributes(rspec)
+        self.updateSliceTags(rspec)
         bwlimit.set(xid = self.xid, 
                 minrate = self.MinRate * 1000, 
                 maxrate = self.MaxRate * 1000, 
         bwlimit.set(xid = self.xid, 
                 minrate = self.MinRate * 1000, 
                 maxrate = self.MaxRate * 1000, 
@@ -217,7 +226,7 @@ class Slice:
     def __repr__(self):
         return self.name
 
     def __repr__(self):
         return self.name
 
-    def updateSliceAttributes(self, rspec):
+    def updateSliceTags(self, rspec):
         '''
         Use respects from GetSlivers to PLC to populate slice object.  Also
         do some sanity checking.
         '''
         Use respects from GetSlivers to PLC to populate slice object.  Also
         do some sanity checking.
@@ -225,24 +234,24 @@ class Slice:
 
         # Sanity check plus policy decision for MinRate:
         # Minrate cant be greater than 25% of MaxRate or NodeCap.
 
         # Sanity check plus policy decision for MinRate:
         # Minrate cant be greater than 25% of MaxRate or NodeCap.
-        MinRate = int(rspec.get("net_min_rate", default_MinRate))
+        MinRate = int(rspec.get("net_min_rate", bwlimit.bwmin / 1000))
         if MinRate > int(.25 * default_MaxRate):
             MinRate = int(.25 * default_MaxRate)
         if MinRate != self.MinRate:
             self.MinRate = MinRate
             logger.log("bwmon:  Updating %s: Min Rate = %s" %(self.name, self.MinRate))
 
         if MinRate > int(.25 * default_MaxRate):
             MinRate = int(.25 * default_MaxRate)
         if MinRate != self.MinRate:
             self.MinRate = MinRate
             logger.log("bwmon:  Updating %s: Min Rate = %s" %(self.name, self.MinRate))
 
-        MaxRate = int(rspec.get('net_max_rate', bwlimit.get_bwcap() / 1000))
+        MaxRate = int(rspec.get('net_max_rate', default_MaxRate))
         if MaxRate != self.MaxRate:
             self.MaxRate = MaxRate
             logger.log("bwmon:  Updating %s: Max Rate = %s" %(self.name, self.MaxRate))
 
         if MaxRate != self.MaxRate:
             self.MaxRate = MaxRate
             logger.log("bwmon:  Updating %s: Max Rate = %s" %(self.name, self.MaxRate))
 
-        Mini2Rate = int(rspec.get('net_i2_min_rate', default_Mini2Rate))
+        Mini2Rate = int(rspec.get('net_i2_min_rate', bwlimit.bwmin / 1000))
         if Mini2Rate != self.Mini2Rate:
             self.Mini2Rate = Mini2Rate 
             logger.log("bwmon:  Updating %s: Min i2 Rate = %s" %(self.name, self.Mini2Rate))
 
         if Mini2Rate != self.Mini2Rate:
             self.Mini2Rate = Mini2Rate 
             logger.log("bwmon:  Updating %s: Min i2 Rate = %s" %(self.name, self.Mini2Rate))
 
-        Maxi2Rate = int(rspec.get('net_i2_max_rate', bwlimit.bwmax / 1000))
+        Maxi2Rate = int(rspec.get('net_i2_max_rate', default_Maxi2Rate))
         if Maxi2Rate != self.Maxi2Rate:
             self.Maxi2Rate = Maxi2Rate
             logger.log("bwmon:  Updating %s: Max i2 Rate = %s" %(self.name, self.Maxi2Rate))
         if Maxi2Rate != self.Maxi2Rate:
             self.Maxi2Rate = Maxi2Rate
             logger.log("bwmon:  Updating %s: Max i2 Rate = %s" %(self.name, self.Maxi2Rate))
@@ -257,12 +266,12 @@ class Slice:
             self.Maxi2KByte = Maxi2KByte
             logger.log("bwmon:  Updating %s: Max i2 KByte = %s" %(self.name, self.Maxi2KByte))
                           
             self.Maxi2KByte = Maxi2KByte
             logger.log("bwmon:  Updating %s: Max i2 KByte = %s" %(self.name, self.Maxi2KByte))
                           
-        ThreshKByte = int(rspec.get('net_thresh_kbyte', default_ThreshKByte))
+        ThreshKByte = int(rspec.get('net_thresh_kbyte', (MaxKByte * .8)))
         if ThreshKByte != self.ThreshKByte:
             self.ThreshKByte = ThreshKByte
             logger.log("bwmon:  Updating %s: Thresh KByte = %s" %(self.name, self.ThreshKByte))
                           
         if ThreshKByte != self.ThreshKByte:
             self.ThreshKByte = ThreshKByte
             logger.log("bwmon:  Updating %s: Thresh KByte = %s" %(self.name, self.ThreshKByte))
                           
-        Threshi2KByte = int(rspec.get('net_i2_thresh_kbyte', default_Threshi2KByte))
+        Threshi2KByte = int(rspec.get('net_i2_thresh_kbyte', (Maxi2KByte * .8)))
         if Threshi2KByte != self.Threshi2KByte:    
             self.Threshi2KByte = Threshi2KByte
             logger.log("bwmon:  Updating %s: i2 Thresh KByte = %s" %(self.name, self.Threshi2KByte))
         if Threshi2KByte != self.Threshi2KByte:    
             self.Threshi2KByte = Threshi2KByte
             logger.log("bwmon:  Updating %s: i2 Thresh KByte = %s" %(self.name, self.Threshi2KByte))
@@ -278,126 +287,163 @@ class Slice:
             logger.log("bwmon:  Updating %s: Net i2 Share = %s" %(self.name, self.i2Share))
 
 
             logger.log("bwmon:  Updating %s: Net i2 Share = %s" %(self.name, self.i2Share))
 
 
-    def reset(self, runningmaxrate, runningmaxi2rate, usedbytes, usedi2bytes, rspec):
+    def reset(self, runningrates, rspec):
         """
         Begin a new recording period. Remove caps by restoring limits
         to their default values.
         """
         """
         Begin a new recording period. Remove caps by restoring limits
         to their default values.
         """
-        
+        # Cache share for later comparison
+        self.Share = runningrates.get('share', 1)
+
         # Query Node Manager for max rate overrides
         # Query Node Manager for max rate overrides
-        self.updateSliceAttributes(rspec)    
+        self.updateSliceTags(rspec)    
 
         # Reset baseline time
         self.time = time.time()
 
         # Reset baseline byte coutns
 
         # Reset baseline time
         self.time = time.time()
 
         # Reset baseline byte coutns
-        self.bytes = usedbytes
-        self.i2bytes = usedi2bytes
+        self.bytes = runningrates.get('usedbytes', 0)
+        self.i2bytes = runningrates.get('usedi2bytes', 0)
 
         # Reset email 
         self.emailed = False
 
         # Reset email 
         self.emailed = False
-        maxrate = self.MaxRate * 1000 
-        maxi2rate = self.Maxi2Rate * 1000 
+        # Reset flag
+        self.capped = False
         # Reset rates.
         # Reset rates.
-        if (self.MaxRate != runningmaxrate) or (self.Maxi2Rate != runningmaxi2rate):
+        maxrate = self.MaxRate * 1000 
+        minrate = self.MinRate * 1000 
+        maxi2rate = self.Maxi2Rate * 1000
+        mini2rate = self.Mini2Rate * 1000
+
+        if (maxrate != runningrates.get('maxrate', 0)) or \
+         (minrate != runningrates.get('maxrate', 0)) or \
+         (maxi2rate != runningrates.get('maxexemptrate', 0)) or \
+         (mini2rate != runningrates.get('minexemptrate', 0)) or \
+         (self.Share != runningrates.get('share', 0)):
             logger.log("bwmon:  %s reset to %s/%s" % \
                   (self.name,
                    bwlimit.format_tc_rate(maxrate),
             logger.log("bwmon:  %s reset to %s/%s" % \
                   (self.name,
                    bwlimit.format_tc_rate(maxrate),
-                   bwlimit.format_tc_rate(maxi2rate)))
-            bwlimit.set(xid = self.xid, 
+                   bwlimit.format_tc_rate(maxi2rate)), 1)
+            bwlimit.set(xid = self.xid, dev = dev_default,
                 minrate = self.MinRate * 1000, 
                 maxrate = self.MaxRate * 1000, 
                 maxexemptrate = self.Maxi2Rate * 1000,
                 minexemptrate = self.Mini2Rate * 1000,
                 share = self.Share)
 
                 minrate = self.MinRate * 1000, 
                 maxrate = self.MaxRate * 1000, 
                 maxexemptrate = self.Maxi2Rate * 1000,
                 minexemptrate = self.Mini2Rate * 1000,
                 share = self.Share)
 
-    def update(self, runningmaxrate, runningmaxi2rate, usedbytes, usedi2bytes, rspec):
+    def notify(self, new_maxrate, new_maxexemptrate, usedbytes, usedi2bytes):
         """
         """
-        Update byte counts and check if byte thresholds have been
-        exceeded. If exceeded, cap to  remaining bytes in limit over remaining in period.  
-        Recalculate every time module runs.
+        Notify the slice it's being capped.
         """
         """
-    
-        # Query Node Manager for max rate overrides
-        self.updateSliceAttributes(rspec)    
-     
-        # Prepare message parameters from the template
+         # Prepare message parameters from the template
         message = ""
         params = {'slice': self.name, 'hostname': socket.gethostname(),
                   'since': time.asctime(time.gmtime(self.time)) + " GMT",
                   'until': time.asctime(time.gmtime(self.time + period)) + " GMT",
                   'date': time.asctime(time.gmtime()) + " GMT",
         message = ""
         params = {'slice': self.name, 'hostname': socket.gethostname(),
                   'since': time.asctime(time.gmtime(self.time)) + " GMT",
                   'until': time.asctime(time.gmtime(self.time + period)) + " GMT",
                   'date': time.asctime(time.gmtime()) + " GMT",
-                  'period': format_period(period)} 
+                  'period': format_period(period)}
+
+        if new_maxrate != (self.MaxRate * 1000):
+            # Format template parameters for low bandwidth message
+            params['class'] = "low bandwidth"
+            params['bytes'] = format_bytes(usedbytes - self.bytes)
+            params['limit'] = format_bytes(self.MaxKByte * 1024)
+            params['new_maxrate'] = bwlimit.format_tc_rate(new_maxrate)
+
+            # Cap low bandwidth burst rate
+            message += template % params
+            logger.log("bwmon:   ** %(slice)s %(class)s capped at %(new_maxrate)s/s " % params)
 
 
+        if new_maxexemptrate != (self.Maxi2Rate * 1000):
+            # Format template parameters for high bandwidth message
+            params['class'] = "high bandwidth"
+            params['bytes'] = format_bytes(usedi2bytes - self.i2bytes)
+            params['limit'] = format_bytes(self.Maxi2KByte * 1024)
+            params['new_maxrate'] = bwlimit.format_tc_rate(new_maxexemptrate)
+            message += template % params
+            logger.log("bwmon:   ** %(slice)s %(class)s capped at %(new_maxrate)s/s " % params)
+       
+        # Notify slice
+        if self.emailed == False:
+            subject = "pl_mom capped bandwidth of slice %(slice)s on %(hostname)s" % params
+            if DEBUG:
+                logger.log("bwmon:  "+ subject)
+                logger.log("bwmon:  "+ message + (footer % params))
+            else:
+                self.emailed = True
+                logger.log("bwmon:  Emailing %s" % self.name)
+                slicemail(self.name, subject, message + (footer % params))
+
+
+    def update(self, runningrates, rspec):
+        """
+        Update byte counts and check if byte thresholds have been
+        exceeded. If exceeded, cap to remaining bytes in limit over remaining time in period.  
+        Recalculate every time module runs.
+        """
+        # cache share for later comparison
+        runningrates['share'] = self.Share
+
+        # Query Node Manager for max rate overrides
+        self.updateSliceTags(rspec)    
+
+        usedbytes = runningrates['usedbytes']
+        usedi2bytes = runningrates['usedi2bytes']
+
+        # Check limits.
         if usedbytes >= (self.bytes + (self.ThreshKByte * 1024)):
         if usedbytes >= (self.bytes + (self.ThreshKByte * 1024)):
-            if verbose:
-                logger.log("bwmon: %s over thresh %s" \
-                  % (self.name, format_bytes(self.ThreshKByte * 1024)))
             sum = self.bytes + (self.ThreshKByte * 1024)
             maxbyte = self.MaxKByte * 1024
             bytesused = usedbytes - self.bytes
             timeused = int(time.time() - self.time)
             sum = self.bytes + (self.ThreshKByte * 1024)
             maxbyte = self.MaxKByte * 1024
             bytesused = usedbytes - self.bytes
             timeused = int(time.time() - self.time)
+            # Calcuate new rate. in bit/s
             new_maxrate = int(((maxbyte - bytesused) * 8)/(period - timeused))
             new_maxrate = int(((maxbyte - bytesused) * 8)/(period - timeused))
+            # Never go under MinRate
             if new_maxrate < (self.MinRate * 1000):
                 new_maxrate = self.MinRate * 1000
             if new_maxrate < (self.MinRate * 1000):
                 new_maxrate = self.MinRate * 1000
+            # State information.  I'm capped.
+            self.capped += True
         else:
         else:
-            new_maxrate = self.MaxRate * 1000 
-
-        # Format template parameters for low bandwidth message
-        params['class'] = "low bandwidth"
-        params['bytes'] = format_bytes(usedbytes - self.bytes)
-        params['limit'] = format_bytes(self.MaxKByte * 1024)
-        params['thresh'] = format_bytes(self.ThreshKByte * 1024)
-        params['new_maxrate'] = bwlimit.format_tc_rate(new_maxrate)
-
-        if verbose:
-            logger.log("bwmon:  %(slice)s %(class)s " \
-                  "%(bytes)s of %(limit)s max %(thresh)s thresh (%(new_maxrate)s/s maxrate)" % \
-                  params)
-
-        # Cap low bandwidth burst rate
-        if new_maxrate != runningmaxrate:
-            message += template % params
-            logger.log("bwmon:   ** %(slice)s %(class)s capped at %(new_maxrate)s/s " % params)
-    
+            # Sanity Check
+            new_maxrate = self.MaxRate * 1000
+            self.capped += False
         if usedi2bytes >= (self.i2bytes + (self.Threshi2KByte * 1024)):
             maxi2byte = self.Maxi2KByte * 1024
             i2bytesused = usedi2bytes - self.i2bytes
             timeused = int(time.time() - self.time)
         if usedi2bytes >= (self.i2bytes + (self.Threshi2KByte * 1024)):
             maxi2byte = self.Maxi2KByte * 1024
             i2bytesused = usedi2bytes - self.i2bytes
             timeused = int(time.time() - self.time)
+            # Calcuate New Rate.
             new_maxi2rate = int(((maxi2byte - i2bytesused) * 8)/(period - timeused))
             new_maxi2rate = int(((maxi2byte - i2bytesused) * 8)/(period - timeused))
+            # Never go under MinRate
             if new_maxi2rate < (self.Mini2Rate * 1000):
                 new_maxi2rate = self.Mini2Rate * 1000
             if new_maxi2rate < (self.Mini2Rate * 1000):
                 new_maxi2rate = self.Mini2Rate * 1000
+            # State information.  I'm capped.
+            self.capped += True
         else:
         else:
+            # Sanity
             new_maxi2rate = self.Maxi2Rate * 1000
             new_maxi2rate = self.Maxi2Rate * 1000
-
-        # Format template parameters for high bandwidth message
-        params['class'] = "high bandwidth"
-        params['bytes'] = format_bytes(usedi2bytes - self.i2bytes)
-        params['limit'] = format_bytes(self.Maxi2KByte * 1024)
-        params['new_maxexemptrate'] = bwlimit.format_tc_rate(new_maxi2rate)
-
-        if verbose:
-            logger.log("bwmon:  %(slice)s %(class)s " \
-                  "%(bytes)s of %(limit)s (%(new_maxrate)s/s maxrate)" % params)
-
-        # Cap high bandwidth burst rate
-        if new_maxi2rate != runningmaxi2rate:
-            message += template % params
-            logger.log("bwmon:  %(slice)s %(class)s capped at %(new_maxexemptrate)s/s" % params)
-
-        # Apply parameters
-        if new_maxrate != runningmaxrate or new_maxi2rate != runningmaxi2rate:
-            bwlimit.set(xid = self.xid, maxrate = new_maxrate, maxexemptrate = new_maxi2rate)
+            self.capped += False
+
+        # Check running values against newly calculated values so as not to run tc
+        # unnecessarily
+        if (runningrates['maxrate'] != new_maxrate) or \
+        (runningrates['minrate'] != self.MinRate * 1000) or \
+        (runningrates['maxexemptrate'] != new_maxi2rate) or \
+        (runningrates['minexemptrate'] != self.Mini2Rate * 1000) or \
+        (runningrates['share'] != self.Share):
+            # Apply parameters
+            bwlimit.set(xid = self.xid, 
+                minrate = self.MinRate * 1000, 
+                maxrate = new_maxrate,
+                minexemptrate = self.Mini2Rate * 1000,
+                maxexemptrate = new_maxi2rate,
+                share = self.Share)
 
         # Notify slice
 
         # Notify slice
-        if message and self.emailed == False:
-            subject = "pl_mom capped bandwidth of slice %(slice)s on %(hostname)s" % params
-            if debug:
-                logger.log("bwmon:  "+ subject)
-                logger.log("bwmon:  "+ message + (footer % params))
-            else:
-                self.emailed = True
-                slicemail(self.name, subject, message + (footer % params))
+        if self.capped == True:
+            self.notify(new_maxrate, new_maxi2rate, usedbytes, usedi2bytes)
+
 
 def gethtbs(root_xid, default_xid):
     """
 
 def gethtbs(root_xid, default_xid):
     """
@@ -418,7 +464,7 @@ def gethtbs(root_xid, default_xid):
         and (xid != default_xid):
             # Orphaned (not associated with a slice) class
             name = "%d?" % xid
         and (xid != default_xid):
             # Orphaned (not associated with a slice) class
             name = "%d?" % xid
-            logger.log("bwmon:  Found orphaned HTB %s. Removing." %name)
+            logger.log("bwmon:  Found orphaned HTB %s. Removing." %name, 1)
             bwlimit.off(xid)
 
         livehtbs[xid] = {'share': share,
             bwlimit.off(xid)
 
         livehtbs[xid] = {'share': share,
@@ -441,13 +487,9 @@ def sync(nmdbcopy):
         period, \
         default_MaxRate, \
         default_Maxi2Rate, \
         period, \
         default_MaxRate, \
         default_Maxi2Rate, \
-        default_MinRate, \
         default_MaxKByte,\
         default_MaxKByte,\
-        default_ThreshKByte,\
         default_Maxi2KByte,\
         default_Maxi2KByte,\
-        default_Threshi2KByte,\
-        default_Share,\
-        verbose
+        default_Share
 
     # All slices
     names = []
 
     # All slices
     names = []
@@ -461,15 +503,15 @@ def sync(nmdbcopy):
 
     try:
         f = open(datafile, "r+")
 
     try:
         f = open(datafile, "r+")
-        logger.log("bwmon:  Loading %s" % datafile)
+        logger.log("bwmon:  Loading %s" % datafile, 2)
         (version, slices, deaddb) = pickle.load(f)
         f.close()
         # Check version of data file
         (version, slices, deaddb) = pickle.load(f)
         f.close()
         # Check version of data file
-        if version != "$Id: bwmon.py,v 1.1.2.11 2007/06/26 18:03:55 faiyaza Exp $":
+        if version != "$Id$":
             logger.log("bwmon:  Not using old version '%s' data file %s" % (version, datafile))
             raise Exception
     except Exception:
             logger.log("bwmon:  Not using old version '%s' data file %s" % (version, datafile))
             raise Exception
     except Exception:
-        version = "$Id: bwmon.py,v 1.1.2.11 2007/06/26 18:03:55 faiyaza Exp $"
+        version = "$Id$"
         slices = {}
         deaddb = {}
 
         slices = {}
         deaddb = {}
 
@@ -481,12 +523,12 @@ def sync(nmdbcopy):
     # to use defaults.
     if root_xid not in slices.keys():
         slices[root_xid] = Slice(root_xid, "root", {})
     # to use defaults.
     if root_xid not in slices.keys():
         slices[root_xid] = Slice(root_xid, "root", {})
-        slices[root_xid].reset(0, 0, 0, 0, {})
+        slices[root_xid].reset({}, {})
     
     # Used by bwlimit.  pass {} since there is no rspec (like above).
     if default_xid not in slices.keys():
         slices[default_xid] = Slice(default_xid, "default", {})
     
     # Used by bwlimit.  pass {} since there is no rspec (like above).
     if default_xid not in slices.keys():
         slices[default_xid] = Slice(default_xid, "default", {})
-        slices[default_xid].reset(0, 0, 0, 0, {})
+        slices[default_xid].reset({}, {})
 
     live = {}
     # Get running slivers that should be on this node (from plc). {xid: name}
 
     live = {}
     # Get running slivers that should be on this node (from plc). {xid: name}
@@ -494,82 +536,108 @@ def sync(nmdbcopy):
     for plcSliver in nmdbcopy.keys():
         live[bwlimit.get_xid(plcSliver)] = nmdbcopy[plcSliver]
 
     for plcSliver in nmdbcopy.keys():
         live[bwlimit.get_xid(plcSliver)] = nmdbcopy[plcSliver]
 
-    logger.log("bwmon:  Found %s instantiated slices" % live.keys().__len__())
-    logger.log("bwmon:  Found %s slices in dat file" % slices.values().__len__())
+    logger.log("bwmon:  Found %s instantiated slices" % live.keys().__len__(), 2)
+    logger.log("bwmon:  Found %s slices in dat file" % slices.values().__len__(), 2)
 
     # Get actual running values from tc.
     # Update slice totals and bandwidth. {xid: {values}}
     kernelhtbs = gethtbs(root_xid, default_xid)
 
     # Get actual running values from tc.
     # Update slice totals and bandwidth. {xid: {values}}
     kernelhtbs = gethtbs(root_xid, default_xid)
-    logger.log("bwmon:  Found %s running HTBs" % kernelhtbs.keys().__len__())
-
-    # Get new slices.
-    # Slices in GetSlivers but not running HTBs
-    newslicesxids = Set(live.keys()) - Set(kernelhtbs.keys())
-    logger.log("bwmon:  Found %s new slices" % newslicesxids.__len__())
+    logger.log("bwmon:  Found %s running HTBs" % kernelhtbs.keys().__len__(), 2)
 
     # The dat file has HTBs for slices, but the HTBs aren't running
     nohtbslices =  Set(slices.keys()) - Set(kernelhtbs.keys())
 
     # The dat file has HTBs for slices, but the HTBs aren't running
     nohtbslices =  Set(slices.keys()) - Set(kernelhtbs.keys())
-    logger.log( "bwmon:  Found %s slices in dat but not running." % nohtbslices.__len__() )
+    logger.log( "bwmon:  Found %s slices in dat but not running." % nohtbslices.__len__(), 2)
     # Reset tc counts.
     for nohtbslice in nohtbslices:
         if live.has_key(nohtbslice): 
     # Reset tc counts.
     for nohtbslice in nohtbslices:
         if live.has_key(nohtbslice): 
-            slices[nohtbslice].reset( 0, 0, 0, 0, live[nohtbslice]['_rspec'] )
-        
+            slices[nohtbslice].reset( {}, live[nohtbslice]['_rspec'] )
+        else:
+            logger.log("bwmon:  Removing abondoned slice %s from dat." % nohtbslice)
+            del slices[nohtbslice]
+
+    # The dat file doesnt have HTB for the slice but kern has HTB
+    slicesnodat = Set(kernelhtbs.keys()) - Set(slices.keys())
+    logger.log( "bwmon:  Found %s slices with HTBs but not in dat" % slicesnodat.__len__(), 2)
+    for slicenodat in slicesnodat:
+        # But slice is running 
+        if live.has_key(slicenodat): 
+            # init the slice.  which means start accounting over since kernel
+            # htb was already there.
+            slices[slicenodat] = Slice(slicenodat, 
+                live[slicenodat]['name'], 
+                live[slicenodat]['_rspec'])
+
+    # Get new slices.
+    # Slices in GetSlivers but not running HTBs
+    newslicesxids = Set(live.keys()) - Set(kernelhtbs.keys())
+    logger.log("bwmon:  Found %s new slices" % newslicesxids.__len__(), 2)
+       
     # Setup new slices
     for newslice in newslicesxids:
         # Delegated slices dont have xids (which are uids) since they haven't been
         # instantiated yet.
         if newslice != None and live[newslice].has_key('_rspec') == True:
     # Setup new slices
     for newslice in newslicesxids:
         # Delegated slices dont have xids (which are uids) since they haven't been
         # instantiated yet.
         if newslice != None and live[newslice].has_key('_rspec') == True:
+            # Check to see if we recently deleted this slice.
             if live[newslice]['name'] not in deaddb.keys():
                 logger.log( "bwmon: New Slice %s" % live[newslice]['name'] )
                 # _rspec is the computed rspec:  NM retrieved data from PLC, computed loans
                 # and made a dict of computed values.
                 slices[newslice] = Slice(newslice, live[newslice]['name'], live[newslice]['_rspec'])
             if live[newslice]['name'] not in deaddb.keys():
                 logger.log( "bwmon: New Slice %s" % live[newslice]['name'] )
                 # _rspec is the computed rspec:  NM retrieved data from PLC, computed loans
                 # and made a dict of computed values.
                 slices[newslice] = Slice(newslice, live[newslice]['name'], live[newslice]['_rspec'])
-                slices[newslice].reset( 0, 0, 0, 0, live[newslice]['_rspec'] )
-                # Double check time for dead slice in deaddb is within 24hr recording period.
+                slices[newslice].reset( {}, live[newslice]['_rspec'] )
+            # Double check time for dead slice in deaddb is within 24hr recording period.
             elif (time.time() <= (deaddb[live[newslice]['name']]['slice'].time + period)):
                 deadslice = deaddb[live[newslice]['name']]
                 logger.log("bwmon: Reinstantiating deleted slice %s" % live[newslice]['name'])
                 slices[newslice] = deadslice['slice']
                 slices[newslice].xid = newslice
                 # Start the HTB
             elif (time.time() <= (deaddb[live[newslice]['name']]['slice'].time + period)):
                 deadslice = deaddb[live[newslice]['name']]
                 logger.log("bwmon: Reinstantiating deleted slice %s" % live[newslice]['name'])
                 slices[newslice] = deadslice['slice']
                 slices[newslice].xid = newslice
                 # Start the HTB
-                slices[newslice].reset(deadslice['slice'].MaxRate,
-                                    deadslice['slice'].Maxi2Rate,
-                                    deadslice['htb']['usedbytes'],
-                                    deadslice['htb']['usedi2bytes'],
-                                    live[newslice]['_rspec'])
+                newvals = {"maxrate": deadslice['slice'].MaxRate * 1000,
+                            "minrate": deadslice['slice'].MinRate * 1000,
+                            "maxexemptrate": deadslice['slice'].Maxi2Rate * 1000,
+                            "usedbytes": deadslice['htb']['usedbytes'] * 1000,
+                            "usedi2bytes": deadslice['htb']['usedi2bytes'],
+                            "share":deadslice['htb']['share']} 
+                slices[newslice].reset(newvals, live[newslice]['_rspec'])
                 # Bring up to date
                 # Bring up to date
-                slices[newslice].update(deadslice['slice'].MaxRate, 
-                                    deadslice['slice'].Maxi2Rate, 
-                                    deadslice['htb']['usedbytes'], 
-                                    deadslice['htb']['usedi2bytes'], 
-                                    live[newslice]['_rspec'])
+                slices[newslice].update(newvals, live[newslice]['_rspec'])
                 # Since the slice has been reinitialed, remove from dead database.
                 # Since the slice has been reinitialed, remove from dead database.
-                del deaddb[deadslice]
+                del deaddb[deadslice['slice'].name]
+                del newvals
         else:
         else:
-            logger.log("bwmon  Slice %s doesn't have xid.  Must be delegated.  Skipping." % live[newslice]['name'])
+            logger.log("bwmon:  Slice %s doesn't have xid.  Skipping." % live[newslice]['name'])
 
     # Move dead slices that exist in the pickle file, but
     # aren't instantiated by PLC into the dead dict until
     # recording period is over.  This is to avoid the case where a slice is dynamically created
     # and destroyed then recreated to get around byte limits.
 
     # Move dead slices that exist in the pickle file, but
     # aren't instantiated by PLC into the dead dict until
     # recording period is over.  This is to avoid the case where a slice is dynamically created
     # and destroyed then recreated to get around byte limits.
-    dead = Set(slices.keys()) - Set(live.keys())
-    logger.log("bwmon:  Found %s dead slices" % (dead.__len__() - 2))
-    for xid in dead:
-        if xid == root_xid or xid == default_xid:
+    deadxids = Set(slices.keys()) - Set(live.keys())
+    logger.log("bwmon:  Found %s dead slices" % (deadxids.__len__() - 2), 2)
+    for deadxid in deadxids:
+        if deadxid == root_xid or deadxid == default_xid:
             continue
             continue
-        logger.log("bwmon:  removing dead slice  %s " % xid)
-        if slices.has_key(xid):
+        logger.log("bwmon:  removing dead slice %s " % deadxid)
+        if slices.has_key(deadxid) and kernelhtbs.has_key(deadxid):
             # add slice (by name) to deaddb
             # add slice (by name) to deaddb
-            deaddb[slices[xid].name] = {'slice': slices[xid], 'htb': kernelhtbs[xid]}
-            del slices[xid]
-        if kernelhtbs.has_key(xid): bwlimit.off(xid)
+            logger.log("bwmon:  Saving bandwidth totals for %s." % slices[deadxid].name)
+            deaddb[slices[deadxid].name] = {'slice': slices[deadxid], 'htb': kernelhtbs[deadxid]}
+            del slices[deadxid]
+        if kernelhtbs.has_key(deadxid): 
+            logger.log("bwmon:  Removing HTB for %s." % deadxid, 2)
+            bwlimit.off(deadxid)
+    
+    # Clean up deaddb
+    for deadslice in deaddb.keys():
+        if (time.time() >= (deaddb[deadslice]['slice'].time + period)):
+            logger.log("bwmon:  Removing dead slice %s from dat." \
+                        % deaddb[deadslice]['slice'].name)
+            del deaddb[deadslice]
 
     # Get actual running values from tc since we've added and removed buckets.
     # Update slice totals and bandwidth. {xid: {values}}
     kernelhtbs = gethtbs(root_xid, default_xid)
 
     # Get actual running values from tc since we've added and removed buckets.
     # Update slice totals and bandwidth. {xid: {values}}
     kernelhtbs = gethtbs(root_xid, default_xid)
-    logger.log("bwmon:  now %s running HTBs" % kernelhtbs.keys().__len__())
+    logger.log("bwmon:  now %s running HTBs" % kernelhtbs.keys().__len__(), 2)
 
 
+    # Update all byte limites on all slices
     for (xid, slice) in slices.iteritems():
         # Monitor only the specified slices
         if xid == root_xid or xid == default_xid: continue
     for (xid, slice) in slices.iteritems():
         # Monitor only the specified slices
         if xid == root_xid or xid == default_xid: continue
@@ -583,36 +651,64 @@ def sync(nmdbcopy):
             # that the byte counters have overflowed (or, more
             # likely, the node was restarted or the HTB buckets
             # were re-initialized).
             # that the byte counters have overflowed (or, more
             # likely, the node was restarted or the HTB buckets
             # were re-initialized).
-            slice.reset(kernelhtbs[xid]['maxrate'], \
-                kernelhtbs[xid]['maxexemptrate'], \
-                kernelhtbs[xid]['usedbytes'], \
-                kernelhtbs[xid]['usedi2bytes'], \
-                live[xid]['_rspec'])
-        else:
-            if debug:  logger.log("bwmon: Updating slice %s" % slice.name)
+            slice.reset(kernelhtbs[xid], live[xid]['_rspec'])
+        elif ENABLE:
+            logger.log("bwmon:  Updating slice %s" % slice.name, 2)
             # Update byte counts
             # Update byte counts
-            slice.update(kernelhtbs[xid]['maxrate'], \
-                kernelhtbs[xid]['maxexemptrate'], \
-                kernelhtbs[xid]['usedbytes'], \
-                kernelhtbs[xid]['usedi2bytes'], \
-                live[xid]['_rspec'])
+            slice.update(kernelhtbs[xid], live[xid]['_rspec'])
 
 
-    logger.log("bwmon:  Saving %s slices in %s" % (slices.keys().__len__(),datafile))
+    logger.log("bwmon:  Saving %s slices in %s" % (slices.keys().__len__(),datafile), 2)
     f = open(datafile, "w")
     pickle.dump((version, slices, deaddb), f)
     f.close()
 
     f = open(datafile, "w")
     pickle.dump((version, slices, deaddb), f)
     f.close()
 
+# doesnt use generic default interface because this runs as its own thread.
+# changing the config variable will not have an effect since GetSlivers: pass
+def getDefaults(nmdbcopy):
+    '''
+    Get defaults from default slice's slice attributes.
+    '''
+    status = True
+    # default slice
+    dfltslice = nmdbcopy.get(PLC_SLICE_PREFIX+"_default")
+    if dfltslice: 
+        if dfltslice['rspec']['net_max_rate'] == -1:
+            allOff()
+            status = False
+    return status
+
+
+def allOff():
+    """
+    Turn off all slice HTBs
+    """
+    # Get/set special slice IDs
+    root_xid = bwlimit.get_xid("root")
+    default_xid = bwlimit.get_xid("default")
+    kernelhtbs = gethtbs(root_xid, default_xid)
+    if len(kernelhtbs):
+        logger.log("bwlimit:  Disabling all running HTBs.")
+        for htb in kernelhtbs.keys(): bwlimit.off(htb) 
+
+
 lock = threading.Event()
 def run():
 lock = threading.Event()
 def run():
-    """When run as a thread, wait for event, lock db, deep copy it, release it, run bwmon.GetSlivers(), then go back to waiting."""
-    if debug:  logger.log("bwmon:  Thread started")
+    """
+    When run as a thread, wait for event, lock db, deep copy it, release it, 
+    run bwmon.GetSlivers(), then go back to waiting.
+    """
+    logger.log("bwmon:  Thread started", 2)
     while True:
         lock.wait()
     while True:
         lock.wait()
-        if debug: logger.log("bwmon:  Event received.  Running.")
+        logger.log("bwmon:  Event received.  Running.", 2)
         database.db_lock.acquire()
         nmdbcopy = copy.deepcopy(database.db)
         database.db_lock.release()
         database.db_lock.acquire()
         nmdbcopy = copy.deepcopy(database.db)
         database.db_lock.release()
-        try:  sync(nmdbcopy)
+        try:  
+            if getDefaults(nmdbcopy) and len(bwlimit.tc("class show dev %s" % dev_default)) > 0:
+                # class show to check if net:InitNodeLimit:bwlimit.init has run.
+                sync(nmdbcopy)
+            else: logger.log("bwmon:  BW limits DISABLED.")
         except: logger.log_exc()
         lock.clear()
 
         except: logger.log_exc()
         lock.clear()