commit 47acd940498dfe605324bd2e4f08826c7f8c5019 Author: Damian Johnson atagar@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...