No such variable self.name. Changed it to network['hostname'].
[nodemanager.git] / bwmon.py
index cc2730c..55c077f 100644 (file)
--- a/bwmon.py
+++ b/bwmon.py
@@ -15,7 +15,7 @@
 # Faiyaz Ahmed <faiyaza@cs.princeton.edu>
 # Copyright (C) 2004-2006 The Trustees of Princeton University
 #
-# $Id: bwmon.py,v 1.20 2007/01/10 16:51:04 faiyaza Exp $
+# $Id: bwmon.py,v 1.6 2007/02/12 23:05:58 faiyaza Exp $
 #
 
 import os
@@ -54,11 +54,12 @@ verbose = 0
 datafile = "/var/lib/misc/bwmon.dat"
 #nm = None
 
-# Burst to line rate (or node cap).  Set by NM.
-default_MaxRate = bwlimit.get_bwcap()
-default_Maxi2Rate = bwlimit.bwmax
+# Burst to line rate (or node cap).  Set by NM. in KBit/s
+default_MaxRate = int(bwlimit.get_bwcap() / 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
 default_MaxKByte = 5662310
@@ -92,6 +93,78 @@ footer = \
 %(date)s %(hostname)s bwcap %(slice)s
 """.lstrip()
 
+def format_bytes(bytes, si = True):
+    """
+    Formats bytes into a string
+    """
+    if si:
+        kilo = 1000.
+    else:
+        # Officially, a kibibyte
+        kilo = 1024.
+
+    if bytes >= (kilo * kilo * kilo):
+        return "%.1f GB" % (bytes / (kilo * kilo * kilo))
+    elif bytes >= 1000000:
+        return "%.1f MB" % (bytes / (kilo * kilo))
+    elif bytes >= 1000:
+        return "%.1f KB" % (bytes / kilo)
+    else:
+        return "%.0f bytes" % bytes
+
+def format_period(seconds):
+    """
+    Formats a period in seconds into a string
+    """
+
+    if seconds == (24 * 60 * 60):
+        return "day"
+    elif seconds == (60 * 60):
+        return "hour"
+    elif seconds > (24 * 60 * 60):
+        return "%.1f days" % (seconds / 24. / 60. / 60.)
+    elif seconds > (60 * 60):
+        return "%.1f hours" % (seconds / 60. / 60.)
+    elif seconds > (60):
+        return "%.1f minutes" % (seconds / 60.)
+    else:
+        return "%.0f seconds" % seconds
+
+def slicemail(slice, subject, body):
+    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]
+
+    if slice is not None and slice != "root":
+        to.append(PLC_MAIL_SLICE_ADDRESS.replace("SLICE", slice))
+
+    header = {'from': "%s Support <%s>" % (PLC_NAME, PLC_MAIL_SUPPORT_ADDRESS),
+              'to': ", ".join(to),
+              'version': sys.version.split(" ")[0],
+              'subject': subject}
+
+    # Write headers
+    sendmail.write(
+"""
+Content-type: text/plain
+From: %(from)s
+Reply-To: %(from)s
+To: %(to)s
+X-Mailer: Python/%(version)s
+Subject: %(subject)s
+
+""".lstrip() % header)
+
+    # Write body
+    sendmail.write(body)
+    # Done
+    sendmail.close()
+
+
 class Slice:
     """
     Stores the last recorded bandwidth parameters of a slice.
@@ -111,7 +184,7 @@ class Slice:
 
     """
 
-    def __init__(self, xid, name, maxrate, maxi2rate, bytes, i2bytes, data):
+    def __init__(self, xid, name, data):
         self.xid = xid
         self.name = name
         self.time = 0
@@ -120,6 +193,7 @@ class Slice:
         self.MaxRate = default_MaxRate
         self.MinRate = default_MinRate
         self.Maxi2Rate = default_Maxi2Rate
+        self.Mini2Rate = default_Mini2Rate
         self.MaxKByte = default_MaxKByte
         self.ThreshKByte = default_ThreshKByte
         self.Maxi2KByte = default_Maxi2KByte
@@ -127,8 +201,14 @@ class Slice:
         self.Share = default_Share
         self.emailed = False
 
-        # Get real values where applicable
-        self.reset(maxrate, maxi2rate, bytes, i2bytes, data)
+        self.updateSliceAttributes(data)
+        bwlimit.set(xid = self.xid, 
+                minrate = self.MinRate, 
+                maxrate = self.MaxRate, 
+                maxexemptrate = self.Maxi2Rate,
+                minexemptrate = self.Mini2Rate,
+                share = self.Share)
+
 
     def __repr__(self):
         return self.name
@@ -139,43 +219,43 @@ class Slice:
             if sliver['name'] == self.name: 
                 for attribute in sliver['attributes']:
                     if attribute['name'] == 'net_min_rate':     
-                        self.MinRate = attribute['value']
+                        self.MinRate = int(attribute['value'])
                         logger.log("bwmon:  Updating %s. Min Rate - %s" \
                           %(self.name, self.MinRate))
                     elif attribute['name'] == 'net_max_rate':       
-                        self.MaxRate = attribute['value']
+                        self.MaxRate = int(attribute['value'])
                         logger.log("bwmon:  Updating %s. Max Rate - %s" \
                           %(self.name, self.MaxRate))
                     elif attribute['name'] == 'net_i2_min_rate':
-                        self.Mini2Rate = attribute['value']
+                        self.Mini2Rate = int(attribute['value'])
                         logger.log("bwmon:  Updating %s. Min i2 Rate - %s" \
                           %(self.name, self.Mini2Rate))
                     elif attribute['name'] == 'net_i2_max_rate':        
-                        self.Maxi2Rate = attribute['value']
+                        self.Maxi2Rate = int(attribute['value'])
                         logger.log("bwmon:  Updating %s. Max i2 Rate - %s" \
                           %(self.name, self.Maxi2Rate))
                     elif attribute['name'] == 'net_max_kbyte':      
-                        self.MaxKByte = attribute['value']
+                        self.MaxKByte = int(attribute['value'])
                         logger.log("bwmon:  Updating %s. Max KByte lim - %s" \
                           %(self.name, self.MaxKByte))
                     elif attribute['name'] == 'net_i2_max_kbyte':   
-                        self.Maxi2KByte = attribute['value']
+                        self.Maxi2KByte = int(attribute['value'])
                         logger.log("bwmon:  Updating %s. Max i2 KByte - %s" \
                           %(self.name, self.Maxi2KByte))
                     elif attribute['name'] == 'net_thresh_kbyte':   
-                        self.ThreshKByte = attribute['value']
+                        self.ThreshKByte = int(attribute['value'])
                         logger.log("bwmon:  Updating %s. Thresh KByte - %s" \
                           %(self.name, self.ThreshKByte))
                     elif attribute['name'] == 'net_i2_thresh_kbyte':    
-                        self.Threshi2KByte = attribute['value']
+                        self.Threshi2KByte = int(attribute['value'])
                         logger.log("bwmon:  Updating %s. i2 Thresh KByte - %s" \
                           %(self.name, self.Threshi2KByte))
                     elif attribute['name'] == 'net_share':  
-                        self.Share = attribute['value']
+                        self.Share = int(attribute['value'])
                         logger.log("bwmon:  Updating %s. Net Share - %s" \
                           %(self.name, self.Share))
                     elif attribute['name'] == 'net_i2_share':   
-                        self.Sharei2 = attribute['value']
+                        self.Sharei2 = int(attribute['value'])
                         logger.log("bwmon:  Updating %s. Net i2 Share - %s" \
                           %(self.name, self.i2Share))
 
@@ -198,18 +278,19 @@ class Slice:
 
         # Reset email 
         self.emailed = False
-
+        maxrate = self.MaxRate * 1000 
+        maxi2rate = self.Maxi2Rate * 1000 
         # Reset rates.
         if (self.MaxRate != runningmaxrate) or (self.Maxi2Rate != runningmaxi2rate):
             logger.log("bwmon:  %s reset to %s/%s" % \
                   (self.name,
-                   bwlimit.format_tc_rate(self.MaxRate),
-                   bwlimit.format_tc_rate(self.Maxi2Rate)))
+                   bwlimit.format_tc_rate(maxrate),
+                   bwlimit.format_tc_rate(maxi2rate)))
             bwlimit.set(xid = self.xid, 
-                minrate = self.MinRate, 
-                maxrate = self.MaxRate, 
-                maxexemptrate = self.Maxi2Rate,
-                minexemptrate = self.Mini2Rate,
+                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, data):
@@ -231,13 +312,13 @@ class Slice:
 
         if usedbytes >= (self.bytes + (self.ThreshKByte * 1024)):
             maxbyte = self.MaxKByte * 1024
-            bytesused = bytes - self.bytes
+            bytesused = usedbytes - self.bytes
             timeused = int(time.time() - self.time)
             new_maxrate = int(((maxbyte - bytesused) * 8)/(period - timeused))
             if new_maxrate < self.MinRate:
                 new_maxrate = self.MinRate
         else:
-            new_maxrate = self.MaxRate 
+            new_maxrate = self.MaxRate * 1000 
 
         # Format template parameters for low bandwidth message
         params['class'] = "low bandwidth"
@@ -258,13 +339,13 @@ class Slice:
     
         if usedi2bytes >= (self.i2bytes + (self.Threshi2KByte * 1024)):
             maxi2byte = self.Maxi2KByte * 1024
-            i2bytesused = i2bytes - self.i2bytes
+            i2bytesused = usedi2bytes - self.i2bytes
             timeused = int(time.time() - self.time)
             new_maxi2rate = int(((maxi2byte - i2bytesused) * 8)/(period - timeused))
             if new_maxi2rate < self.Mini2Rate:
                 new_maxi2rate = self.Mini2Rate
         else:
-            new_maxi2rate = self.Maxi2Rate 
+            new_maxi2rate = self.Maxi2Rate * 1000
 
         # Format template parameters for high bandwidth message
         params['class'] = "high bandwidth"
@@ -321,23 +402,40 @@ def GetSlivers(data):
         (version, slices) = pickle.load(f)
         f.close()
         # Check version of data file
-        if version != "$Id: bwmon.py,v 1.20 2007/01/10 16:51:04 faiyaza Exp $":
+        if version != "$Id: bwmon.py,v 1.6 2007/02/12 23:05:58 faiyaza Exp $":
             logger.log("bwmon:  Not using old version '%s' data file %s" % (version, datafile))
             raise Exception
     except Exception:
-        version = "$Id: bwmon.py,v 1.20 2007/01/10 16:51:04 faiyaza Exp $"
+        version = "$Id: bwmon.py,v 1.6 2007/02/12 23:05:58 faiyaza Exp $"
         slices = {}
 
-    # Get special slice IDs
+    # Get/set special slice IDs
     root_xid = bwlimit.get_xid("root")
     default_xid = bwlimit.get_xid("default")
 
-    # {name: xid}
+    if root_xid not in slices.keys():
+        slices[root_xid] = Slice(root_xid, "root", data)
+        slices[root_xid].reset(0, 0, 0, 0, data)
+
+    if default_xid not in slices.keys():
+        slices[default_xid] = Slice(default_xid, "default", data)
+        slices[default_xid].reset(0, 0, 0, 0, data)
+
     live = {}
+    # Get running slivers. {xid: name}
     for sliver in data['slivers']:
-        live[sliver['name']] = bwlimit.get_xid(sliver['name'])
+        live[bwlimit.get_xid(sliver['name'])] = sliver['name']
+
+
+    # Setup new slices.
+    newslicesxids = Set(live.keys()) - Set(slices.keys())
+    for newslicexid in newslicesxids:
+        logger.log("bwmon: New Slice %s" % live[newslicexid])
+        slices[newslicexid] = Slice(newslicexid, live[newslicexid], data)
+        slices[newslicexid].reset(0, 0, 0, 0, data)
 
     # Get actual running values from tc.
+    # Update slice totals and bandwidth.
     for params in bwlimit.get():
         (xid, share,
          minrate, maxrate,
@@ -372,14 +470,18 @@ def GetSlivers(data):
                 # Update byte counts
                 slice.update(maxrate, maxexemptrate, usedbytes, usedi2bytes, data)
         else:
+            # Just in case.  Probably (hopefully) this will never happen.
             # New slice, initialize state
             if verbose:
                 logger.log("bwmon: New Slice %s" % name)
-            slice = slices[xid] = Slice(xid, name, maxrate, maxexemptrate, usedbytes, usedi2bytes, data)
+            slice = slices[xid] = Slice(xid, name, data)
+            slice.reset(maxrate, maxexemptrate, usedbytes, usedi2bytes, data)
 
     # Delete dead slices
-    dead = Set(slices.keys()) - Set(live.values())
+    dead = Set(slices.keys()) - Set(live.keys())
     for xid in dead:
+        if xid == root_xid or xid == default_xid:
+            continue
         del slices[xid]
         bwlimit.off(xid)
 
@@ -399,74 +501,3 @@ def GetSlivers(data):
 def start(options, config):
     pass
 
-def format_bytes(bytes, si = True):
-    """
-    Formats bytes into a string
-    """
-    if si:
-        kilo = 1000.
-    else:
-        # Officially, a kibibyte
-        kilo = 1024.
-
-    if bytes >= (kilo * kilo * kilo):
-        return "%.1f GB" % (bytes / (kilo * kilo * kilo))
-    elif bytes >= 1000000:
-        return "%.1f MB" % (bytes / (kilo * kilo))
-    elif bytes >= 1000:
-        return "%.1f KB" % (bytes / kilo)
-    else:
-        return "%.0f bytes" % bytes
-
-def format_period(seconds):
-    """
-    Formats a period in seconds into a string
-    """
-
-    if seconds == (24 * 60 * 60):
-        return "day"
-    elif seconds == (60 * 60):
-        return "hour"
-    elif seconds > (24 * 60 * 60):
-        return "%.1f days" % (seconds / 24. / 60. / 60.)
-    elif seconds > (60 * 60):
-        return "%.1f hours" % (seconds / 60. / 60.)
-    elif seconds > (60):
-        return "%.1f minutes" % (seconds / 60.)
-    else:
-        return "%.0f seconds" % seconds
-
-def slicemail(slice, subject, body):
-    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]
-
-    if slice is not None and slice != "root":
-        to.append(PLC_MAIL_SLICE_ADDRESS.replace("SLICE", slice))
-
-    header = {'from': "%s Support <%s>" % (PLC_NAME, PLC_MAIL_SUPPORT_ADDRESS),
-              'to': ", ".join(to),
-              'version': sys.version.split(" ")[0],
-              'subject': subject}
-
-    # Write headers
-    sendmail.write(
-"""
-Content-type: text/plain
-From: %(from)s
-Reply-To: %(from)s
-To: %(to)s
-X-Mailer: Python/%(version)s
-Subject: %(subject)s
-
-""".lstrip() % header)
-
-    # Write body
-    sendmail.write(body)
-    # Done
-    sendmail.close()
-