[tor-commits] [arm/master] Using stem's event handling

atagar at torproject.org atagar at torproject.org
Mon Dec 17 04:25:17 UTC 2012


commit 7f295fe63f60c221bce6a9fa28ff6b713ea656c7
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Dec 16 18:32:15 2012 -0800

    Using stem's event handling
    
    Replacing TorCtl event handling with stem's. This includes a few changes...
    
    * Dropped some of the custom messaging for event types. On the down side those
      messages might have been a tad more user friendly. On the up side we'll now
      provide the full event message content rather than hiding things that we
      don't recognize. Personally I consider this to be a net good thing. We can
      add a user friendly str() for Events if we want a better rendering.
    
    * We no longer support listener reattachment. This is because stem itself
      provides reattachment but we've broken it by misusing the Controller class.
      We're constructing fresh Controller instances rather than reusing the one we
      made priorly - when this is fixed we'll have reattachment again.
    
    * The interpretor panel no longer supports SETEVENTS nor event listening. It
      took some pretty ugly hacks to make it work in the first place, and I'm not
      very inclined to redo these hacks with stem. Besides, we'll probably be
      splintering the interpretor panel into its own application for ticket #6439.
---
 src/cli/graphing/graphPanel.py |    9 +-
 src/cli/logPanel.py            |  185 ++++++++++++++------------------------
 src/util/torInterpretor.py     |   17 ++--
 src/util/torTools.py           |  196 +++++++++-------------------------------
 4 files changed, 120 insertions(+), 287 deletions(-)

diff --git a/src/cli/graphing/graphPanel.py b/src/cli/graphing/graphPanel.py
index 76221ff..d03d1bd 100644
--- a/src/cli/graphing/graphPanel.py
+++ b/src/cli/graphing/graphPanel.py
@@ -18,11 +18,12 @@ Downloaded (0.0 B/sec):           Uploaded (0.0 B/sec):
 
 import copy
 import curses
-from TorCtl import TorCtl
 
 import cli.popups
 import cli.controller
 
+import stem.control
+
 from util import enum, panel, torTools, uiTools
 
 # time intervals at which graphs can be updated
@@ -56,7 +57,7 @@ def loadConfig(config):
     "features.graph.interval": (0, len(UPDATE_INTERVALS) - 1),
     "features.graph.bound": (0, 2)})
 
-class GraphStats(TorCtl.PostEventListener):
+class GraphStats:
   """
   Module that's expected to update dynamically and provide attributes to be
   graphed. Up to two graphs (a 'primary' and 'secondary') can be displayed at a
@@ -68,8 +69,6 @@ class GraphStats(TorCtl.PostEventListener):
     Initializes parameters needed to present a graph.
     """
     
-    TorCtl.PostEventListener.__init__(self)
-    
     # panel to be redrawn when updated (set when added to GraphPanel)
     self._graphPanel = None
     self.isSelected = False
@@ -96,7 +95,7 @@ class GraphStats(TorCtl.PostEventListener):
       self.secondaryCounts[i] = (self.maxCol + 1) * [0]
     
     # tracks BW events
-    torTools.getConn().addEventListener(self)
+    torTools.getConn().addEventListener(self.bandwidth_event, stem.control.EventType.BW)
   
   def clone(self, newCopy=None):
     """
diff --git a/src/cli/logPanel.py b/src/cli/logPanel.py
index ee7dee1..b866c62 100644
--- a/src/cli/logPanel.py
+++ b/src/cli/logPanel.py
@@ -11,9 +11,8 @@ import curses
 import logging
 import threading
 
-from TorCtl import TorCtl
-
 import stem.util.log
+from stem.response import events
 
 import popups
 from version import VERSION
@@ -35,7 +34,7 @@ EVENT_LISTING = """        d DEBUG      a ADDRMAP           k DESCCHANGED   s ST
                      j CLIENTS_SEEN      q ORCONN
           DINWE tor runlevel+            A All Events
           12345 arm runlevel+            X No Events
-          67890 torctl runlevel+         U Unknown Events"""
+                                         U Unknown Events"""
 
 RUNLEVEL_EVENT_COLOR = {log.DEBUG: "magenta", log.INFO: "blue", log.NOTICE: "green",
                         log.WARN: "yellow", log.ERR: "red"}
