commit 0081c9a6122ac3fbb967da0f7228317f170eae38 Author: Damian Johnson atagar@torproject.org Date: Tue Apr 2 11:03:02 2019 -0700
Add a get_uptime() method
Taking advantage of our new getinfo option for determining the tor process runtime.
https://trac.torproject.org/projects/tor/ticket/25132 https://gitweb.torproject.org/torspec.git/commit/?id=e9ef624 --- docs/change_log.rst | 1 + stem/control.py | 44 +++++++++++++++++++++++++++++++++++++++++ stem/version.py | 2 ++ test/unit/control/controller.py | 25 +++++++++++++++++++++++ 4 files changed, 72 insertions(+)
diff --git a/docs/change_log.rst b/docs/change_log.rst index 89626ca4..5c0208cb 100644 --- a/docs/change_log.rst +++ b/docs/change_log.rst @@ -47,6 +47,7 @@ The following are only available within Stem's `git repository
* **Controller**
+ * Added :func:`~stem.control.Controller.get_uptime` method to the :class:`~stem.control.Controller` * Controller events could fail to be delivered in a timely fashion (:trac:`27173`) * Adjusted :func:`~stem.control.Controller.get_microdescriptors` fallback to also use '.new' cache files (:trac:`28508`) * **DORMANT** and **ACTIVE** :data:`~stem.Signal` (:spec:`4421149`) diff --git a/stem/control.py b/stem/control.py index ce2d79a4..b4436af0 100644 --- a/stem/control.py +++ b/stem/control.py @@ -84,6 +84,7 @@ If you're fine with allowing your script to raise exceptions then this can be mo |- get_protocolinfo - information about the controller interface |- get_user - provides the user tor is running as |- get_pid - provides the pid of our tor process + |- get_uptime - duration tor has been running |- is_user_traffic_allowed - checks if we send or receive direct user traffic | |- get_microdescriptor - querying the microdescriptor for a relay @@ -1650,6 +1651,49 @@ class Controller(BaseController): else: raise ValueError("Unable to resolve tor's pid" if self.is_localhost() else "Tor isn't running locally")
+ @with_default() + def get_uptime(self, default = UNDEFINED): + """ + get_uptime(default = UNDEFINED) + + Provides the duration in seconds that tor has been running. + + .. versionadded:: 1.8.0 + + :param object default: response if the query fails + + :returns: **int** for the number of seconds tor has been running + + :raises: **ValueError** if unable to determine the uptime and no default + was provided + """ + + if self.get_version() >= stem.version.Requirement.GETINFO_UPTIME: + uptime = self.get_info('uptime', None) + + if uptime and uptime.isdigit(): + return int(uptime) + else: + raise ValueError("'GETINFO uptime' did not provide a valid numeric response: %s" % uptime) + else: + # Tor doesn't yet support this GETINFO option, attempt to determine the + # uptime of the process ourselves. + + if not self.is_localhost(): + raise ValueError('Unable to determine the uptime when tor is not running locally') + + pid = self.get_pid(None) + + if not pid: + raise ValueError('Unable to determine the pid of the tor process') + + start_time = stem.util.system.start_time(pid) + + if not start_time: + raise ValueError('Unable to determine when the tor process began') + + return time.time() - start_time + def is_user_traffic_allowed(self): """ Checks if we're likely to service direct user traffic. This essentially diff --git a/stem/version.py b/stem/version.py index 21dc3a0b..c13db22c 100644 --- a/stem/version.py +++ b/stem/version.py @@ -64,6 +64,7 @@ easily parsed and compared, for instance... **GETINFO_CONFIG_TEXT** 'GETINFO config-text' query **GETINFO_GEOIP_AVAILABLE** 'GETINFO ip-to-country/ipv4-available' query and its ipv6 counterpart **GETINFO_MICRODESCRIPTORS** 'GETINFO md/all' query + **GETINFO_UPTIME** 'GETINFO uptime' query **HIDDEN_SERVICE_V3** Support for v3 hidden services **HSFETCH** HSFETCH requests **HSFETCH_V3** HSFETCH for version 3 hidden services @@ -379,6 +380,7 @@ Requirement = stem.util.enum.Enum( ('GETINFO_CONFIG_TEXT', Version('0.2.2.7-alpha')), ('GETINFO_GEOIP_AVAILABLE', Version('0.3.2.1-alpha')), ('GETINFO_MICRODESCRIPTORS', Version('0.3.5.1-alpha')), + ('GETINFO_UPTIME', Version('0.3.5.1-alpha')), ('HIDDEN_SERVICE_V3', Version('0.3.3.1-alpha')), ('HSFETCH', Version('0.2.7.1-alpha')), ('HSFETCH_V3', Version('0.4.1.1-alpha')), diff --git a/test/unit/control/controller.py b/test/unit/control/controller.py index 13d26c4b..f5e63771 100644 --- a/test/unit/control/controller.py +++ b/test/unit/control/controller.py @@ -562,6 +562,31 @@ class TestControl(unittest.TestCase):
self.assertEqual(432, self.controller.get_pid())
+ @patch('stem.control.Controller.get_version', Mock(return_value = stem.version.Version('0.5.0.14'))) + @patch('stem.control.Controller.get_info') + def test_get_uptime_by_getinfo(self, getinfo_mock): + """ + Exercise the get_uptime() resolution via a GETINFO query. + """ + + getinfo_mock.return_value = '321' + self.assertEqual(321, self.controller.get_uptime()) + + getinfo_mock.return_value = 'abc' + self.assertRaisesWith(ValueError, "'GETINFO uptime' did not provide a valid numeric response: abc", self.controller.get_uptime) + + @patch('stem.socket.ControlSocket.is_localhost', Mock(return_value = True)) + @patch('stem.control.Controller.get_version', Mock(return_value = stem.version.Version('0.1.0.14'))) + @patch('stem.control.Controller.get_pid', Mock(return_value = '12')) + @patch('stem.util.system.start_time', Mock(return_value = 5000.0)) + @patch('time.time', Mock(return_value = 5200.0)) + def test_get_uptime_by_process(self): + """ + Exercise the get_uptime() resolution via process age. + """ + + self.assertEqual(200, self.controller.get_uptime()) + @patch('stem.control.Controller.get_info') def test_get_network_status_for_ourselves(self, get_info_mock): """
tor-commits@lists.torproject.org