[tor-commits] [nyx/master] Track connection uptime

atagar at torproject.org atagar at torproject.org
Tue Sep 22 17:08:39 UTC 2015


commit 40f5bb69cc3bf48871d92816888e5e7842a4e75c
Author: Damian Johnson <atagar at torproject.org>
Date:   Wed Jul 15 09:49:48 2015 -0700

    Track connection uptime
    
    Our connection panel does some gross things to keep track of connection uptime.
    Our tracker class can easily handle this, simplifying the panel and making this
    functionality more testable.
---
 nyx/util/tracker.py                     |   26 +++++++++++++----
 test/util/tracker/connection_tracker.py |   48 ++++++++++++++++++++++++++-----
 2 files changed, 61 insertions(+), 13 deletions(-)

diff --git a/nyx/util/tracker.py b/nyx/util/tracker.py
index 638768b..2471aff 100644
--- a/nyx/util/tracker.py
+++ b/nyx/util/tracker.py
@@ -68,6 +68,14 @@ RESOURCE_TRACKER = None
 PORT_USAGE_TRACKER = None
 CONSENSUS_TRACKER = None
 
+# Extending stem's Connection tuple with attributes for the uptime of the
+# connection.
+
+Connection = collections.namedtuple('Connection', [
+  'start_time',
+  'is_legacy',  # boolean to indicate if the connection predated us
+] + list(stem.util.connection.Connection._fields))
+
 Resources = collections.namedtuple('Resources', [
   'cpu_sample',
   'cpu_average',
@@ -441,8 +449,10 @@ class ConnectionTracker(Daemon):
     super(ConnectionTracker, self).__init__(rate)
 
     self._connections = []
+    self._start_times = {}  # connection => (unix_timestamp, is_legacy)
     self._resolvers = connection.system_resolvers()
     self._custom_resolver = None
+    self._is_first_run = True
 
     # Number of times in a row we've either failed with our current resolver or
     # concluded that our rate is too low.
@@ -462,12 +472,16 @@ class ConnectionTracker(Daemon):
 
     try:
       start_time = time.time()
+      new_connections, new_start_times = [], {}
 
-      self._connections = connection.get_connections(
-        resolver,
-        process_pid = process_pid,
-        process_name = process_name,
-      )
+      for conn in connection.get_connections(resolver, process_pid = process_pid, process_name = process_name):
+        conn_start_time, is_legacy = self._start_times.get(conn, (start_time, self._is_first_run))
+        new_start_times[conn] = (conn_start_time, is_legacy)
+        new_connections.append(Connection(conn_start_time, is_legacy, *conn))
+
+      self._connections = new_connections
+      self._start_times = new_start_times
+      self._is_first_run = False
 
       runtime = time.time() - start_time
 
@@ -539,7 +553,7 @@ class ConnectionTracker(Daemon):
     """
     Provides a listing of tor's latest connections.
 
-    :returns: **list** of :class:`~stem.util.connection.Connection` we last
+    :returns: **list** of :class:`~nyx.util.tracker.Connection` we last
       retrieved, an empty list if our tracker's been stopped
     """
 
diff --git a/test/util/tracker/connection_tracker.py b/test/util/tracker/connection_tracker.py
index 416a96b..0f3c93f 100644
--- a/test/util/tracker/connection_tracker.py
+++ b/test/util/tracker/connection_tracker.py
@@ -7,9 +7,9 @@ from stem.util import connection
 
 from mock import Mock, patch
 
-CONNECTION_1 = connection.Connection('127.0.0.1', 3531, '75.119.206.243', 22, 'tcp')
-CONNECTION_2 = connection.Connection('127.0.0.1', 1766, '86.59.30.40', 443, 'tcp')
-CONNECTION_3 = connection.Connection('127.0.0.1', 1059, '74.125.28.106', 80, 'tcp')
+STEM_CONNECTION_1 = connection.Connection('127.0.0.1', 3531, '75.119.206.243', 22, 'tcp')
+STEM_CONNECTION_2 = connection.Connection('127.0.0.1', 1766, '86.59.30.40', 443, 'tcp')
+STEM_CONNECTION_3 = connection.Connection('127.0.0.1', 1059, '74.125.28.106', 80, 'tcp')
 
 
 class TestConnectionTracker(unittest.TestCase):
@@ -19,7 +19,7 @@ class TestConnectionTracker(unittest.TestCase):
   @patch('nyx.util.tracker.connection.system_resolvers', Mock(return_value = [connection.Resolver.NETSTAT]))
   def test_fetching_connections(self, get_value_mock, tor_controller_mock):
     tor_controller_mock().get_pid.return_value = 12345
-    get_value_mock.return_value = [CONNECTION_1, CONNECTION_2, CONNECTION_3]
+    get_value_mock.return_value = [STEM_CONNECTION_1, STEM_CONNECTION_2, STEM_CONNECTION_3]
 
     with ConnectionTracker(0.04) as daemon:
       time.sleep(0.01)
@@ -27,7 +27,7 @@ class TestConnectionTracker(unittest.TestCase):
       connections = daemon.get_value()
 
       self.assertEqual(1, daemon.run_counter())
-      self.assertEqual([CONNECTION_1, CONNECTION_2, CONNECTION_3], connections)
+      self.assertEqual(['75.119.206.243', '86.59.30.40', '74.125.28.106'], [conn.remote_address for conn in connections])
 
       get_value_mock.return_value = []  # no connection results
       time.sleep(0.05)
@@ -63,7 +63,7 @@ class TestConnectionTracker(unittest.TestCase):
       # Now make connection resolution work. We still shouldn't provide any
       # results since we stopped looking.
 
-      get_value_mock.return_value = [CONNECTION_1, CONNECTION_2]
+      get_value_mock.return_value = [STEM_CONNECTION_1, STEM_CONNECTION_2]
       get_value_mock.side_effect = None
       time.sleep(0.05)
       self.assertEqual([], daemon.get_value())
@@ -73,4 +73,38 @@ class TestConnectionTracker(unittest.TestCase):
 
       daemon.set_custom_resolver(connection.Resolver.NETSTAT)
       time.sleep(0.05)
-      self.assertEqual([CONNECTION_1, CONNECTION_2], daemon.get_value())
+      self.assertEqual(['75.119.206.243', '86.59.30.40'], [conn.remote_address for conn in daemon.get_value()])
+
+  @patch('nyx.util.tracker.tor_controller')
+  @patch('nyx.util.tracker.connection.get_connections')
+  @patch('nyx.util.tracker.system', Mock(return_value = Mock()))
+  @patch('nyx.util.tracker.connection.system_resolvers', Mock(return_value = [connection.Resolver.NETSTAT]))
+  def test_tracking_uptime(self, get_value_mock, tor_controller_mock):
+    tor_controller_mock().get_pid.return_value = 12345
+    get_value_mock.return_value = [STEM_CONNECTION_1]
+    first_start_time = time.time()
+
+    with ConnectionTracker(0.04) as daemon:
+      time.sleep(0.01)
+
+      connections = daemon.get_value()
+      self.assertEqual(1, len(connections))
+
+      self.assertEqual('75.119.206.243', connections[0].remote_address)
+      self.assertTrue(first_start_time < connections[0].start_time < time.time())
+      self.assertTrue(connections[0].is_legacy)
+
+      second_start_time = time.time()
+      get_value_mock.return_value = [STEM_CONNECTION_1, STEM_CONNECTION_2]
+      time.sleep(0.05)
+
+      connections = daemon.get_value()
+      self.assertEqual(2, len(connections))
+
+      self.assertEqual('75.119.206.243', connections[0].remote_address)
+      self.assertTrue(first_start_time < connections[0].start_time < time.time())
+      self.assertTrue(connections[0].is_legacy)
+
+      self.assertEqual('86.59.30.40', connections[1].remote_address)
+      self.assertTrue(second_start_time < connections[1].start_time < time.time())
+      self.assertFalse(connections[1].is_legacy)





More information about the tor-commits mailing list