[tor-commits] [stem/master] Add a get_uptime() method

atagar at torproject.org atagar at torproject.org
Wed Apr 3 18:04:49 UTC 2019


commit 0081c9a6122ac3fbb967da0f7228317f170eae38
Author: Damian Johnson <atagar at 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):
     """





More information about the tor-commits mailing list