merge changes from HEAD
authorMark Huang <mlhuang@cs.princeton.edu>
Fri, 15 Dec 2006 20:43:10 +0000 (20:43 +0000)
committerMark Huang <mlhuang@cs.princeton.edu>
Fri, 15 Dec 2006 20:43:10 +0000 (20:43 +0000)
bwmon.py
pl_mom.py
pl_mom.spec
swapmon.py

index 3aa88c1..3b4ab16 100755 (executable)
--- 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.3.2.5 2006/08/24 20:16:14 mlhuang Exp $
+# $Id: bwmon.py,v 1.15 2006/12/13 21:39:23 faiyaza Exp $
 #
 
 import syslog
@@ -44,15 +44,24 @@ 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_maxexemptrate = bwlimit.bwmax
 
+# What we cap to when slices break the rules.
 # 500 Kbit or 5.4 GB per day
-default_avgrate = 500000
-
+#default_avgrate = 500000
 # 1.5 Mbit or 16.4 GB per day
-default_avgexemptrate = 1500000
+#default_avgexemptrate = 1500000
+
+# 5.4 Gbyte per day. 5.4 * 1024 k * 1024M * 1024G 
+# 5.4 Gbyte per day max allowed transfered per recording period
+default_ByteMax = 5798205850
+default_ByteThresh = int(.8 * default_ByteMax) 
+# 16.4 Gbyte per day max allowed transfered per recording period to I2
+default_ExemptByteMax = 17609365914 
+default_ExemptByteThresh = int(.8 * default_ExemptByteMax) 
+
 
 # Average over 1 day
 period = 1 * seconds_per_day
@@ -64,11 +73,11 @@ The slice %(slice)s has transmitted more than %(bytes)s from
 %(hostname)s to %(class)s destinations
 since %(since)s.
 
-Its maximum %(class)s burst rate will be capped at %(avgrate)s
+Its maximum %(class)s burst rate will be capped at %(new_maxrate)s
 until %(until)s.
 
 Please reduce the average %(class)s transmission rate
-of the slice to %(avgrate)s, or %(limit)s per %(period)s.
+of the slice %(limit)s per %(period)s.
 
 """.lstrip()
 
@@ -86,31 +95,88 @@ class Slice:
     time - beginning of recording period in UNIX seconds
     bytes - low bandwidth bytes transmitted at the beginning of the recording period
     exemptbytes - high bandwidth bytes transmitted at the beginning of the recording period (for I2 -F)
-    last_avgrate - last recorded avgrate from NM
-    last_maxrate - last recorded maxrate from NM
-    last_avgexemptrate - last recorded avgexemptrate from NM
-    last_maxexemptrate - last recorded maxexemptrate from NM
+    ByteMax - total volume of data allowed
+    ByteThresh - After thresh, cap node to (maxbyte - bytes)/(time left in period)
+    ExemptByteMax - Same as above, but for i2.
+    ExemptByteThresh - i2 ByteThresh
+    maxrate - max_rate slice attribute. 
+    maxexemptrate - max_exempt_rate slice attribute.
+    self.emailed = did we email during this recording period
+
     """
 
     def __init__(self, xid, name, maxrate, maxexemptrate, bytes, exemptbytes):
         self.xid = xid
         self.name = name
-       self.time = 0
-       self.exemptbytes = 0
-       self.last_maxrate = default_maxrate
-       self.last_avgrate = default_avgrate
-       self.last_avgexemptrate = default_avgexemptrate 
-       self.last_maxexemptrate = default_maxexemptrate 
+        self.time = 0
+        self.bytes = 0
+        self.exemptbytes = 0
+        self.ByteMax = default_ByteMax
+        self.ByteThresh = default_ByteThresh
+        self.ExemptByteMax = default_ExemptByteMax
+        self.ExemptByteThresh = default_ExemptByteThresh
+        self.maxrate = default_maxrate
+        self.maxexemptrate = default_maxexemptrate
+        self.emailed = False
+
+        # Get real values where applicable
         self.reset(maxrate, maxexemptrate, bytes, exemptbytes)
 
     def __repr__(self):
         return self.name
 
