[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] r22604: {arm} Adding bandwidth prepopulation from the state file. (in arm/trunk: . interface interface/graphing)



Author: atagar
Date: 2010-07-06 01:00:08 +0000 (Tue, 06 Jul 2010)
New Revision: 22604

Modified:
   arm/trunk/armrc.sample
   arm/trunk/interface/controller.py
   arm/trunk/interface/graphing/bandwidthStats.py
Log:
Adding bandwidth prepopulation from the state file.



Modified: arm/trunk/armrc.sample
===================================================================
--- arm/trunk/armrc.sample	2010-07-05 22:59:52 UTC (rev 22603)
+++ arm/trunk/armrc.sample	2010-07-06 01:00:08 UTC (rev 22604)
@@ -29,6 +29,7 @@
 features.graph.ps.secondaryStat rss
 features.graph.ps.cachedOnly true
 
+features.graph.bw.prepopulate true
 features.graph.bw.accounting.show true
 features.graph.bw.accounting.rate 10
 features.graph.bw.accounting.isTimeLong false
@@ -70,6 +71,7 @@
 log.panelRecreated DEBUG
 log.graph.ps.invalidStat WARN
 log.graph.ps.abandon WARN
+log.graph.bw.prepopulateFailure NOTICE
 log.connLookupFailed INFO
 log.connLookupFailover NOTICE
 log.connLookupAbandon WARN

Modified: arm/trunk/interface/controller.py
===================================================================
--- arm/trunk/interface/controller.py	2010-07-05 22:59:52 UTC (rev 22603)
+++ arm/trunk/interface/controller.py	2010-07-06 01:00:08 UTC (rev 22604)
@@ -42,7 +42,7 @@
   ["torrc"]]
 PAUSEABLE = ["header", "graph", "log", "conn"]
 
-CONFIG = {"features.graph.type": 1, "log.configEntryUndefined": log.NOTICE}
+CONFIG = {"features.graph.type": 1, "features.graph.bw.prepopulate": True, "log.configEntryUndefined": log.NOTICE}
 
 class ControlPanel(panel.Panel):
   """ Draws single line label for interface controls. """
@@ -402,6 +402,11 @@
   conn.add_event_listener(panels["conn"])
   conn.add_event_listener(sighupTracker)
   
+  # prepopulates bandwidth values from state file
+  if CONFIG["features.graph.bw.prepopulate"]:
+    isSuccessful = panels["graph"].stats["bandwidth"].prepopulateFromState()
+    if isSuccessful: panels["graph"].updateInterval = 4
+  
   # tells Tor to listen to the events we're interested
   loggedEvents = setEventListening(loggedEvents, isBlindMode)
   panels["log"].loggedEvents = loggedEvents # strips any that couldn't be set

Modified: arm/trunk/interface/graphing/bandwidthStats.py
===================================================================
--- arm/trunk/interface/graphing/bandwidthStats.py	2010-07-05 22:59:52 UTC (rev 22603)
+++ arm/trunk/interface/graphing/bandwidthStats.py	2010-07-06 01:00:08 UTC (rev 22604)
@@ -7,7 +7,7 @@
 from TorCtl import TorCtl
 
 import graphPanel
-from util import torTools, uiTools
+from util import log, sysTools, torTools, uiTools
 
 DL_COLOR, UL_COLOR = "green", "cyan"
 
@@ -18,8 +18,10 @@
 # valid keys for the accountingInfo mapping
 ACCOUNTING_ARGS = ("status", "resetTime", "read", "written", "readLimit", "writtenLimit")
 
-DEFAULT_CONFIG = {"features.graph.bw.accounting.show": True, "features.graph.bw.accounting.rate": 10, "features.graph.bw.accounting.isTimeLong": False}
+PREPOPULATE_FAILURE_MSG = "Unable to prepopulate bandwidth information (%s)"
 
+DEFAULT_CONFIG = {"features.graph.bw.accounting.show": True, "features.graph.bw.accounting.rate": 10, "features.graph.bw.accounting.isTimeLong": False, "log.graph.bw.prepopulateFailure": log.NOTICE}
+
 class BandwidthStats(graphPanel.GraphStats, TorCtl.PostEventListener):
   """
   Uses tor BW events to generate bandwidth usage graph.
@@ -52,8 +54,6 @@
     if self._config["features.graph.bw.accounting.show"]:
       self.isAccounting = conn.getInfo('accounting/enabled') == '1'
   
-  # TODO: Currently this function is unused and pending responses on #tor-dev
-  # to figure out if the feature's even viable. If not, scrape.
   def prepopulateFromState(self):
     """
     Attempts to use tor's state file to prepopulate values for the 15 minute
