[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r22573: {arm} Client side caching for several frequently fetched parameter (in arm/trunk: interface interface/graphing util)
Author: atagar
Date: 2010-06-30 05:40:05 +0000 (Wed, 30 Jun 2010)
New Revision: 22573
Modified:
arm/trunk/interface/controller.py
arm/trunk/interface/graphing/bandwidthStats.py
arm/trunk/interface/graphing/psStats.py
arm/trunk/interface/headerPanel.py
arm/trunk/interface/logPanel.py
arm/trunk/util/torTools.py
Log:
Client side caching for several frequently fetched parameters (fingerprint, flags, etc) as well as several bug fixes.
Modified: arm/trunk/interface/controller.py
===================================================================
--- arm/trunk/interface/controller.py 2010-06-30 02:56:50 UTC (rev 22572)
+++ arm/trunk/interface/controller.py 2010-06-30 05:40:05 UTC (rev 22573)
@@ -340,7 +340,7 @@
except curses.error: pass
# attempts to determine tor's current pid (left as None if unresolveable, logging an error later)
- torPid = torTools.getConn().getPid()
+ torPid = torTools.getConn().getMyPid()
try:
confLocation = conn.get_info("config-file")["config-file"]
@@ -450,7 +450,7 @@
# other panels that use torrc data
panels["conn"].resetOptions()
#if not isBlindMode: panels["graph"].stats["connections"].resetOptions(conn)
- panels["graph"].stats["bandwidth"].resetOptions()
+ #panels["graph"].stats["bandwidth"].resetOptions()
# if bandwidth graph is being shown then height might have changed
if panels["graph"].currentDisplay == "bandwidth":
Modified: arm/trunk/interface/graphing/bandwidthStats.py
===================================================================
--- arm/trunk/interface/graphing/bandwidthStats.py 2010-06-30 02:56:50 UTC (rev 22572)
+++ arm/trunk/interface/graphing/bandwidthStats.py 2010-06-30 05:40:05 UTC (rev 22573)
@@ -20,76 +20,20 @@
DEFAULT_CONFIG = {"features.graph.bw.accounting.show": True, "features.graph.bw.accounting.rate": 10, "features.graph.bw.accounting.isTimeLong": False}
-class ObservedBandwidthTracker(TorCtl.PostEventListener):
+class BandwidthStats(graphPanel.GraphStats, TorCtl.PostEventListener):
"""
- This keeps track of the relay's current observed bandwidth (the throughput
- noted by the directory authorities and used by clients for relay selection).
- This is a parameter of the descriptors and hence is likely to get stale.
- """
-
- def __init__(self):
- TorCtl.PostEventListener.__init__(self)
- self.observedBandwidth = None
-
- conn = torTools.getConn()
- conn.addEventListener(self) # listenes for NEWDESC events
- conn.addStatusListener(self.resetListener) # listens for new controllers
-
- self._parseDescriptors()
-
- def getObservedBandwidth(self):
- """
- Reports the observed bandwidth noted in the cached descriptors. This
- provides None if descriptors are unavailable or otherwise unable to be
- parsed.
- """
-
- return self.observedBandwidth
-
- def new_desc_event(self, event):
- myFingerprint = torTools.getConn().getFingerprint()
- if not myFingerprint or myFingerprint in event.idlist:
- self._parseDescriptors()
-
- def _parseDescriptors(self):
- conn = torTools.getConn()
- myFingerprint = conn.getInfo("fingerprint")
- myDescLines = []
-
- if myFingerprint:
- myDesc = conn.getInfo("desc/id/%s" % myFingerprint)
- if myDesc: myDescLines = myDesc.split("\n")
-
- for line in myDescLines:
- if line.startswith("bandwidth"):
- # line should look something like:
- # bandwidth 40960 102400 47284
- comp = line.split()
-
- if len(comp) == 4 and comp[-1].isdigit():
- self.observedBandwidth = uiTools.getSizeLabel(int(comp[-1]), 1)
- return
-
- self.observedBandwidth = None # no bandwidth desc entry found
-
- def resetListener(self, conn, eventType):
- if eventType == torTools.TOR_INIT: self._parseDescriptors()
-
-class BandwidthStats(graphPanel.GraphStats):
- """
Uses tor BW events to generate bandwidth usage graph.
"""
def __init__(self, config=None):
graphPanel.GraphStats.__init__(self)
+ TorCtl.PostEventListener.__init__(self)
self._config = dict(DEFAULT_CONFIG)
if config:
config.update(self._config)
self._config["features.graph.bw.accounting.rate"] = max(1, self._config["features.graph.bw.accounting.rate"])
- self.observedBwTracker = ObservedBandwidthTracker()
-
# accounting data (set by _updateAccountingInfo method)
self.accountingLastUpdated = 0
self.accountingInfo = dict([(arg, "") for arg in ACCOUNTING_ARGS])
@@ -97,41 +41,16 @@
# listens for tor reload (sighup) events which can reset the bandwidth
# rate/burst and if tor's using accounting
conn = torTools.getConn()
- self.isAccounting, self.bwRate, self.bwBurst = False, None, None
+ self._titleStats, self.isAccounting = [], False
self.resetListener(conn, torTools.TOR_INIT) # initializes values
conn.addStatusListener(self.resetListener)
def resetListener(self, conn, eventType):
- # queries for rate, burst, and accounting status if it might have changed
- if eventType == torTools.TOR_INIT:
- if self._config["features.graph.bw.accounting.show"]:
- self.isAccounting = conn.getInfo('accounting/enabled') == '1'
-
- # effective relayed bandwidth is the minimum of BandwidthRate,
- # MaxAdvertisedBandwidth, and RelayBandwidthRate (if set)
- effectiveRate = int(conn.getOption("BandwidthRate"))
-
- relayRate = conn.getOption("RelayBandwidthRate")
- if relayRate and relayRate != "0":
- effectiveRate = min(effectiveRate, int(relayRate))
-
- maxAdvertised = conn.getOption("MaxAdvertisedBandwidth")
- if maxAdvertised: effectiveRate = min(effectiveRate, int(maxAdvertised))
-
- # effective burst (same for BandwidthBurst and RelayBandwidthBurst)
- effectiveBurst = int(conn.getOption("BandwidthBurst"))
-
- relayBurst = conn.getOption("RelayBandwidthBurst")
- if relayBurst and relayBurst != "0":
- effectiveBurst = min(effectiveBurst, int(relayBurst))
-
- self.bwRate = uiTools.getSizeLabel(effectiveRate, 1)
- self.bwBurst = uiTools.getSizeLabel(effectiveBurst, 1)
-
- # if both are using rounded values then strip off the ".0" decimal
- if ".0" in self.bwRate and ".0" in self.bwBurst:
- self.bwRate = self.bwRate.replace(".0", "")
- self.bwBurst = self.bwBurst.replace(".0", "")
+ # updates title parameters and accounting status if they changed
+ self.new_desc_event(None) # updates title params
+
+ if self._config["features.graph.bw.accounting.show"]:
+ self.isAccounting = conn.getInfo('accounting/enabled') == '1'
def bandwidth_event(self, event):
if self.isAccounting and self.isNextTickRedraw():
@@ -183,10 +102,7 @@
panel.addfstr(10, 0, "<b>Accounting:</b> Connection Closed...")
def getTitle(self, width):
- stats, observedBw = [], self.observedBwTracker.getObservedBandwidth()
- if self.bwRate: stats.append("limit: %s" % self.bwRate)
- if self.bwBurst: stats.append("burst: %s" % self.bwBurst)
- if observedBw: stats.append("observed: %s" % observedBw)
+ stats = list(self._titleStats)
while True:
if not stats: return "Bandwidth:"
@@ -227,6 +143,28 @@
def getPreferredHeight(self):
return 13 if self.isAccounting else 10
+ def new_desc_event(self, event):
+ # updates self._titleStats with updated values
+ conn = torTools.getConn()
+ myFingerprint = conn.getMyFingerprint()
+
+ if not self._titleStats or not myFingerprint or (event and myFingerprint in event.idlist):
+ bwRate = uiTools.getSizeLabel(conn.getMyBandwidthRate(), 1)
+ bwBurst = uiTools.getSizeLabel(conn.getMyBandwidthBurst(), 1)
+ bwObserved = conn.getMyBandwidthObserved()
+
+ # if both are using rounded values then strip off the ".0" decimal
+ if ".0" in bwRate and ".0" in bwBurst:
+ bwRate = bwRate.replace(".0", "")
+ bwBurst = bwBurst.replace(".0", "")
+
+ stats = []
+ stats.append("limit: %s" % bwRate)
+ stats.append("burst: %s" % bwBurst)
+ if bwObserved: stats.append("observed: %s" % uiTools.getSizeLabel(bwObserved, 1))
+
+ self._titleStats = stats
+
def _getAvgLabel(self, isPrimary):
total = self.primaryTotal if isPrimary else self.secondaryTotal
return "avg: %s/sec" % uiTools.getSizeLabel((total / max(1, self.tick)) * 1024, 1)
Modified: arm/trunk/interface/graphing/psStats.py
===================================================================
--- arm/trunk/interface/graphing/psStats.py 2010-06-30 02:56:50 UTC (rev 22572)
+++ arm/trunk/interface/graphing/psStats.py 2010-06-30 05:40:05 UTC (rev 22573)
@@ -26,7 +26,7 @@
self._config = dict(DEFAULT_CONFIG)
if config: config.update(self._config)
- self.queryPid = torTools.getConn().getPid()
+ self.queryPid = torTools.getConn().getMyPid()
self.queryParam = [self._config["features.graph.ps.primaryStat"], self._config["features.graph.ps.secondaryStat"]]
# If we're getting the same stats as the header panel then issues identical
Modified: arm/trunk/interface/headerPanel.py
===================================================================
--- arm/trunk/interface/headerPanel.py 2010-06-30 02:56:50 UTC (rev 22572)
+++ arm/trunk/interface/headerPanel.py 2010-06-30 05:40:05 UTC (rev 22573)
@@ -294,7 +294,7 @@
self.vals["sys/os"] = unameVals[0]
self.vals["sys/version"] = unameVals[2]
- pid = conn.getPid()
+ pid = conn.getMyPid()
self.vals["ps/pid"] = pid if pid else ""
# reverts volatile parameters to defaults
@@ -313,16 +313,9 @@
if self.vals["tor/address"] == "Unknown":
volatile["tor/address"] = conn.getInfo("address", self.vals["tor/address"])
- volatile["tor/fingerprint"] = conn.getFingerprint(self.vals["tor/fingerprint"])
+ volatile["tor/fingerprint"] = conn.getMyFingerprint(self.vals["tor/fingerprint"])
+ volatile["tor/flags"] = conn.getMyFlags(self.vals["tor/flags"])
- # sets flags
- nsEntry = conn.getNetworkStatus()
- if nsEntry:
- # network status contains a couple of lines, looking like:
- # r caerSidi p1aag7VwarGxqctS7/fS0y5FU+s 9On1TRGCEpljszPpJR1hKqlzaY8 2010-05-26 09:26:06 76.104.132.98 9001 0
- # s Fast HSDir Named Running Stable Valid
- if len(nsEntry) >= 2: volatile["tor/flags"] = nsEntry[1][2:].split()
-
# ps derived stats
psParams = ["%cpu", "rss", "%mem", "etime"]
if self.vals["ps/pid"]:
Modified: arm/trunk/interface/logPanel.py
===================================================================
--- arm/trunk/interface/logPanel.py 2010-06-30 02:56:50 UTC (rev 22572)
+++ arm/trunk/interface/logPanel.py 2010-06-30 05:40:05 UTC (rev 22573)
@@ -215,11 +215,12 @@
def ns_event(self, event):
# NetworkStatus params: nickname, idhash, orhash, ip, orport (int), dirport (int), flags, idhex, bandwidth, updated (datetime)
- msg = ""
- for ns in event.nslist:
- msg += ", %s (%s:%i)" % (ns.nickname, ns.ip, ns.orport)
- if len(msg) > 1: msg = msg[2:]
- self.registerEvent("NS", "Listed (%i): %s" % (len(event.nslist), msg), "blue")
+ if "NS" in self.loggedEvents:
+ msg = ""
+ for ns in event.nslist:
+ msg += ", %s (%s:%i)" % (ns.nickname, ns.ip, ns.orport)
+ if len(msg) > 1: msg = msg[2:]
+ self.registerEvent("NS", "Listed (%i): %s" % (len(event.nslist), msg), "blue")
def new_consensus_event(self, event):
if "NEWCONSENSUS" in self.loggedEvents:
Modified: arm/trunk/util/torTools.py
===================================================================
--- arm/trunk/util/torTools.py 2010-06-30 02:56:50 UTC (rev 22572)
+++ arm/trunk/util/torTools.py 2010-06-30 05:40:05 UTC (rev 22573)
@@ -262,11 +262,15 @@
self._isReset = False # internal flag for tracking resets
self._status = TOR_CLOSED # current status of the attached control port
self._statusTime = 0 # unix timestamp for the duration of the status
- self._myNsEntry = None # network status entry for this relay (none if unset or possibly changed)
- # cached information static for a connection (None if unset, "UNKNOWN" if
- # unable to be determined)
- self.pid = None
+ # cached getInfo parameters (None if unset or possibly changed)
+ self._myNsEntry, self._myDescEntry = None, None
+ self._myBwRate, self._myBwBurst, self._myBwObserved = None, None, None
+ self._myFlags = None
+ self._myFingerprint = None
+
+ # None if unset, "UNKNOWN" if unable to be determined
+ self._myPid = None
def init(self, conn=None):
"""
@@ -289,6 +293,7 @@
if self.conn: self.close() # shut down current connection
self.conn = conn
self.conn.add_event_listener(self)
+ for listener in self.eventListeners: self.conn.add_event_listener(listener)
# sets the events listened for by the new controller (incompatable events
# are dropped with a logged warning)
@@ -311,7 +316,6 @@
if self.conn:
self.conn.close()
self.conn = None
- self.pid = None
self.connLock.release()
self._status = TOR_CLOSED
@@ -419,53 +423,186 @@
if not suppressExc and raisedExc: raise raisedExc
else: return result
- def getFingerprint(self, default = None):
+ def getMyNetworkStatus(self):
"""
+ Provides the network status entry for this relay if available (otherwise
+ provides None).
+ """
+
+ if self._myNsEntry: return self._myNsEntry
+
+ self.connLock.acquire()
+
+ myFingerprint = self.getMyFingerprint()
+ if not self._myNsEntry and myFingerprint:
+ nsResults = self.getInfo("ns/id/%s" % myFingerprint)
+ if nsResults: self._myNsEntry = nsResults.split("\n")
+
+ self.connLock.release()
+
+ return self._myNsEntry
+
+ def getMyDescriptor(self):
+ """
+ Provides the descrptor entry for this relay if available (otherwise
+ provides None).
+ """
+
+ if self._myDescEntry: return self._myDescEntry
+
+ self.connLock.acquire()
+
+ myFingerprint = self.getMyFingerprint()
+ if not self._myDescEntry and myFingerprint:
+ descResults = self.getInfo("desc/id/%s" % myFingerprint)
+ if descResults: self._myDescEntry = descResults.split("\n")
+
+ self.connLock.release()
+
+ return self._myDescEntry
+
+ def getMyBandwidthRate(self):
+ """
+ Provides the effective relaying bandwidth rate of this relay.
+ """
+
+ if self._myBwRate: return self._myBwRate
+
+ # effective relayed bandwidth is the minimum of BandwidthRate,
+ # MaxAdvertisedBandwidth, and RelayBandwidthRate (if set)
+ self.connLock.acquire()
+
+ if not self._myBwRate:
+ effectiveRate = int(self.getOption("BandwidthRate"))
+
+ relayRate = self.getOption("RelayBandwidthRate")
+ if relayRate and relayRate != "0":
+ effectiveRate = min(effectiveRate, int(relayRate))
+
+ maxAdvertised = self.getOption("MaxAdvertisedBandwidth")
+ if maxAdvertised: effectiveRate = min(effectiveRate, int(maxAdvertised))
+
+ self._myBwRate = effectiveRate
+
+ self.connLock.release()
+
+ return self._myBwRate
+
+ def getMyBandwidthBurst(self):
+ """
+ Provides the effective bandwidth burst rate of this relay.
+ """
+
+ if self._myBwBurst: return self._myBwBurst
+
+ # effective burst (same for BandwidthBurst and RelayBandwidthBurst)
+ self.connLock.acquire()
+
+ if not self._myBwBurst:
+ effectiveBurst = int(self.getOption("BandwidthBurst"))
+
+ relayBurst = self.getOption("RelayBandwidthBurst")
+ if relayBurst and relayBurst != "0":
+ effectiveBurst = min(effectiveBurst, int(relayBurst))
+
+ self._myBwBurst = effectiveBurst
+
+ self.connLock.release()
+
+ return self._myBwBurst
+
+ def getMyBandwidthObserved(self):
+ """
+ Provides the relay's current observed bandwidth (the throughput as noted by
+ the directory authorities and used by clients for relay selection). This is
+ fetched from the descriptors and hence will get stale if descriptors aren't
+ periodically updated.
+
+ This provides None if descriptors are unavailable or otherwise unable to be
+ parsed.
+ """
+
+ if self._myBwObserved: return self._myBwObserved
+
+ self.connLock.acquire()
+
+ myDescriptor = self.getMyDescriptor()
+ if not self._myBwObserved and myDescriptor:
+ for line in myDescriptor:
+ if line.startswith("bandwidth"):
+ # line should look something like:
+ # bandwidth 40960 102400 47284
+ comp = line.split()
+
+ if len(comp) == 4 and comp[-1].isdigit():
+ self._myBwObserved = int(comp[-1])
+ break
+
+ self.connLock.release()
+
+ return self._myBwObserved
+
+ def getMyFingerprint(self, default = None):
+ """
Provides the fingerprint for this relay.
Arguments:
default - result if the query fails
"""
- # TODO: figure out what can cause the fingerprint to change so this can be cached
- myFingerprint = self.getInfo("fingerprint")
+ # fingerprints are kept until sighup if set (most likely not even a setconf
+ # can change it since it's in the data directory)
+ if self._myFingerprint: return self._myFingerprint
- if myFingerprint: return myFingerprint
+ self.connLock.acquire()
+ if not self._myFingerprint: self._myFingerprint = self.getInfo("fingerprint")
+ self.connLock.release()
+
+ if self._myFingerprint: return self._myFingerprint
else: return default
- def getNetworkStatus(self):
+ def getMyFlags(self, default = None):
"""
- Provides the network status entry for this relay if available (otherwise
- provides None).
+ Provides the flags held by this relay.
+
+ Arguments:
+ default - result if the query fails
"""
- if self._myNsEntry: return self._myNsEntry
+ if self._myFlags: return self.myFlags
self.connLock.acquire()
- myFingerprint = self.getFingerprint()
- if myFingerprint:
- nsResults = self.getInfo("ns/id/%s" % myFingerprint)
- if nsResults: self._myNsEntry = nsResults.split("\n")
+ myNetworkStatus = self.getMyNetworkStatus()
+ if not self._myFlags and myNetworkStatus:
+ # network status contains a couple of lines, looking like:
+ # r caerSidi p1aag7VwarGxqctS7/fS0y5FU+s 9On1TRGCEpljszPpJR1hKqlzaY8 2010-05-26 09:26:06 76.104.132.98 9001 0
+ # s Fast HSDir Named Running Stable Valid
+ if len(myNetworkStatus) >= 2: self._myFlags = myNetworkStatus[1][2:].split()
self.connLock.release()
- return self._myNsEntry
+ if self._myFlags: return self._myFlags
+ else: return default
- def getPid(self):
+ def getMyPid(self):
"""
Provides the pid of the attached tor process (None if no controller exists
or this can't be determined).
"""
+ if self._myPid:
+ if self._myPid != "UNKNOWN" or not self.isAlive(): return None
+ else: return self._myPid
+
self.connLock.acquire()
- if self.pid == "UNKNOWN" or not self.isAlive(): result = None
- elif self.pid: result = self.pid
+ if self._myPid == "UNKNOWN" or not self.isAlive(): result = None
+ elif self._myPid: result = self._myPid
else:
result = getPid(int(self.getOption("ControlPort", 9051)))
- if result: self.pid = result
- else: self.pid = "UNKNOWN"
+ if result: self._myPid = result
+ else: self._myPid = "UNKNOWN"
self.connLock.release()
@@ -658,7 +795,7 @@
if self._isReset: break
if not self._isReset:
- errorLine, torPid = "", self.getPid()
+ errorLine, torPid = "", self.getMyPid()
if torPid:
for line in pkillOutput:
if line.startswith("pkill: %s - " % torPid):
@@ -689,19 +826,22 @@
thread.start_new_thread(self._notifyStatusListeners, (TOR_INIT,))
def ns_event(self, event):
- # TODO: Not sure if idhash or orhash is the relay's fingerprint
- #myFingerprint = self.getFingerprint()
- #if myFingerprint:
- # for ns in event.nslist:
- # if ns.idhash == myFingerprint:
- # self._myNsEntry = None
- # return
-
- self._myNsEntry = None
+ myFingerprint = self.getMyFingerprint()
+ if myFingerprint:
+ for ns in event.nslist:
+ if ns.idhex == myFingerprint:
+ self._myNsEntry, self._myFlags = None, None
+ return
+ else: self._myNsEntry, self._myFlags = None, None
def new_consensus_event(self, event):
- self._myNsEntry = None
+ self._myNsEntry, self._myFlags = None, None
+ def new_desc_event(self, event):
+ myFingerprint = self.getMyFingerprint()
+ if not myFingerprint or myFingerprint in event.idlist:
+ self._myDescEntry, self._myBwObserved = None, None
+
def _notifyStatusListeners(self, eventType):
"""
Sends a notice to all current listeners that a given change in tor's
@@ -711,6 +851,11 @@
eventType - enum representing tor's new status
"""
+ # resets cached getInfo parameters
+ self._myNsEntry, self._myDescEntry = None, None
+ self._myBwRate, self._myBwBurst, self._myBwObserved = None, None, None
+ self._myFlags, self._myFingerprint, self._myPid = None, None, None
+
for callback in self.statusListeners:
callback(self, eventType)