@@ -155,7 +154,7 @@ def expandEvents(eventAbbr):
 
 def getMissingEventTypes():
   """
-  Provides the event types the current torctl connection supports but arm
+  Provides the event types the current tor connection supports but arm
   doesn't. This provides an empty list if no event types are missing, and None
   if the GETINFO query fails.
   """
@@ -168,28 +167,6 @@ def getMissingEventTypes():
     return [event for event in torEventTypes if not event in armEventTypes]
   else: return None # GETINFO call failed
 
-def setEventListening(events):
-  """
-  Configures the events Tor listens for, filtering non-tor events from what we
-  request from the controller. This returns a sorted list of the events we
-  successfully set.
-  
-  Arguments:
-    events - event types to attempt to set
-  """
-  
-  events = set(events) # drops duplicates
-  torEvents = events.intersection(set(TOR_EVENT_TYPES.values()))
-  
-  # adds events unrecognized by arm if we're listening to the 'UNKNOWN' type
-  if "UNKNOWN" in events:
-    torEvents.update(set(getMissingEventTypes()))
-  
-  setEvents = torTools.getConn().setControllerEvents(list(torEvents))
-  
-  # provides back the input set minus events we failed to set
-  return sorted(events.difference(torTools.FAILED_EVENTS))
-
 def loadLogMessages():
   """
   Fetches a mapping of common log messages to their runlevels from the config.
@@ -491,95 +468,9 @@ class LogEntry():
     
     return self._displayMessage
 
-class TorEventObserver(TorCtl.PostEventListener):
-  """
-  Listens for all types of events provided by TorCtl, providing an LogEntry
-  instance to the given callback function.
-  """
-  
-  def __init__(self, callback):
-    """
-    Tor event listener with the purpose of translating events to nicely
-    formatted calls of a callback function.
-    
-    Arguments:
-      callback - function accepting a LogEntry, called when an event of these
-                 types occur
-    """
-    
-    TorCtl.PostEventListener.__init__(self)
-    self.callback = callback
-  
-  def circ_status_event(self, event):
-    msg = "ID: %-3s STATUS: %-10s PATH: %s" % (event.circ_id, event.status, ", ".join(event.path))
-    if event.purpose: msg += " PURPOSE: %s" % event.purpose
-    if event.reason: msg += " REASON: %s" % event.reason
-    if event.remote_reason: msg += " REMOTE_REASON: %s" % event.remote_reason
-    self._notify(event, msg, "yellow")
-  
-  def buildtimeout_set_event(self, event):
-    self._notify(event, "SET_TYPE: %s, TOTAL_TIMES: %s, TIMEOUT_MS: %s, XM: %s, ALPHA: %s, CUTOFF_QUANTILE: %s" % (event.set_type, event.total_times, event.timeout_ms, event.xm, event.alpha, event.cutoff_quantile))
-  
-  def stream_status_event(self, event):
-    self._notify(event, "ID: %s STATUS: %s CIRC_ID: %s TARGET: %s:%s REASON: %s REMOTE_REASON: %s SOURCE: %s SOURCE_ADDR: %s PURPOSE: %s" % (event.strm_id, event.status, event.circ_id, event.target_host, event.target_port, event.reason, event.remote_reason, event.source, event.source_addr, event.purpose))
-  
-  def or_conn_status_event(self, event):
-    msg = "STATUS: %-10s ENDPOINT: %-20s" % (event.status, event.endpoint)
-    if event.reason: msg += " REASON: %-6s" % event.reason
-    if event.ncircs: msg += " NCIRCS: %i" % event.ncircs
-    self._notify(event, msg)
-  
-  def stream_bw_event(self, event):
-    self._notify(event, "ID: %s READ: %s WRITTEN: %s" % (event.strm_id, event.bytes_read, event.bytes_written))
-  
-  def bandwidth_event(self, event):
-    self._notify(event, "READ: %i, WRITTEN: %i" % (event.read, event.written), "cyan")
-  
-  def msg_event(self, event):
-    self._notify(event, event.msg, RUNLEVEL_EVENT_COLOR[event.level])
-  
-  def new_desc_event(self, event):
-    idlistStr = [str(item) for item in event.idlist]
-    self._notify(event, ", ".join(idlistStr))
-  
-  def address_mapped_event(self, event):
-    whenLabel, gmtExpiryLabel = "", ""
-    
-    if event.when:
-      whenLabel = time.strftime("%H:%M %m/%d/%Y", event.when)
-    
-    # TODO: torctl is getting an 'error' and 'gmt_expiry' attribute so display
-    # those when they become available
-    #
-    #if event.gmt_expiry:
-    #  gmtExpiryLabel = time.strftime("%H:%M %m/%d/%Y", event.gmt_expiry)
-    
-    self._notify(event, "%s, %s -> %s" % (whenLabel, event.from_addr, event.to_addr))
-  
-  def ns_event(self, event):
-    # NetworkStatus params: nickname, idhash, orhash, ip, orport (int),
-    #     dirport (int), flags, idhex, bandwidth, updated (datetime)
-    msg = ", ".join(["%s (%s)" % (ns.idhex, ns.nickname) for ns in event.nslist])
-    self._notify(event, "Listed (%i): %s" % (len(event.nslist), msg), "blue")
-  
-  def new_consensus_event(self, event):
-    msg = ", ".join(["%s (%s)" % (ns.idhex, ns.nickname) for ns in event.nslist])
-    self._notify(event, "Listed (%i): %s" % (len(event.nslist), msg), "magenta")
-  
-  def guard_event(self, event):
-    msg = "%s (%s), STATUS: %s" % (event.idhex, event.nick, event.status)
-    self._notify(event, msg, "yellow")
-  
-  def unknown_event(self, event):
-    msg = "(%s) %s" % (event.event_name, event.event_string)
-    self.callback(LogEntry(event.arrived_at, "UNKNOWN", msg, "red"))
-  
-  def _notify(self, event, msg, color="white"):
-    self.callback(LogEntry(event.arrived_at, event.event_name, msg, color))
-
 class LogPanel(panel.Panel, threading.Thread, logging.Handler):
   """
