reguire gnupg1 on f>=31; sense the system to use gpg1 when installed
[nodemanager.git] / bwmon.py
index 855767e..3a1f533 100644 (file)
--- a/bwmon.py
+++ b/bwmon.py
@@ -1,7 +1,4 @@
-#!/usr/bin/python
-#
-# $Id$
-# $URL$
+#!/usr/bin/python3
 #
 # Average bandwidth monitoring script. Run periodically via NM db.sync to
 # enforce a soft limit on daily bandwidth usage for each slice. If a
@@ -26,10 +23,12 @@ import socket
 import copy
 import threading
 
+import plnode.bwlimit as bwlimit
+
 import logger
 import tools
-import bwlimit
 import database
+from config import Config
 
 priority = 20
 
@@ -41,14 +40,6 @@ ENABLE = True
 
 DB_FILE = "/var/lib/nodemanager/bwmon.pickle"
 
-try:
-    sys.path.append("/etc/planetlab")
-    from plc_config import *
-except:
-    DEBUG = True
-    logger.verbose("bwmon: Warning: Configuration file /etc/planetlab/plc_config.py not found")
-    logger.log("bwmon: Running in DEBUG mode.  Logging to file and not emailing.")
-
 # Constants
 seconds_per_day = 24 * 60 * 60
 bits_per_byte = 8
@@ -57,14 +48,14 @@ dev_default = tools.get_default_if()
 # Burst to line rate (or node cap).  Set by NM. in KBit/s
 default_MaxRate = int(bwlimit.get_bwcap(dev_default) / 1000)
 default_Maxi2Rate = int(bwlimit.bwmax / 1000)
-# 5.4 Gbyte per day. 5.4 * 1024 k * 1024M * 1024G 
+# 5.4 Gbyte per day. 5.4 * 1024 k * 1024M * 1024G
 # 5.4 Gbyte per day max allowed transfered per recording period
 # 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 
+# -- 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)
@@ -144,16 +135,17 @@ def slicemail(slice, subject, body):
     '''
     Front end to sendmail.  Sends email to slice alias with given subject and body.
     '''
-
-    sendmail = os.popen("/usr/sbin/sendmail -N never -t -f%s" % PLC_MAIL_SUPPORT_ADDRESS, "w")
+    config = Config()
+    sendmail = os.popen("/usr/sbin/sendmail -N never -t -f%s" % config.PLC_MAIL_SUPPORT_ADDRESS, "w")
 
     # Parsed from MyPLC config
-    to = [PLC_MAIL_MOM_LIST_ADDRESS]
+    to = [config.PLC_MAIL_MOM_LIST_ADDRESS]
 
     if slice is not None and slice != "root":
-        to.append(PLC_MAIL_SLICE_ADDRESS.replace("SLICE", slice))
+        to.append(config.PLC_MAIL_SLICE_ADDRESS.replace("SLICE", slice))
 
-    header = {'from': "%s Support <%s>" % (PLC_NAME, PLC_MAIL_SUPPORT_ADDRESS),
+    header = {'from': "%s Support <%s>" % (config.PLC_NAME,
+                                           config.PLC_MAIL_SUPPORT_ADDRESS),
               'to': ", ".join(to),
               'version': sys.version.split(" ")[0],
               'subject': subject}
@@ -187,9 +179,9 @@ class Slice:
     i2bytes - high bandwidth bytes transmitted at the beginning of the recording period (for I2 -F)
     MaxKByte - total volume of data allowed
     ThreshKbyte - After thresh, cap node to (maxkbyte - bytes)/(time left in period)
-    Maxi2KByte - same as MaxKByte, but for i2 
-    Threshi2Kbyte - same as Threshi2KByte, but for i2 
-    MaxRate - max_rate slice attribute. 
+    Maxi2KByte - same as MaxKByte, but for i2
+    Threshi2Kbyte - same as Threshi2KByte, but for i2
+    MaxRate - max_rate slice attribute.
     Maxi2Rate - max_exempt_rate slice attribute.
     Share - Used by Sirius to loan min rates
     Sharei2 - Used by Sirius to loan min rates for i2
@@ -204,9 +196,9 @@ class Slice:
         self.bytes = 0
         self.i2bytes = 0
         self.MaxRate = default_MaxRate
-        self.MinRate = bwlimit.bwmin / 1000
+        self.MinRate = bwlimit.bwmin // 1000
         self.Maxi2Rate = default_Maxi2Rate
-        self.Mini2Rate = bwlimit.bwmin / 1000
+        self.Mini2Rate = bwlimit.bwmin // 1000
         self.MaxKByte = default_MaxKByte
         self.ThreshKByte = int(.8 * self.MaxKByte)
         self.Maxi2KByte = default_Maxi2KByte
@@ -217,12 +209,13 @@ class Slice:
         self.capped = False
 
         self.updateSliceTags(rspec)
-        bwlimit.set(xid = self.xid, 
-                minrate = self.MinRate * 1000, 
-                maxrate = self.MaxRate * 1000, 
-                maxexemptrate = self.Maxi2Rate * 1000,
-                minexemptrate = self.Mini2Rate * 1000,
-                share = self.Share)
+        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)
 
     def __repr__(self):
         return self.name
