commit 4e8aaa4daadb02db1686dde57c42c15aef6821e9 Author: Damian Johnson atagar@torproject.org Date: Tue Feb 5 09:48:12 2013 -0800
Support for 'flag-thresholds' lines in network status votes
Parsing the new 'flag-thresholds' in network status votes - thanks to Karsten for pointing this out.
metrics-lib change: https://gitweb.torproject.org/metrics-lib.git/commitdiff/c2a0dbf8bf100a19660...
dir-spec addition: https://trac.torproject.org/8165 --- stem/descriptor/networkstatus.py | 21 ++++++++ test/unit/descriptor/networkstatus/document_v3.py | 52 +++++++++++++++++++++ 2 files changed, 73 insertions(+), 0 deletions(-)
diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py index 9f3730c..3377f5c 100644 --- a/stem/descriptor/networkstatus.py +++ b/stem/descriptor/networkstatus.py @@ -87,6 +87,7 @@ HEADER_STATUS_DOCUMENT_FIELDS = ( ("client-versions", True, True, False), ("server-versions", True, True, False), ("known-flags", True, True, True), + ("flag-thresholds", True, False, False), ("params", True, True, False), )
@@ -442,6 +443,7 @@ class NetworkStatusDocumentV3(NetworkStatusDocument):
:var list consensus_methods: list of ints for the supported method versions :var datetime published: time when the document was published + :var dict flag_thresholds: ***** mapping of internal performance thresholds used while making the vote
***** attribute is either required when we're parsed with validation or has a default value, others are left as None if undefined @@ -551,6 +553,7 @@ class _DocumentHeader(object): self.client_versions = [] self.server_versions = [] self.known_flags = [] + self.flag_thresholds = {} self.params = dict(DEFAULT_PARAMS) if default_params else {}
self._unrecognized_lines = [] @@ -681,6 +684,24 @@ class _DocumentHeader(object):
# simply fetches the entries, excluding empty strings self.known_flags = [entry for entry in value.split(" ") if entry] + elif keyword == "flag-thresholds": + # "flag-thresholds" SP THRESHOLDS + # + # TODO: format is being discussed on... + # https://trac.torproject.org/8165 + + value = value.strip() + + if value: + for entry in value.split(" "): + if not '=' in entry: + if not validate: + continue + + raise ValueError("Network status document's '%s' line is expected to be space separated key=value mappings, got: %s" % (keyword, line)) + + entry_key, entry_value = entry.split("=", 1) + self.flag_thresholds[entry_key] = entry_value elif keyword == "params": # "params" [Parameters] # Parameter ::= Keyword '=' Int32 diff --git a/test/unit/descriptor/networkstatus/document_v3.py b/test/unit/descriptor/networkstatus/document_v3.py index 74c7fe4..71d56e5 100644 --- a/test/unit/descriptor/networkstatus/document_v3.py +++ b/test/unit/descriptor/networkstatus/document_v3.py @@ -65,6 +65,7 @@ class TestNetworkStatusDocument(unittest.TestCase): self.assertEqual([], document.client_versions) self.assertEqual([], document.server_versions) self.assertEqual(expected_known_flags, document.known_flags) + self.assertEqual({}, document.flag_thresholds) self.assertEqual(DEFAULT_PARAMS, document.params) self.assertEqual((), document.directory_authorities) self.assertEqual({}, document.bandwidth_weights) @@ -98,6 +99,7 @@ class TestNetworkStatusDocument(unittest.TestCase): self.assertEqual([], document.client_versions) self.assertEqual([], document.server_versions) self.assertEqual(expected_known_flags, document.known_flags) + self.assertEqual({}, document.flag_thresholds) self.assertEqual(DEFAULT_PARAMS, document.params) self.assertEqual((), document.directory_authorities) self.assertEqual({}, document.bandwidth_weights) @@ -472,6 +474,56 @@ class TestNetworkStatusDocument(unittest.TestCase): document = get_network_status_document_v3({"known-flags": test_value}) self.assertEquals(expected_value, document.known_flags)
+ def test_flag_thresholds(self): + """ + Parses the flag-thresholds entry. + """ + + test_values = ( + ("", {}), + ("fast-speed=40960", {u"fast-speed": u"40960"}), # numeric value + ("guard-wfu=94.669%", {u"guard-wfu": u"94.669%"}), # percentage value + ("guard-wfu=94.669% guard-tk=691200", {u"guard-wfu": u"94.669%", u"guard-tk": u"691200"}), # multiple values + ) + + for test_value, expected_value in test_values: + document = get_network_status_document_v3({"vote-status": "vote", "flag-thresholds": test_value}) + self.assertEquals(expected_value, document.flag_thresholds) + + # parses a full entry found in an actual vote + + full_line = "stable-uptime=693369 stable-mtbf=153249 fast-speed=40960 guard-wfu=94.669% guard-tk=691200 guard-bw-inc-exits=174080 guard-bw-exc-exits=184320 enough-mtbf=1" + + expected_value = { + u"stable-uptime": u"693369", + u"stable-mtbf": u"153249", + u"fast-speed": u"40960", + u"guard-wfu": u"94.669%", + u"guard-tk": u"691200", + u"guard-bw-inc-exits": u"174080", + u"guard-bw-exc-exits": u"184320", + u"enough-mtbf": u"1", + } + + document = get_network_status_document_v3({"vote-status": "vote", "flag-thresholds": full_line}) + self.assertEquals(expected_value, document.flag_thresholds) + + # TODO: At present our validation is pretty permissive since the field + # doesn't yet have a formal specificiation. We should expand this test when + # it does. + + test_values = ( + "stable-uptime 693369", # not a key=value mapping + #"stable-uptime=693369\tstable-mtbf=153249", # non-space divider + ) + + for test_value in test_values: + content = get_network_status_document_v3({"vote-status": "vote", "flag-thresholds": test_value}, content = True) + self.assertRaises(ValueError, NetworkStatusDocumentV3, content) + + document = NetworkStatusDocumentV3(content, False) + self.assertEquals({}, document.flag_thresholds) + def test_params(self): """ General testing for the 'params' line, exercising the happy cases.