+    def updateSliceAttributes(self):
+        # Query Node Manager for max rate overrides
+        try:
+            vals = nm.query(self.name, 
+                [('nm_net_max_rate', self.maxrate),
+                ('nm_net_max_exempt_rate', self.maxexemptrate),
+                ("nm_net_max_byte", int(self.ByteMax / 1024)),
+                ("nm_net_max_exempt_byte", int(self.ExemptByteMax / 1024)),
+                ("nm_net_max_thresh_byte", int( .8 * self.ByteMax / 1024)),
+                ("nm_net_max_thresh_exempt_byte", int(.8 * self.ExemptByteMax / 1024)),
+                ("nm_net_avg_rate", 0),
+                ("nm_net_avg_exempt_rate", 0)])
+
+            (self.maxrate,
+                self.maxexemptrate,
+                ByteMax,
+                ExemptByteMax,
+                ByteThresh,
+                ExemptByteThresh,
+                avgrate,
+                avgexemptrate) = vals
+            
+            # The shitty bit.  Gotta bias the limits so as not to overflow xmlrpc
+            self.ByteMax = ByteMax * 1024 
+            self.ByteThresh = ByteThresh * 1024 
+            self.ExemptByteMax = ExemptByteMax * 1024 
+            self.ExemptByteThresh = ExemptByteThresh * 1024 
+
+            # The hack here is that when i pass 0 to the xmlrpc request to NM, 
+            # for rate limits and it comes back non zero, then screw the byte limits.
+            # Mult by the period and recompute the byte limits.  The thought is
+            # If/when PLC switches to byte limits, the avgrates wont be used as
+            # slice attributes and will return as 0
+            if (avgrate != 0):
+                self.ByteMax = avgrate * period 
+                self.ByteThresh = int(self.ByteMax * .8)
+
+            if (avgexemptrate != 0):
+                self.ExemptByteMax = avgexemptrate * period
+                self.ExemptByteThresh = int(self.ExemptByteMax * .8)
+
+        except Exception, err:
+            print "Warning: Exception received while querying NM:", err
+
     def reset(self, maxrate, maxexemptrate, bytes, exemptbytes):
         """
         Begin a new recording period. Remove caps by restoring limits
         to their default values.
         """
+        
+        # Query Node Manager for max rate overrides
+        self.updateSliceAttributes()    
 
         # Reset baseline time
         self.time = time.time()
@@ -119,59 +185,33 @@ class Slice:
         self.bytes = bytes
         self.exemptbytes = exemptbytes
 
-       # If NM except"ns below, and new_max* doesn't get set, use last.
-       new_maxrate = self.last_maxrate
-       new_maxexemptrate = self.last_maxexemptrate
-
-        # Query Node Manager for max rate overrides
-       try:
-               vals = nm.query(self.name, [('nm_net_max_rate', self.last_maxrate),
-                               ('nm_net_max_exempt_rate', self.last_maxexemptrate),
-                               ('nm_net_avg_rate', self.last_avgrate),
-                               ('nm_net_avg_exempt_rate', self.last_avgexemptrate)])
-               (new_maxrate, new_maxexemptrate, 
-                       self.last_avgrate, self.last_avgexemptrate) = vals 
-               #If NM is alive, and there is a cap, update new
-               self.last_maxrate = new_maxrate
-               self.last_maxexemptrate = new_maxexemptrate
-
-       except Exception, err:
-               print "Warning: Exception received while querying NM:", err
+        # Reset email 
+        self.emailed = False
 
-        if new_maxrate != maxrate or new_maxexemptrate != maxexemptrate:
+        if (self.maxrate != maxrate) or (self.maxexemptrate != maxexemptrate):
             print "%s reset to %s/%s" % \
                   (self.name,
-                   bwlimit.format_tc_rate(new_maxrate),
-                   bwlimit.format_tc_rate(new_maxexemptrate))
-            bwlimit.set(xid = self.xid, maxrate = new_maxrate, maxexemptrate = new_maxexemptrate)
+                   bwlimit.format_tc_rate(self.maxrate),
+                   bwlimit.format_tc_rate(self.maxexemptrate))
+            bwlimit.set(xid = self.xid, maxrate = self.maxrate, maxexemptrate = self.maxexemptrate)
 
     def update(self, maxrate, maxexemptrate, bytes, exemptbytes):
         """
-        Update byte counts and check if average rates have been
-        exceeded. In the worst case (instantaneous usage of the entire
-        average daily byte limit at the beginning of the recording
-        period), the slice will be immediately capped and will get to
-        send twice the average daily byte limit. In the common case,
-        it will get to send slightly more than the average daily byte
-        limit.
+        Update byte counts and check if byte limits have been
+        exceeded. 
         """