@@ -235,7 +228,7 @@ class Slice:
 
         # Sanity check plus policy decision for MinRate:
         # Minrate cant be greater than 25% of MaxRate or NodeCap.
-        MinRate = int(rspec.get("net_min_rate", bwlimit.bwmin / 1000))
+        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:
@@ -247,36 +240,36 @@ class Slice:
             self.MaxRate = MaxRate
             logger.log("bwmon: Updating %s: Max Rate = %s" %(self.name, self.MaxRate))
 
-        Mini2Rate = int(rspec.get('net_i2_min_rate', bwlimit.bwmin / 1000))
+        Mini2Rate = int(rspec.get('net_i2_min_rate', bwlimit.bwmin // 1000))
         if Mini2Rate != self.Mini2Rate:
-            self.Mini2Rate = 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', default_Maxi2Rate))
         if Maxi2Rate != self.Maxi2Rate:
             self.Maxi2Rate = Maxi2Rate
             logger.log("bwmon: Updating %s: Max i2 Rate = %s" %(self.name, self.Maxi2Rate))
-                          
+
         MaxKByte = int(rspec.get('net_max_kbyte', default_MaxKByte))
         if MaxKByte != self.MaxKByte:
             self.MaxKByte = MaxKByte
             logger.log("bwmon: Updating %s: Max KByte lim = %s" %(self.name, self.MaxKByte))
-                          
+
         Maxi2KByte = int(rspec.get('net_i2_max_kbyte', default_Maxi2KByte))
         if Maxi2KByte != 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', (MaxKByte * .8)))
         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', (Maxi2KByte * .8)))
-        if Threshi2KByte != self.Threshi2KByte:    
+        if Threshi2KByte != self.Threshi2KByte:
             self.Threshi2KByte = Threshi2KByte
             logger.log("bwmon: Updating %s: i2 Thresh KByte = %s" %(self.name, self.Threshi2KByte))
+
         Share = int(rspec.get('net_share', default_Share))
         if Share != self.Share:
             self.Share = Share
@@ -284,7 +277,7 @@ class Slice:
 
         Sharei2 = int(rspec.get('net_i2_share', default_Share))
         if Sharei2 != self.Sharei2:
-            self.Sharei2 = Sharei2 
+            self.Sharei2 = Sharei2
             logger.log("bwmon: Updating %s: Net i2 Share = %s" %(self.name, self.i2Share))
 
 
@@ -297,7 +290,7 @@ class Slice:
         self.Share = runningrates.get('share', 1)
 
         # Query Node Manager for max rate overrides
-        self.updateSliceTags(rspec)    
+        self.updateSliceTags(rspec)
 
         # Reset baseline time
         self.time = time.time()
@@ -306,13 +299,13 @@ class Slice:
         self.bytes = runningrates.get('usedbytes', 0)
         self.i2bytes = runningrates.get('usedi2bytes', 0)
 
-        # Reset email 
+        # Reset email
         self.emailed = False
         # Reset flag
         self.capped = False
         # Reset rates.
-        maxrate = self.MaxRate * 1000 
-        minrate = self.MinRate * 1000 
+        maxrate = self.MaxRate * 1000
+        minrate = self.MinRate * 1000
         maxi2rate = self.Maxi2Rate * 1000
         mini2rate = self.Mini2Rate * 1000
 
@@ -326,8 +319,8 @@ class Slice:
                             bwlimit.format_tc_rate(maxrate),
                             bwlimit.format_tc_rate(maxi2rate)))
             bwlimit.set(xid = self.xid, dev = dev_default,
-                minrate = self.MinRate * 1000, 
-                maxrate = self.MaxRate * 1000, 
+                minrate = self.MinRate * 1000,
+                maxrate = self.MaxRate * 1000,
                 maxexemptrate = self.Maxi2Rate * 1000,
                 minexemptrate = self.Mini2Rate * 1000,
                 share = self.Share)