@@ -61,21 +61,104 @@
     returns True if successful and False otherwise.
     """
     
+    # gets the uptime (using the same parameters as the header panel to take
+    # advantage of caching
+    conn, uptime = torTools.getConn(), None
+    queryPid = conn.getMyPid()
+    if queryPid:
+      queryParam = ["%cpu", "rss", "%mem", "etime"]
+      queryCmd = "ps -p %s -o %s" % (queryPid, ",".join(queryParam))
+      psCall = sysTools.call(queryCmd, 3600, True)
+      
+      if psCall and len(psCall) == 2:
+        stats = psCall[1].strip().split()
+        if len(stats) == 4: uptime = stats[3]
     
+    # checks if tor has been running for at least a day, the reason being that
+    # the state tracks a day's worth of data and this should only prepopulate
+    # results associated with this tor instance
+    if not uptime or not "-" in uptime:
+      msg = PREPOPULATE_FAILURE_MSG % "insufficient uptime"
+      log.log(self._config["log.graph.bw.prepopulateFailure"], msg)
+      return False
+    
     # get the user's data directory (usually '~/.tor')
     dataDir = conn.getOption("DataDirectory")
-    if not dataDir: return False
+    if not dataDir:
+      msg = PREPOPULATE_FAILURE_MSG % "data directory not found"
+      log.log(self._config["log.graph.bw.prepopulateFailure"], msg)
+      return False
     
     # attempt to open the state file
     try: stateFile = open("%s/state" % dataDir, "r")
-    except IOError: return False
+    except IOError:
+      msg = PREPOPULATE_FAILURE_MSG % "unable to read the state file"
+      log.log(self._config["log.graph.bw.prepopulateFailure"], msg)
+      return False
     
-    # find the BWHistory lines (might not exist yet for new relays)
-    bwReadLine, bwWriteLine = None, None
+    # get the BWHistory entries (ordered oldest to newest) and number of
+    # intervals since last recorded
+    bwReadEntries, bwWriteEntries = None, None
+    missingReadEntries, missingWriteEntries = None, None
     
+    # converts from gmt to local with respect to DST
+    if time.localtime()[8]: tz_offset = time.altzone
+    else: tz_offset = time.timezone
+    
     for line in stateFile:
-      if line.startswith("BWHistoryReadValues"): bwReadLine = line
-      elif line.startswith("BWHistoryWriteValues"): bwWriteLine = line
+      line = line.strip()
+      
+      if line.startswith("BWHistoryReadValues"):
+        bwReadEntries = line[20:].split(",")
+        bwReadEntries = [int(entry) / 1024.0 / 900 for entry in bwReadEntries]
+      elif line.startswith("BWHistoryWriteValues"):
+        bwWriteEntries = line[21:].split(",")
+        bwWriteEntries = [int(entry) / 1024.0 / 900 for entry in bwWriteEntries]
+      elif line.startswith("BWHistoryReadEnds"):
+        lastReadTime = time.mktime(time.strptime(line[18:], "%Y-%m-%d %H:%M:%S")) - tz_offset
+        missingReadEntries = int((time.time() - lastReadTime) / 900)
+      elif line.startswith("BWHistoryWriteEnds"):
+        lastWriteTime = time.mktime(time.strptime(line[19:], "%Y-%m-%d %H:%M:%S")) - tz_offset
+        missingWriteEntries = int((time.time() - lastWriteTime) / 900)
+    
+    if not bwReadEntries or not bwWriteEntries or not lastReadTime or not lastWriteTime:
+      msg = PREPOPULATE_FAILURE_MSG % "bandwidth stats missing from state file"
+      log.log(self._config["log.graph.bw.prepopulateFailure"], msg)
+      return False
+    
+    # fills missing entries with the last value
+    bwReadEntries += [bwReadEntries[-1]] * missingReadEntries
+    bwWriteEntries += [bwWriteEntries[-1]] * missingWriteEntries
+    
+    # crops starting entries so they're the same size
+    entryCount = min(len(bwReadEntries), len(bwWriteEntries), self.maxCol)
+    bwReadEntries = bwReadEntries[len(bwReadEntries) - entryCount:]
+    bwWriteEntries = bwWriteEntries[len(bwWriteEntries) - entryCount:]
+    
+    # gets index for 15-minute interval
+    intervalIndex = 0
+    for indexEntry in graphPanel.UPDATE_INTERVALS:
+      if indexEntry[1] == 900: break
+      else: intervalIndex += 1
+    
+    # fills the graphing parameters with state information
+    for i in range(entryCount):
+      readVal, writeVal = bwReadEntries[i], bwWriteEntries[i]
+      
+      self.lastPrimary, self.lastSecondary = readVal, writeVal
+      self.primaryTotal += readVal * 900
+      self.secondaryTotal += writeVal * 900
+      self.tick += 900
+      
+      self.primaryCounts[intervalIndex].insert(0, readVal)
+      self.secondaryCounts[intervalIndex].insert(0, writeVal)
+    
+    self.maxPrimary[intervalIndex] = max(self.primaryCounts)
+    self.maxSecondary[intervalIndex] = max(self.secondaryCounts)
+    del self.primaryCounts[intervalIndex][self.maxCol + 1:]
+    del self.secondaryCounts[intervalIndex][self.maxCol + 1:]
+    
+    return True
   
   def bandwidth_event(self, event):
     if self.isAccounting and self.isNextTickRedraw():