commit 40f5bb69cc3bf48871d92816888e5e7842a4e75c Author: Damian Johnson atagar@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)