-  Listens for and displays tor, arm, and torctl events. This can prepopulate
+  Listens for and displays tor, arm, and stem events. This can prepopulate
   from tor's log file if it exists.
   """
   
@@ -626,13 +517,14 @@ class LogPanel(panel.Panel, threading.Thread, logging.Handler):
     # collapses duplicate log entries if false, showing only the most recent
     self.showDuplicates = self._config["features.log.showDuplicateEntries"]
     
+    self.loggedEvents = [] # needs to be set before we receive any events
+    
     # restricts the input to the set of events we can listen to, and
     # configures the controller to liten to them
-    loggedEvents = setEventListening(loggedEvents)
+    self.loggedEvents = self.setEventListening(loggedEvents)
     
     self.setPauseAttr("msgLog")         # tracks the message log when we're paused
     self.msgLog = []                    # log entries, sorted by the timestamp
-    self.loggedEvents = loggedEvents    # events we're listening to
     self.regexFilter = None             # filter for presented log events (no filtering if None)
     self.lastContentHeight = 0          # height of the rendered content when last drawn
     self.logFile = None                 # file log messages are saved to (skipped if None)
@@ -666,9 +558,8 @@ class LogPanel(panel.Panel, threading.Thread, logging.Handler):
     # leaving lastContentHeight as being too low causes initialization problems
     self.lastContentHeight = len(self.msgLog)
     
-    # adds listeners for tor and torctl events
+    # adds listeners for tor and stem events
     conn = torTools.getConn()
-    conn.addEventListener(TorEventObserver(self.registerEvent))
     conn.addStatusListener(self._resetListener)
     
     # opens log file if we'll be saving entries
@@ -750,6 +641,33 @@ class LogPanel(panel.Panel, threading.Thread, logging.Handler):
     
     self.showDuplicates = isVisible
   
+  def registerTorEvent(self, event):
+    """
+    Translates a stem.response.event.Event instance into a LogEvent, and calls
+    registerEvent().
+    """
+    
+    msg, color = ' '.join(str(event).split(' ')[1:]), "white"
+    
+    if isinstance(event, events.CircuitEvent):
+      color = "yellow"
+    elif isinstance(event, events.BandwidthEvent):
+      color = "cyan"
+      msg = "READ: %i, WRITTEN: %i" % (event.read, event.written)
+    elif isinstance(event, events.LogEvent):
+      color = RUNLEVEL_EVENT_COLOR[event.runlevel]
+      msg = event.message
+    elif isinstance(event, events.NetworkStatusEvent):
+      color = "blue"
+    elif isinstance(event, events.NewConsensusEvent):
+      color = "magenta"
+    elif isinstance(event, events.GuardEvent):
+      color = "yellow"
+    elif not event.type in TOR_EVENT_TYPES.values():
+      color = "red" # unknown event type
+    
+    self.registerEvent(LogEntry(event.arrived_at, event.type, msg, color))
+  
   def registerEvent(self, event):
     """
     Notes event and redraws log. If paused it's held in a temporary buffer.
