[tor-commits] [stem/master] Parsing 'exit-*' extrainfo lines

atagar at torproject.org atagar at torproject.org
Mon May 14 00:14:27 UTC 2012


commit 47acd940498dfe605324bd2e4f08826c7f8c5019
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun May 13 16:50:28 2012 -0700

    Parsing 'exit-*' extrainfo lines
    
    Handling the last extrainfo attributes...
      * exit-stats-end
      * exit-kibibytes-written
      * exit-kibibytes-read
      * exit-streams-opened
---
 stem/descriptor/extrainfo_descriptor.py      |   45 +++++++++++++++++++++++++-
 test/unit/descriptor/extrainfo_descriptor.py |   35 +++++++++++++++++++-
 2 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/stem/descriptor/extrainfo_descriptor.py b/stem/descriptor/extrainfo_descriptor.py
index 2b6997c..9f71c3c 100644
--- a/stem/descriptor/extrainfo_descriptor.py
+++ b/stem/descriptor/extrainfo_descriptor.py
@@ -243,6 +243,13 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
       entry_stats_interval (int) - length in seconds of the interval
       entry_ips (dict) - mapping of locales to rounded count of unique user ips
     
+    Exit Attributes:
+      exit_stats_end (datetime) - end of the period when stats were gathered
+      exit_stats_interval (int) - length in seconds of the interval
+      exit_kibibytes_written (dict) - traffic per port (keys are ints or 'other')
+      exit_kibibytes_read (dict) - traffic per port (keys are ints or 'other')
+      exit_streams_opened (dict) - streams per port (keys are ints or 'other')
+    
     Bridge Attributes:
       bridge_stats_end (datetime) - end of the period when stats were gathered
       bridge_stats_interval (int) - length in seconds of the interval
@@ -336,6 +343,12 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
     self.entry_stats_interval = None
     self.entry_ips = None
     
+    self.exit_stats_end = None
+    self.exit_stats_interval = None
+    self.exit_kibibytes_written = None
+    self.exit_kibibytes_read = None
+    self.exit_streams_opened = None
+    
     self.bridge_stats_end = None
     self.bridge_stats_interval = None
     self.bridge_ips = None
@@ -522,7 +535,7 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
         except ValueError:
           if validate:
             raise ValueError("Timestamp on %s line wasn't parseable: %s" % (keyword, line))
-      elif keyword in ("cell-stats-end", "entry-stats-end", "bridge-stats-end", "dirreq-stats-end"):
+      elif keyword in ("cell-stats-end", "entry-stats-end", "exit-stats-end", "bridge-stats-end", "dirreq-stats-end"):
         # "<keyword>" YYYY-MM-DD HH:MM:SS (NSEC s)
         
         try:
@@ -534,6 +547,9 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
           elif keyword == "entry-stats-end":
             self.entry_stats_end = timestamp
             self.entry_stats_interval = interval
+          elif keyword == "exit-stats-end":
+            self.exit_stats_end = timestamp
+            self.exit_stats_interval = interval
           elif keyword == "bridge-stats-end":
             self.bridge_stats_end = timestamp
             self.bridge_stats_interval = interval
@@ -592,6 +608,32 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
             self.dir_write_history_values = history_values
         except ValueError, exc:
           if validate: raise exc
+      elif keyword in ("exit-kibibytes-written", "exit-kibibytes-read", "exit-streams-opened"):
+        # "<keyword>" port=N,port=N,...
+        
+        port_mappings = {}
+        error_msg = "Entries in %s line should only be PORT=N entries: %s" % (keyword, line)
+        
+        if value:
+          for entry in value.split(","):
+            if not "=" in entry:
+              if validate: raise ValueError(error_msg)
+              else: continue
+            
+            port, stat = entry.split("=", 1)
+            
+            if (port == 'other' or stem.util.connection.is_valid_port(port)) and stat.isdigit():
+              if port != 'other': port = int(port)
+              port_mappings[port] = int(stat)
+            elif validate:
+              raise ValueError(error_msg)
+        
+        if keyword == "exit-kibibytes-written":
+          self.exit_kibibytes_written = port_mappings
+        elif keyword == "exit-kibibytes-read":
+          self.exit_kibibytes_read = port_mappings
+        elif keyword == "exit-streams-opened":
+          self.exit_streams_opened = port_mappings
       elif keyword in ("dirreq-v2-ips", "dirreq-v3-ips", "dirreq-v2-reqs", "dirreq-v3-reqs", "geoip-client-origins", "entry-ips", "bridge-ips"):
         # "<keyword>" CC=N,CC=N,...
         #
@@ -616,6 +658,7 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
               locale_usage[locale] = int(count)
             elif validate:
               raise ValueError(error_msg)
+        
         if keyword == "dirreq-v2-ips":
           self.dir_v2_ips = locale_usage
         elif keyword == "dirreq-v3-ips":
diff --git a/test/unit/descriptor/extrainfo_descriptor.py b/test/unit/descriptor/extrainfo_descriptor.py
index 76a4344..5d4c910 100644
--- a/test/unit/descriptor/extrainfo_descriptor.py
+++ b/test/unit/descriptor/extrainfo_descriptor.py
@@ -363,7 +363,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
     "<keyword>" YYYY-MM-DD HH:MM:SS (NSEC s)
     """
     
-    for keyword in ('cell-stats-end', 'entry-stats-end', 'bridge-stats-end', 'dirreq-stats-end'):
+    for keyword in ('cell-stats-end', 'entry-stats-end', 'exit-stats-end', 'bridge-stats-end', 'dirreq-stats-end'):
       end_attr = keyword.replace('-', '_').replace('dirreq', 'dir')
       interval_attr = end_attr[:-4] + "_interval"
       
@@ -431,6 +431,39 @@ class TestExtraInfoDescriptor(unittest.TestCase):
         self.assertEquals(None, getattr(desc, interval_attr))
         self.assertEquals(None, getattr(desc, values_attr))
   
+  def test_port_mapping_lines(self):
+    """
+    Uses valid and invalid data to tests lines of the form...
+    "<keyword>" port=N,port=N,...
+    """
+    
+    for keyword in ('exit-kibibytes-written', 'exit-kibibytes-read', 'exit-streams-opened'):
+      attr = keyword.replace('-', '_')
+      
+      test_entries = (
+        ("", {}),
+        ("443=100,other=111", {443: 100, 'other': 111}),
+        ("80=115533759,443=1777,995=690", {80: 115533759, 443: 1777, 995: 690}),
+      )
+      
+      for test_value, expected_value in test_entries:
+        desc_text = _make_descriptor({keyword: test_value})
+        desc = ExtraInfoDescriptor(desc_text)
+        self.assertEquals(expected_value, getattr(desc, attr))
+      
+      test_entries = (
+        "8000000=115533759",
+        "-80=115533759",
+        "80=-115533759",
+        "=115533759",
+        "80=",
+        "80,115533759",
+      )
+      
+      for entry in test_entries:
+        desc_text = _make_descriptor({keyword: entry})
+        self._expect_invalid_attr(desc_text, attr, {})
+  
   def test_locale_mapping_lines(self):
     """
     Uses valid and invalid data to tests lines of the form...





More information about the tor-commits mailing list