-       
-       # If NM except'ns below, and avg*rate doesn't get set, use last_*.
-       avgrate = self.last_avgrate
-       avgexemptrate = self.last_avgexemptrate
-
-        # Query Node Manager for max average rate overrides
-       try:
-               (avgrate, avgexemptrate) = nm.query(self.name, 
-                       [('nm_net_avg_rate', self.last_avgrate), 
-                       ('nm_net_avg_exempt_rate', self.last_avgexemptrate)])
-               #If NM is alive, and there is a cap, update new
-               self.last_avgexemptrate = avgexemptrate
-               self.last_avgrate = avgrate
-        except Exception, err:
-                print "Warning: Exception received while querying NM:", err
-       # Prepare message parameters from the template
+    
+        # Query Node Manager for max rate overrides
+        self.updateSliceAttributes()    
+     
+        if verbose:
+            print("\n%s slice attributes "\
+                "maxrate %s, maxexemptrate %s" % \
+                (self.name, 
+                bwlimit.format_tc_rate(maxrate), 
+                bwlimit.format_tc_rate(maxexemptrate)))
+
+        # Prepare message parameters from the template
         message = ""
         params = {'slice': self.name, 'hostname': socket.gethostname(),
                   'since': time.asctime(time.gmtime(self.time)) + " GMT",
@@ -179,10 +219,9 @@ class Slice:
                   'date': time.asctime(time.gmtime()) + " GMT",
                   'period': format_period(period)} 
 
-        bytelimit = avgrate * period / bits_per_byte
-        if bytes >= (self.bytes + bytelimit) and \
-           maxrate > avgrate:
-            new_maxrate = avgrate
+        if bytes >= (self.bytes + self.ByteThresh):
+            new_maxrate = \
+            int((self.ByteMax - self.bytes + bytes)/(period - time.time() - self.time))
         else:
             new_maxrate = maxrate
 
@@ -190,23 +229,22 @@ class Slice:
         params['class'] = "low bandwidth"
         params['bytes'] = format_bytes(bytes - self.bytes)
         params['maxrate'] = bwlimit.format_tc_rate(maxrate)
-        params['limit'] = format_bytes(bytelimit)
-        params['avgrate'] = bwlimit.format_tc_rate(avgrate)
+        params['limit'] = format_bytes(self.ByteMax)
+        params['new_maxrate'] = bwlimit.format_tc_rate(new_maxrate)
 
         if verbose:
             print "%(slice)s %(class)s " \
-                  "%(bytes)s, %(limit)s (%(maxrate)s max/%(avgrate)s avg)" % \
+                  "%(bytes)s of %(limit)s (%(new_maxrate)s maxrate)" % \
                   params
 
         # Cap low bandwidth burst rate
         if new_maxrate != maxrate:
             message += template % params
-            print "%(slice)s %(class)s capped at %(avgrate)s (%(bytes)s/%(limit)s)" % params
-
-        exemptbytelimit = avgexemptrate * period / bits_per_byte
-        if exemptbytes >= (self.exemptbytes + exemptbytelimit) and \
-           maxexemptrate > avgexemptrate:
-            new_maxexemptrate = avgexemptrate
+            print "%(slice)s %(class)s capped at %(new_maxrate)s " % params
+    
+        if exemptbytes >= (self.exemptbytes + self.ExemptByteThresh):
+            new_maxexemptrate = \
+            int((self.ExemptByteMax - (self.bytes + bytes))/(period - (time.time() - self.time)))
         else:
             new_maxexemptrate = maxexemptrate
 