@@ -801,7 +719,7 @@ class LogPanel(panel.Panel, threading.Thread, logging.Handler):
     
     # configures the controller to listen for these tor events, and provides
     # back a subset without anything we're failing to listen to
-    setTypes = setEventListening(eventTypes)
+    setTypes = self.setEventListening(eventTypes)
     self.loggedEvents = setTypes
     self.redraw(True)
     self.valsLock.release()
@@ -1217,6 +1135,35 @@ class LogPanel(panel.Panel, threading.Thread, logging.Handler):
     self._cond.notifyAll()
     self._cond.release()
   
+  def setEventListening(self, events):
+    """
+    Configures the events Tor listens for, filtering non-tor events from what we
+    request from the controller. This returns a sorted list of the events we
+    successfully set.
+    
+    Arguments:
+      events - event types to attempt to set
+    """
+    
+    events = set(events) # drops duplicates
+    torEvents = events.intersection(set(TOR_EVENT_TYPES.values()))
+    
+    # adds events unrecognized by arm if we're listening to the 'UNKNOWN' type
+    if "UNKNOWN" in events:
+      torEvents.update(set(getMissingEventTypes()))
+    
+    torConn = torTools.getConn()
+    torConn.removeEventListener(self.registerTorEvent)
+    
+    for eventType in list(torEvents):
+      try:
+        torConn.addEventListener(self.registerTorEvent, eventType)
+      except stem.ProtocolError:
+        torEvents.remove(eventType)
+    
+    # provides back the input set minus events we failed to set
+    return sorted(torEvents)
+  
   def _resetListener(self, _, eventType):
     # if we're attaching to a new tor instance then clears the log and
     # prepopulates it with the content belonging to this instance
@@ -1257,7 +1204,7 @@ class LogPanel(panel.Panel, threading.Thread, logging.Handler):
         labelPattern = uiTools.cropStr(currentPattern, width - 18)
         panelLabel = "Events (filter: %s):" % labelPattern
     else:
-      # does the following with all runlevel types (tor, arm, and torctl):
+      # does the following with all runlevel types (tor, arm, and stem):
       # - pulls to the start of the list
       # - condenses range if there's three or more in a row (ex. "ARM_INFO - WARN")
       # - condense further if there's identical runlevel ranges for multiple
diff --git a/src/util/torInterpretor.py b/src/util/torInterpretor.py
index 674e5e2..5023045 100644
--- a/src/util/torInterpretor.py
+++ b/src/util/torInterpretor.py
@@ -11,12 +11,6 @@ import version
 
 import stem
 
-# TODO: util should never import from the cli. This is being done as a hack to
-# simplify event listening, but we should later move the TorEventObserver into
-# the util.
-
-from cli.logPanel import TorEventObserver
-
 from util import connections, enum, hostnames, torConfig, torTools, uiTools
 
 COLOR_PROMPT = True     # provides a colored interpretor prompt
@@ -445,7 +439,12 @@ class ControlInterpretor:
     self.eventBuffer = [] # unread event messages
     self.loggedEvents = [] # event types that we're listening for
     