@@ -361,10 +354,10 @@ class Slice:
             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
@@ -380,14 +373,14 @@ class Slice:
     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.  
+        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)    
+        self.updateSliceTags(rspec)
 
         usedbytes = runningrates['usedbytes']
         usedi2bytes = runningrates['usedi2bytes']
@@ -399,7 +392,8 @@ class Slice:
             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
@@ -409,13 +403,14 @@ class Slice:
             # 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)
             # 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
@@ -431,11 +426,11 @@ class Slice:
         if (runningrates['maxrate'] != new_maxrate) or \
         (runningrates['minrate'] != self.MinRate * 1000) or \
         (runningrates['maxexemptrate'] != new_maxi2rate) or \
-        (runningrates['minexemptrate'] != self.Mini2Rate * 1000) or \
+        ('minexemptrate' in runningrates and runningrates['minexemptrate'] != self.Mini2Rate * 1000) or \
         (runningrates['share'] != self.Share):
             # Apply parameters
-            bwlimit.set(xid = self.xid, 
-                minrate = self.MinRate * 1000, 
+            bwlimit.set(xid = self.xid, dev = dev_default,
+                minrate = self.MinRate * 1000,
                 maxrate = new_maxrate,
                 minexemptrate = self.Mini2Rate * 1000,
                 maxexemptrate = new_maxi2rate,
@@ -452,12 +447,12 @@ def gethtbs(root_xid, default_xid):
     Turn off HTBs without names.
     """
     livehtbs = {}
-    for params in bwlimit.get():
+    for params in bwlimit.get(dev = dev_default):
         (xid, share,
          minrate, maxrate,
          minexemptrate, maxexemptrate,
          usedbytes, usedi2bytes) = params
-        
+
         name = bwlimit.get_slice(xid)
 
         if (name is None) \
@@ -466,7 +461,7 @@ def gethtbs(root_xid, default_xid):
             # Orphaned (not associated with a slice) class
             name = "%d?" % xid
             logger.log("bwmon: Found orphaned HTB %s. Removing." %name)
-            bwlimit.off(xid)
+            bwlimit.off(xid, dev = dev_default)
 
         livehtbs[xid] = {'share': share,
             'minrate': minrate,
@@ -474,15 +469,15 @@ def gethtbs(root_xid, default_xid):
             'maxexemptrate': maxexemptrate,
             'minexemptrate': minexemptrate,
             'usedbytes': usedbytes,
-            'name': name, 
+            'name': name,
             'usedi2bytes': usedi2bytes}
 
     return livehtbs
 
 def sync(nmdbcopy):
     """
-    Syncs tc, db, and bwmon.pickle. 
-    Then, starts new slices, kills old ones, and updates byte accounts for each running slice.  
+    Syncs tc, db, and bwmon.pickle.
+    Then, starts new slices, kills old ones, and updates byte accounts for each running slice.
     Sends emails and caps those that went over their limit.
     """
     # Defaults
@@ -490,20 +485,24 @@ def sync(nmdbcopy):
         period, \
         default_MaxRate, \
         default_Maxi2Rate, \
-        default_MaxKByte,\
-        default_Maxi2KByte,\
-        default_Share
+        default_MaxKByte, \
+        default_Maxi2KByte, \
+        default_Share, \
+        dev_default
 
     # All slices
     names = []
-    # In case the limits have changed. 
-    default_MaxRate = int(bwlimit.get_bwcap() / 1000)
+    # In case the limits have changed.
+    default_MaxRate = int(bwlimit.get_bwcap(dev_default) / 1000)
     default_Maxi2Rate = int(bwlimit.bwmax / 1000)
 
     # Incase default isn't set yet.
     if default_MaxRate == -1:
         default_MaxRate = 1000000
 
+    # xxx $Id$
+    # with svn we used to have a trick to detect upgrades of this file
+    # this has gone with the move to git, without any noticeable effect on operations though
     try:
         f = open(DB_FILE, "r+")
         logger.verbose("bwmon: Loading %s" % DB_FILE)
@@ -522,37 +521,37 @@ def sync(nmdbcopy):
     root_xid = bwlimit.get_xid("root")
     default_xid = bwlimit.get_xid("default")
 
-    # Since root is required for sanity, its not in the API/plc database, so pass {} 
+    # Since root is required for sanity, its not in the API/plc database, so pass {}
     # to use defaults.
-    if root_xid not in slices.keys():
+    if root_xid not in list(slices.keys()):
         slices[root_xid] = Slice(root_xid, "root", {})
         slices[root_xid].reset({}, {})
-    
+
     # Used by bwlimit.  pass {} since there is no rspec (like above).
-    if default_xid not in slices.keys():
+    if default_xid not in list(slices.keys()):
         slices[default_xid] = Slice(default_xid, "default", {})
         slices[default_xid].reset({}, {})
 
     live = {}
     # Get running slivers that should be on this node (from plc). {xid: name}
     # db keys on name, bwmon keys on xid.  db doesnt have xid either.
-    for plcSliver in nmdbcopy.keys():
+    for plcSliver in list(nmdbcopy.keys()):
         live[bwlimit.get_xid(plcSliver)] = nmdbcopy[plcSliver]
 
-    logger.verbose("bwmon: Found %s instantiated slices" % live.keys().__len__())
-    logger.verbose("bwmon: Found %s slices in dat file" % slices.values().__len__())
+    logger.verbose("bwmon: Found %s instantiated slices" % list(live.keys()).__len__())
+    logger.verbose("bwmon: Found %s slices in dat file" % list(slices.values()).__len__())
 
     # Get actual running values from tc.
     # Update slice totals and bandwidth. {xid: {values}}
     kernelhtbs = gethtbs(root_xid, default_xid)
-    logger.verbose("bwmon: Found %s running HTBs" % kernelhtbs.keys().__len__())
+    logger.verbose("bwmon: Found %s running HTBs" % list(kernelhtbs.keys()).__len__())
 
     # The dat file has HTBs for slices, but the HTBs aren't running
     nohtbslices =  set(slices.keys()) - set(kernelhtbs.keys())
     logger.verbose( "bwmon: Found %s slices in dat but not running." % nohtbslices.__len__())
     # Reset tc counts.
     for nohtbslice in nohtbslices:
-        if live.has_key(nohtbslice): 
+        if nohtbslice in live:
             slices[nohtbslice].reset( {}, live[nohtbslice]['_rspec'] )
         else:
             logger.log("bwmon: Removing abondoned slice %s from dat." % nohtbslice)
@@ -562,26 +561,26 @@ def sync(nmdbcopy):
     slicesnodat = set(kernelhtbs.keys()) - set(slices.keys())
     logger.verbose( "bwmon: Found %s slices with HTBs but not in dat" % slicesnodat.__len__())
     for slicenodat in slicesnodat:
-        # But slice is running 
-        if live.has_key(slicenodat): 
+        # But slice is running
+        if slicenodat in live:
             # init the slice.  which means start accounting over since kernel
             # htb was already there.
-            slices[slicenodat] = Slice(slicenodat, 
-                live[slicenodat]['name'], 
+            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.verbose("bwmon: Found %s new slices" % newslicesxids.__len__())
-       
+
     # 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:
+        if newslice != None and ('_rspec' in live[newslice]) == True:
             # Check to see if we recently deleted this slice.
-            if live[newslice]['name'] not in deaddb.keys():
+            if live[newslice]['name'] not in list(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.
@@ -599,7 +598,7 @@ def sync(nmdbcopy):
                             "maxexemptrate": deadslice['slice'].Maxi2Rate * 1000,
                             "usedbytes": deadslice['htb']['usedbytes'] * 1000,
                             "usedi2bytes": deadslice['htb']['usedi2bytes'],
-                            "share":deadslice['htb']['share']} 
+                            "share":deadslice['htb']['share']}
                 slices[newslice].reset(newvals, live[newslice]['_rspec'])
                 # Bring up to date
                 slices[newslice].update(newvals, live[newslice]['_rspec'])
@@ -619,17 +618,17 @@ def sync(nmdbcopy):
         if deadxid == root_xid or deadxid == default_xid:
             continue
         logger.log("bwmon: removing dead slice %s " % deadxid)
-        if slices.has_key(deadxid) and kernelhtbs.has_key(deadxid):
+        if deadxid in slices and deadxid in kernelhtbs:
             # add slice (by name) to deaddb
             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): 
+        if deadxid in kernelhtbs:
             logger.verbose("bwmon: Removing HTB for %s." % deadxid)
-            bwlimit.off(deadxid)
-    
+            bwlimit.off(deadxid, dev = dev_default)
+
     # Clean up deaddb
-    for deadslice in deaddb.keys():
+    for deadslice in list(deaddb.keys()):
         if (time.time() >= (deaddb[deadslice]['slice'].time + period)):
             logger.log("bwmon: Removing dead slice %s from dat." \
                         % deaddb[deadslice]['slice'].name)
@@ -638,15 +637,15 @@ def sync(nmdbcopy):
     # 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.verbose("bwmon: now %s running HTBs" % kernelhtbs.keys().__len__())
+    logger.verbose("bwmon: now %s running HTBs" % list(kernelhtbs.keys()).__len__())
 
     # Update all byte limites on all slices
-    for (xid, slice) in slices.iteritems():
+    for (xid, slice) in slices.items():
         # Monitor only the specified slices
         if xid == root_xid or xid == default_xid: continue
         if names and name not in names:
             continue
+
         if (time.time() >= (slice.time + period)) or \
             (kernelhtbs[xid]['usedbytes'] < slice.bytes) or \
             (kernelhtbs[xid]['usedi2bytes'] < slice.i2bytes):
@@ -660,7 +659,7 @@ def sync(nmdbcopy):
             # Update byte counts
             slice.update(kernelhtbs[xid], live[xid]['_rspec'])
 
-    logger.verbose("bwmon: Saving %s slices in %s" % (slices.keys().__len__(),DB_FILE))
+    logger.verbose("bwmon: Saving %s slices in %s" % (list(slices.keys()).__len__(), DB_FILE))
     f = open(DB_FILE, "w")
     pickle.dump((version, slices, deaddb), f)
     f.close()
@@ -673,8 +672,8 @@ def getDefaults(nmdbcopy):
     '''
     status = True
     # default slice
-    dfltslice = nmdbcopy.get(PLC_SLICE_PREFIX+"_default")
-    if dfltslice: 
+    dfltslice = nmdbcopy.get(Config().PLC_SLICE_PREFIX+"_default")
+    if dfltslice:
         if dfltslice['rspec']['net_max_rate'] == -1:
             allOff()
             status = False
@@ -691,13 +690,13 @@ def allOff():
     kernelhtbs = gethtbs(root_xid, default_xid)
     if len(kernelhtbs):
         logger.log("bwmon: Disabling all running HTBs.")
-        for htb in kernelhtbs.keys(): bwlimit.off(htb) 
+        for htb in list(kernelhtbs.keys()): bwlimit.off(htb, dev = dev_default)
 
 
 lock = threading.Event()
 def run():
     """
-    When run as a thread, wait for event, lock db, deep copy it, release it, 
+    When run as a thread, wait for event, lock db, deep copy it, release it,
     run bwmon.GetSlivers(), then go back to waiting.
     """
     logger.verbose("bwmon: Thread started")
@@ -707,7 +706,7 @@ def run():
         database.db_lock.acquire()
         nmdbcopy = copy.deepcopy(database.db)
         database.db_lock.release()
-        try:  
+        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)
@@ -719,5 +718,5 @@ def start(*args):
     tools.as_daemon_thread(run)
 
 def GetSlivers(*args):
-    logger.verbose ("bwmon: triggering dummy GetSlivers") 
+    logger.verbose ("bwmon: triggering dummy GetSlivers")
     pass