@@ -214,43 +252,45 @@ class Slice:
         params['class'] = "high bandwidth"
         params['bytes'] = format_bytes(exemptbytes - self.exemptbytes)
         params['maxrate'] = bwlimit.format_tc_rate(maxexemptrate)
-        params['limit'] = format_bytes(exemptbytelimit)
-        params['avgrate'] = bwlimit.format_tc_rate(avgexemptrate)
+        params['limit'] = format_bytes(self.ExemptByteMax)
+        params['new_maxexemptrate'] = bwlimit.format_tc_rate(new_maxexemptrate)
 
         if verbose:
             print "%(slice)s %(class)s " \
-                  "%(bytes)s, %(limit)s (%(maxrate)s max /%(avgrate)s avg)" % \
-                  params
+                  "%(bytes)s of %(limit)s (%(new_maxrate)s maxrate)" % params
 
         # Cap high bandwidth burst rate
         if new_maxexemptrate != maxexemptrate:
             message += template % params
-            print "%(slice)s %(class)s capped at %(avgrate)s (%(bytes)s/%(limit)s)" % params
+            print "%(slice)s %(class)s capped at %(new_maxexemptrate)s" % params
 
         # Apply parameters
         if new_maxrate != maxrate or new_maxexemptrate != maxexemptrate:
             bwlimit.set(xid = self.xid, maxrate = new_maxrate, maxexemptrate = new_maxexemptrate)
 
         # Notify slice
-        if message:
+        if message and self.emailed == False:
             subject = "pl_mom capped bandwidth of slice %(slice)s on %(hostname)s" % params
             if debug:
                 print subject
                 print message + (footer % params)
             else:
+                self.emailed = True
                 slicemail(self.name, subject, message + (footer % params))
 
+
+
 def usage():
     print """
 Usage: %s [OPTIONS]...
 
 Options:
-        -d, --debug             Enable debugging (default: %s)
-        -v, --verbose           Increase verbosity level (default: %d)
-        -f, --file=FILE         Data file (default: %s)
-        -s, --slice=SLICE       Constrain monitoring to these slices (default: all)
+        -d, --debug            Enable debugging (default: %s)
+        -v, --verbose            Increase verbosity level (default: %d)
+        -f, --file=FILE            Data file (default: %s)
+        -s, --slice=SLICE        Constrain monitoring to these slices (default: all)
         -p, --period=SECONDS    Interval in seconds over which to enforce average byte limits (default: %s)
-        -h, --help              This message
+        -h, --help                This message
 """.lstrip() % (sys.argv[0], debug, verbose, datafile, format_period(period))
 
 def main():
@@ -298,21 +338,22 @@ def main():
         (version, slices) = pickle.load(f)
         f.close()
         # Check version of data file
-        if version != "$Id: bwmon.py,v 1.3.2.5 2006/08/24 20:16:14 mlhuang Exp $":
+        if version != "$Id: bwmon.py,v 1.15 2006/12/13 21:39:23 faiyaza Exp $":
             print "Not using old version '%s' data file %s" % (version, datafile)
             raise Exception
     except Exception:
-        version = "$Id: bwmon.py,v 1.3.2.5 2006/08/24 20:16:14 mlhuang Exp $"
+        version = "$Id: bwmon.py,v 1.15 2006/12/13 21:39:23 faiyaza Exp $"
         slices = {}
 
     # Get special slice IDs
     root_xid = bwlimit.get_xid("root")
     default_xid = bwlimit.get_xid("default")
 
-    #Open connection to Node Manager
+    #Open connection to Node Manager. Global.
     nm = NM()
 
     live = []