-    torTools.getConn().addEventListener(TorEventObserver(self.registerEvent))
+    # TODO: Dropping event listening since...
+    # a. it was a complete hack
+    # b. said hack isn't worth porting to stem
+    # c. I'm likely about to separate the interpretor from arm anyway
+    
+    #torTools.getConn().addEventListener(TorEventObserver(self.registerEvent))
   
   def registerEvent(self, event):
     """
@@ -913,8 +912,10 @@ class ControlInterpretor:
         except Exception, exc:
           outputEntry.append((str(exc), ERROR_FORMAT))
       elif cmd == "SETEVENTS":
+        return ([], []) # dropping support, see the comment for the event listener above for why
+        
         self.loggedEvents = arg.split()
-        allEvents = torTools.getConn().setControllerEvents(self.loggedEvents)
+        #allEvents = torTools.getConn().setControllerEvents(self.loggedEvents)
         setEvents = set(self.loggedEvents).intersection(allEvents)
         
         if setEvents:
diff --git a/src/util/torTools.py b/src/util/torTools.py
index 36cb70f..d261bb0 100644
--- a/src/util/torTools.py
+++ b/src/util/torTools.py
@@ -38,14 +38,6 @@ DIR_SERVERS = [("86.59.21.38", "80"),         # tor26
 # message logged by default when a controller can't set an event type
 DEFAULT_FAILED_EVENT_MSG = "Unsupported event type: %s"
 
-# TODO: check version when reattaching to controller and if version changes, flush?
-# Skips attempting to set events we've failed to set before. This avoids
-# logging duplicate warnings but can be problematic if controllers belonging
-# to multiple versions of tor are attached, making this unreflective of the
-# controller's capabilites. However, this is a pretty bizarre edge case.
-DROP_FAILED_EVENTS = True
-FAILED_EVENTS = set()
-
 CONTROLLER = None # singleton Controller instance
 
 UNDEFINED = "<Undefined_ >"
@@ -493,7 +485,6 @@ class Controller(TorCtl.PostEventListener):
     self.conn = None                    # None if uninitialized or controller's been closed
     self.controller = None
     self.connLock = threading.RLock()
-    self.eventListeners = []            # instances listening for tor controller events
     self.statusListeners = []           # callback functions for tor's state changes
     self.controllerEvents = []          # list of successfully set controller events
     self._fingerprintMappings = None    # mappings of ip -> [(port, fingerprint), ...]
@@ -541,6 +532,9 @@ class Controller(TorCtl.PostEventListener):
       controller - stem based Controller instance
     """
     
+    # TODO: We should reuse our controller instance so event listeners will be
+    # re-attached. This is a point of regression until we do... :(
+    
     if conn.is_live() and controller.is_alive() and conn != self.conn:
       self.connLock.acquire()
       
@@ -550,8 +544,11 @@ class Controller(TorCtl.PostEventListener):
       self.controller = controller
       log.log(log.INFO, "Stem connected to tor version %s" % self.controller.get_version())
       
-      self.conn.add_event_listener(self)
-      for listener in self.eventListeners: self.conn.add_event_listener(listener)
+      self.controller.add_event_listener(self.msg_event, stem.control.EventType.NOTICE)
+      self.controller.add_event_listener(self.ns_event, stem.control.EventType.NS)
+      self.controller.add_event_listener(self.new_consensus_event, stem.control.EventType.NEWCONSENSUS)
+      self.controller.add_event_listener(self.new_desc_event, stem.control.EventType.NEWDESC)
+      self.controller.add_event_listener(self.circ_status_event, stem.control.EventType.CIRC)
       
       # registers this as our first heartbeat
       self._updateHeartbeat()
@@ -570,10 +567,6 @@ class Controller(TorCtl.PostEventListener):
       self._isExitingAllowed = self._exitPolicyChecker.isExitingAllowed()
       self._exitPolicyLookupCache = {}
       
-      # sets the events listened for by the new controller (incompatible events
-      # are dropped with a logged warning)
-      self.setControllerEvents(self.controllerEvents)
-      
       self._status = State.INIT
       self._statusTime = time.time()
       
@@ -1387,19 +1380,24 @@ class Controller(TorCtl.PostEventListener):
     
     return result
   
-  def addEventListener(self, listener):
+  def addEventListener(self, listener, *eventTypes):
     """
     Directs further tor controller events to callback functions of the
     listener. If a new control connection is initialized then this listener is
     reattached.
+    """
     
-    Arguments:
-      listener - TorCtl.PostEventListener instance listening for events
+    self.connLock.acquire()
+    if self.isAlive(): self.controller.add_event_listener(listener, *eventTypes)
+    self.connLock.release()
+  
+  def removeEventListener(self, listener):
+    """
+    Stops the given event listener from being notified of further events.
     """
     
     self.connLock.acquire()
-    self.eventListeners.append(listener)
-    if self.isAlive(): self.conn.add_event_listener(listener)
+    if self.isAlive(): self.controller.remove_event_listener(listener)
     self.connLock.release()
   
   def addStatusListener(self, callback):
@@ -1435,92 +1433,6 @@ class Controller(TorCtl.PostEventListener):
     
     return list(self.controllerEvents)
   
-  def setControllerEvents(self, events):
-    """
-    Sets the events being requested from any attached tor instance, logging
-    warnings for event types that aren't supported (possibly due to version
-    issues). Events in REQ_EVENTS will also be included, logging at the error
-    level with an additional description in case of failure.
-    
-    This remembers the successfully set events and tries to request them from
-    any tor instance it attaches to in the future too (again logging and
-    dropping unsuccessful event types).
-    
-    This returns the listing of event types that were successfully set. If not
-    currently attached to a tor instance then all events are assumed to be ok,
-    then attempted when next attached to a control port.
-    
-    Arguments:
-      events - listing of events to be set
-    """
-    
-    self.connLock.acquire()
-    
-    returnVal = []
-    if self.isAlive():
-      events = set(events)
-      events = events.union(set(REQ_EVENTS.keys()))
-      unavailableEvents = set()
-      
-      # removes anything we've already failed to set
-      if DROP_FAILED_EVENTS:
-        unavailableEvents.update(events.intersection(FAILED_EVENTS))
-        events.difference_update(FAILED_EVENTS)
-      
-      # initial check for event availability, using the 'events/names' GETINFO
-      # option to detect invalid events
-      validEvents = self.getInfo("events/names", None)
-      
-      if validEvents:
-        validEvents = set(validEvents.split())
-        unavailableEvents.update(events.difference(validEvents))
-        events.intersection_update(validEvents)
-      
-      # attempt to set events via trial and error
-      isEventsSet, isAbandoned = False, False
-      
-      while not isEventsSet and not isAbandoned:
-        try:
-          self.conn.set_events(list(events))
-          isEventsSet = True
-        except TorCtl.ErrorReply, exc:
-          msg = str(exc)
-          
-          if "Unrecognized event" in msg:
-            # figure out type of event we failed to listen for
-            start = msg.find("event \"") + 7
-            end = msg.rfind("\"")
-            failedType = msg[start:end]
-            
-            unavailableEvents.add(failedType)
-            events.discard(failedType)
-          else:
-            # unexpected error, abandon attempt
-            isAbandoned = True
-        except TorCtl.TorCtlClosed:
-          self.close()
-          isAbandoned = True
-      
-      FAILED_EVENTS.update(unavailableEvents)
-      if not isAbandoned:
-        # logs warnings or errors for failed events
-        for eventType in unavailableEvents:
-          defaultMsg = DEFAULT_FAILED_EVENT_MSG % eventType
-          if eventType in REQ_EVENTS:
-            log.log(log.ERR, defaultMsg + " (%s)" % REQ_EVENTS[eventType])
-          else:
-            log.log(log.WARN, defaultMsg)
-        
-        self.controllerEvents = list(events)
-        returnVal = list(events)
-    else:
-      # attempts to set the events when next attached to a control port
-      self.controllerEvents = list(events)
-      returnVal = list(events)
-    
-    self.connLock.release()
-    return returnVal
-  
   def reload(self, issueSighup = False):
     """
     This resets tor (sending a RELOAD signal to the control port) causing tor's
@@ -1631,7 +1543,7 @@ class Controller(TorCtl.PostEventListener):
     causing the torrc and internal state to be reset.
     """
     
-    if event.level == "NOTICE" and event.msg.startswith("Received reload signal (hup)"):
+    if event.runlevel == "NOTICE" and event.message.startswith("Received reload signal (hup)"):
       self.connLock.acquire()
       
       if self.isAlive():
@@ -1652,8 +1564,8 @@ class Controller(TorCtl.PostEventListener):
     
     myFingerprint = self.getInfo("fingerprint", None)
     if myFingerprint:
-      for ns in event.nslist:
-        if ns.idhex == myFingerprint:
+      for desc in event.desc:
+        if desc.fingerprint == myFingerprint:
           self._cachedParam["nsEntry"] = None
           self._cachedParam["flags"] = None
           self._cachedParam["bwMeasured"] = None
@@ -1681,7 +1593,7 @@ class Controller(TorCtl.PostEventListener):
     self._consensusLookupCache = {}
     
     if self._fingerprintMappings != None:
-      self._fingerprintMappings = self._getFingerprintMappings(event.nslist)
+      self._fingerprintMappings = self._getFingerprintMappings(event.desc)
     
     self.connLock.release()
   
@@ -1691,7 +1603,9 @@ class Controller(TorCtl.PostEventListener):
     self.connLock.acquire()
     
     myFingerprint = self.getInfo("fingerprint", None)