+    # Get actuall running values from tc.
     for params in bwlimit.get():
         (xid, share,
          minrate, maxrate,
@@ -332,9 +373,8 @@ def main():
         # Monitor only the specified slices
         if names and name not in names:
             continue
-
-       #slices is populated from the pickle file
-       #xid is populated from bwlimit (read from /etc/passwd) 
+        #slices is populated from the pickle file
+        #xid is populated from bwlimit (read from /etc/passwd) 
         if slices.has_key(xid):
             slice = slices[xid]
             if time.time() >= (slice.time + period) or \
index 309609c..cf0f2ea 100644 (file)
--- a/pl_mom.py
+++ b/pl_mom.py
@@ -5,7 +5,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: pl_mom.py,v 1.3.2.4 2006/08/24 20:16:14 mlhuang Exp $
+# $Id: pl_mom.py,v 1.6 2006/11/18 18:00:14 mlhuang Exp $
 #
 
 import os
@@ -86,7 +86,7 @@ def removepid(prog):
     os.unlink("/var/run/%s.pid" % prog)
 
 def slicemail(slice, subject, body):
-    sendmail = os.popen("/usr/sbin/sendmail -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":
index 025ddf4..559e7ea 100644 (file)
@@ -1,6 +1,6 @@
 %define name pl_mom
 %define version 0.6
-%define release 8%{?pldistro:.%{pldistro}}%{?date:.%{date}}
+%define release 9%{?pldistro:.%{pldistro}}%{?date:.%{date}}
  
 Summary: PlanetLab node monitoring tools
 Name: %{name}
index 354d216..d1eda7b 100755 (executable)
@@ -10,7 +10,7 @@
 # Faiyaz Ahmed <faiyaza@cs.princeton.edu>
 # Copyright (C) 2004-2006 The Trustees of Princeton University
 #
-# $Id: swapmon.py,v 1.5.2.4 2006/08/24 20:16:14 mlhuang Exp $
+# $Id: swapmon.py,v 1.11 2006/12/02 19:11:47 mlhuang Exp $
 #
 
 import syslog
@@ -92,7 +92,7 @@ Sometime before %(date)s, swap space was
 nearly exhausted on %(hostname)s.
 
 Slice %(slice)s was reset since it was the largest consumer of
-physical memory at %(rss)s (%(percent)4.1f%%).
+physical memory at %(rss)s (%(percent)4.1f%%) (%(sz)s writable).
 
 Please reply to this message explaining the nature of your experiment,
 and what you are doing to address the problem.
@@ -112,8 +112,8 @@ Sometime before %(date)s, swap space was
 nearly exhausted on %(hostname)s.
 
 System slice %(slice)s was the largest consumer of physical memory at
-%(rss)s (%(percent)4.1f%%). It was not reset, but please verify its
-behavior.
+%(rss)s (%(percent)4.1f%%) (%(sz)s writable). It was not reset,
+but please verify its behavior.
 
 %(slice)s processes prior to alarm:
 
@@ -130,7 +130,8 @@ Sometime before %(date)s, swap space was
 nearly exhausted on %(hostname)s.
 
 Slice %(slice)s was killed since it was the largest consumer of
-physical memory at %(rss)s (%(percent)4.1f%%) after repeated restarts.
+physical memory at %(rss)s (%(percent)4.1f%%) (%(sz)s writable)
+after repeated restarts.
 
 Please reply to this message explaining the nature of your experiment,
 and what you are doing to address the problem.
@@ -268,21 +269,22 @@ Options:
 def slicestat(names = None):
     """
     Get status of specified slices (if names is None or empty, all
-    slices). vsize and rss are in KiB. Returns
+    slices). vsize, sz, and rss are in KiB. Returns
 
     {xid: {'xid': slice_id,
            'name': slice_name,
            'procs': [{'pid': pid, 'xid': slice_id, 'user', username, 'cmd': command,
-                      'vsize': virtual_kib, 'rss': physical_kib,
+                      'vsize': virtual_kib, 'sz': potential_kib, 'rss': physical_kib,
                       'pcpu': cpu_percent, 'pmem': mem_percent}]
            'vsize': total_virtual_kib,
+          'sz': total_potential_kib,
            'rss': total_physical_kib}}
     """
     
     # Mandatory fields. xid is a virtual field inserted by vps. Make
     # sure cmd is last so that it does not get truncated
     # automatically.
-    fields = ['pid', 'xid', 'user', 'vsize', 'rss', 'pcpu', 'pmem', 'cmd']
+    fields = ['pid', 'xid', 'user', 'vsize', 'sz', 'rss', 'pcpu', 'pmem', 'cmd']
 
     # vps inserts xid after pid in the output, but ps doesn't know
     # what the field means.
@@ -356,10 +358,11 @@ def slicestat(names = None):
         if slices.has_key(proc['xid']):
             slice = slices[proc['xid']]
         else:
-            slice = {'xid': proc['xid'], 'name': name, 'procs': [], 'vsize': 0, 'rss': 0}
+            slice = {'xid': proc['xid'], 'name': name, 'procs': [], 'vsize': 0, 'sz': 0, 'rss': 0}
 
         slice['procs'].append(proc)
         slice['vsize'] += proc['vsize']
+       slice['sz'] += proc['sz']
         slice['rss'] += proc['rss']
 
         slices[proc['xid']] = slice
@@ -368,18 +371,25 @@ def slicestat(names = None):
 
 def memtotal():
     """
-    Returns total physical memory on the system in KiB.
+    Returns total physical and swap memory on the system in KiB.
     """
 
+    mem = 0
+    swap = 0
+
     meminfo = open("/proc/meminfo", "r")
-    line = meminfo.readline()
+    for line in meminfo.readlines():
+       try:
+           (name, value, kb) = line.split()
+       except:
+           continue
+       if name == "MemTotal:": 
+           mem = int(value)
+       elif name == "SwapTotal:":
+           swap = int(value)
     meminfo.close()
-    if line[0:8] == "MemTotal":
-        # MemTotal: 255396 kB
-        (name, value, kb) = line.split()
-        return int(value)
 
-    return 0
+    return (mem, swap)
 
 def swap_used():
     """
@@ -407,19 +417,26 @@ def swap_used():
 
     return 100 * total_used / total_swap
 
-def summary(names = None, total_rss = memtotal()):
+def summary(slices = None, total_mem = None, total_swap = None):
     """
     Return a summary of memory usage by slice.
     """
-    slicelist = slicestat(names).values()
-    slicelist.sort(lambda a, b: b['rss'] - a['rss'])
-
-    table = "%-20s%10s%24s\n\n" % ("Slice", "Processes", "Memory Usage")
+    if not slices:
+       slices = slicestat()
+    slicelist = slices.values()
+    slicelist.sort(lambda a, b: b['sz'] - a['sz'])
+    if total_mem is None or total_swap is None:
+       (total_mem, total_swap) = memtotal()
+
+    table = "%-20s%10s%24s%24s\n\n" % ("Slice", "Processes", "Memory Usage", "Potential Usage")
     for slice in slicelist:
-        table += "%-20s%10d%16s (%4.1f%%)\n" % \
+        table += "%-20s%10d%16s (%4.1f%%)%16s (%4.1f%%)\n" % \
                  (slice['name'], len(slice['procs']),
                   format_bytes(slice['rss'] * 1024, si = False),
-                  100. * slice['rss'] / total_rss)
+                  100. * slice['rss'] / total_mem,
+                  format_bytes(slice['sz'] * 1024, si = False),
+                  100. * slice['sz'] / (total_mem + total_swap))
+                 
 
     return table
 
@@ -456,10 +473,12 @@ def main():
             reset_thresh = int(optval)
         elif opt == "--reboot-thresh":
             reboot_thresh = int(optval)
+        elif opt == "--min-thresh":
+            rss_min = int(optval)
         elif opt == "--system-slice":
             system_slices.append(optval)
         elif opt == "--status":
-            print summary(names)
+            print summary(slicestat(names))
             sys.exit(0)
         else:
             usage()
@@ -476,8 +495,8 @@ def main():
         syslog.openlog("swapmon")
         sys.stdout = sys.stderr = Logger()
 
-    # Get total physical memory
-    total_rss = memtotal()
+    # Get total memory
+    (total_mem, total_swap) = memtotal()
 
     try:
         f = open(datafile, "r+")
@@ -486,13 +505,13 @@ def main():
         (version, slices) = pickle.load(f)
         f.close()
         # Check version of data file
-        if version != "$Id: swapmon.py,v 1.5.2.4 2006/08/24 20:16:14 mlhuang Exp $":
+        if version != "$Id: swapmon.py,v 1.11 2006/12/02 19:11:47 mlhuang Exp $":
             print "Not using old version '%s' data file %s" % (version, datafile)
             raise Exception
 
         params = {'hostname': socket.gethostname(),
                   'date': time.asctime(time.gmtime()) + " GMT",
-                  'table': summary(total_rss)}
+                  'table': summary(slices, total_mem, total_swap)}
 
         if debug:
             print rebooted_subject % params
@@ -503,7 +522,7 @@ def main():
         # Delete data file
         os.unlink(datafile)
     except Exception:
-        version = "$Id: swapmon.py,v 1.5.2.4 2006/08/24 20:16:14 mlhuang Exp $"
+        version = "$Id: swapmon.py,v 1.11 2006/12/02 19:11:47 mlhuang Exp $"
         slices = {}
 
     # Query process table every 30 seconds, or when a large change in
@@ -522,7 +541,7 @@ def main():
         used = swap_used()
 
        for resetslice in resetlist.keys():
-               resetlist[resetslice].update()
+           resetlist[resetslice].update()
        
         if last_used is None:
             last_used = used
@@ -550,7 +569,7 @@ def main():
             slicelist = slices.values()
             slicelist.sort(lambda a, b: b['rss'] - a['rss'])
             for slice in slicelist:
-                percent = 100. * slice['rss'] / total_rss
+                percent = 100. * slice['rss'] / total_mem
 
                 if slice['rss'] < rss_min:
                     continue
@@ -563,11 +582,12 @@ def main():
 
                 slice['procs'].sort(lambda a, b: b['rss'] - a['rss'])
 
-                table = "%5s %10s %10s %4s %4s %s\n\n" % ("PID", "VIRT", "RES", '%CPU', '%MEM', 'COMMAND')
+                table = "%5s %10s %10s %10s %4s %4s %s\n\n" % ("PID", "VIRT", "SZ", "RES", '%CPU', '%MEM', 'COMMAND')
                 for proc in slice['procs']:
-                    table += "%5s %10s %10s %4.1f %4.1f %s\n" % \
+                    table += "%5s %10s %10s %10s %4.1f %4.1f %s\n" % \
                              (proc['pid'],
                               format_bytes(proc['vsize'] * 1024, si = False),
+                              format_bytes(proc['sz'] * 1024, si = False),
                               format_bytes(proc['rss'] * 1024, si = False),
                               proc['pcpu'], proc['pmem'], proc['cmd'])
 
@@ -576,27 +596,29 @@ def main():
                           'table': table,
                           'slice': slice['name'],
                           'rss': format_bytes(slice['rss'] * 1024, si = False),
+                         'sz': format_bytes(slice['sz'] * 1024, si = False),
                           'percent': percent}
 
                 # Match slice name against system slice patterns
                 is_system_slice = filter(None, [re.match(pattern, slice['name']) for pattern in system_slices])
 
                 if is_system_slice: 
-                       if slice['name'] not in warned:
-                                       warned.append(slice['name'])
-                                       if debug:
-                                               print alarm_subject % params
-                                               print alarm_body % params
-                               else:
-                                       print "Warning slice " + slice['name']
-                                       slicemail(slice['name'], alarm_subject % params, 
-                                       alarm_body % params)
+                   # Do not reset system slices, just warn once
+                   if slice['name'] not in warned:
+                       warned.append(slice['name'])
+                       if debug:
+                           print alarm_subject % params
+                           print alarm_body % params
+                       else:
+                           print "Warning slice " + slice['name']
+                           slicemail(slice['name'], alarm_subject % params, 
+                                     alarm_body % params)
                 else:
-                       # Reset slice
-                       if not resetlist.has_key(slice['name']):
-                               resetlist[slice['name']] = Reset(slice['name'])
-                        resetlist[slice['name']].reset(params)
-                       slices = slicestat(names)
+                   # Reset slice
+                   if not resetlist.has_key(slice['name']):
+                       resetlist[slice['name']] = Reset(slice['name'])
+                   resetlist[slice['name']].reset(params)
+                   slices = slicestat(names)
 
         if timer <= 0 or used >= (last_used + change_thresh):
             if used >= (last_used + change_thresh):