-    if not myFingerprint or myFingerprint in event.idlist:
+    desc_fingerprints = [fingerprint for (fingerprint, nickname) in event.relays]
+    
+    if not myFingerprint or myFingerprint in desc_fingerprints:
       self._cachedParam["descEntry"] = None
       self._cachedParam["bwObserved"] = None
     
@@ -1702,32 +1616,26 @@ class Controller(TorCtl.PostEventListener):
     self._descriptorLookupCache = {}
     
     if self._fingerprintMappings != None:
-      for fingerprint in event.idlist:
+      for fingerprint in desc_fingerprints:
         # gets consensus data for the new descriptor
-        try: nsLookup = self.conn.get_network_status("id/%s" % fingerprint)
-        except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): continue
-        
-        if len(nsLookup) > 1:
-          # multiple records for fingerprint (shouldn't happen)
-          log.log(log.WARN, "Multiple consensus entries for fingerprint: %s" % fingerprint)
-          continue
+        try: desc = self.controller.get_network_status(fingerprint)
+        except stem.ControllerError: continue
         
         # updates fingerprintMappings with new data
-        newRelay = nsLookup[0]
-        if newRelay.ip in self._fingerprintMappings:
+        if desc.address in self._fingerprintMappings:
           # if entry already exists with the same orport, remove it
           orportMatch = None
-          for entryPort, entryFingerprint in self._fingerprintMappings[newRelay.ip]:
-            if entryPort == newRelay.orport:
+          for entryPort, entryFingerprint in self._fingerprintMappings[desc.address]:
+            if entryPort == desc.or_port:
               orportMatch = (entryPort, entryFingerprint)
               break
           
-          if orportMatch: self._fingerprintMappings[newRelay.ip].remove(orportMatch)
+          if orportMatch: self._fingerprintMappings[desc.address].remove(orportMatch)
           
           # add the new entry
-          self._fingerprintMappings[newRelay.ip].append((newRelay.orport, newRelay.idhex))
+          self._fingerprintMappings[desc.address].append((desc.or_port, desc.fingerprint))
         else:
-          self._fingerprintMappings[newRelay.ip] = [(newRelay.orport, newRelay.idhex)]
+          self._fingerprintMappings[desc.address] = [(desc.or_port, desc.fingerprint)]
     
     self.connLock.release()
   
@@ -1742,27 +1650,6 @@ class Controller(TorCtl.PostEventListener):
     
     self._cachedParam["circuits"] = None
   
-  def buildtimeout_set_event(self, event):
-    self._updateHeartbeat()
-  
-  def stream_status_event(self, event):
-    self._updateHeartbeat()
-  
-  def or_conn_status_event(self, event):
-    self._updateHeartbeat()
-  
-  def stream_bw_event(self, event):
-    self._updateHeartbeat()
-  
-  def bandwidth_event(self, event):
-    self._updateHeartbeat()
-  
-  def address_mapped_event(self, event):
-    self._updateHeartbeat()
-  
-  def unknown_event(self, event):
-    self._updateHeartbeat()
-  
   def _updateHeartbeat(self):
     """
     Called on any event occurance to note the time it occured.
@@ -1771,26 +1658,25 @@ class Controller(TorCtl.PostEventListener):
     # alternative is to use the event's timestamp (via event.arrived_at)
     self.lastHeartbeat = time.time()
   
-  def _getFingerprintMappings(self, nsList = None):
+  def _getFingerprintMappings(self, descriptors = None):
     """
     Provides IP address to (port, fingerprint) tuple mappings for all of the
     currently cached relays.
     
     Arguments:
-      nsList - network status listing (fetched if not provided)
+      descriptors - router status entries (fetched if not provided)
     """
     
     results = {}
     if self.isAlive():
       # fetch the current network status if not provided
-      if not nsList:
-        try: nsList = self.conn.get_network_status(get_iterator=True)
-        except (socket.error, TorCtl.TorCtlClosed, TorCtl.ErrorReply): nsList = []
+      if not descriptors:
+        try: descriptors = self.controller.get_network_statuses()
+        except stem.ControllerError: descriptors = []
       
       # construct mappings of ips to relay data
-      for relay in nsList:
-        if relay.ip in results: results[relay.ip].append((relay.orport, relay.idhex))
-        else: results[relay.ip] = [(relay.orport, relay.idhex)]
+      for desc in descriptors:
+        results.setdefault(desc.address, []).append((desc.or_port, desc.fingerprint))
     
     return results
   





More information about the tor-commits mailing list