commit 5e12da0a50ee60577f3a571eea0ef3350e146d10 Author: Damian Johnson atagar@torproject.org Date: Sat Sep 8 12:15:34 2012 -0700
Parsing network-status-version attribute
I don't like our assumption that this is a v3 network status document, but I'll need to think more about how to handle other versions later. For now keeping the validation assertion that parsed documents are v3. --- stem/descriptor/networkstatus.py | 56 +++++++++++++++--------- test/integ/descriptor/networkstatus.py | 4 +- test/unit/descriptor/networkstatus/document.py | 16 ++++++- 3 files changed, 50 insertions(+), 26 deletions(-)
diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py index 2140b4d..d554d42 100644 --- a/stem/descriptor/networkstatus.py +++ b/stem/descriptor/networkstatus.py @@ -198,7 +198,7 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
:var tuple routers: RouterStatusEntry contained in the document
- :var str network_status_version: ***** document version + :var str version: ***** document version :var str vote_status: ***** status of the vote (is either "vote" or "consensus") :var int consensus_method: **~** consensus method used to generate a consensus :var list consensus_methods: **^** A list of supported consensus generation methods (integers) @@ -236,7 +236,7 @@ class NetworkStatusDocument(stem.descriptor.Descriptor): self.directory_authorities = [] self.directory_signatures = []
- self.network_status_version = None + self.version = None self.vote_status = None self.consensus_methods = [] self.published = None @@ -255,8 +255,8 @@ class NetworkStatusDocument(stem.descriptor.Descriptor): document_file = StringIO(raw_content) header, footer, routers_end = _get_document_content(document_file, validate)
- self._parse_old(header + footer, validate) self._parse(header, footer, validate) + self._parse_old(header + footer, validate)
if document_file.tell() < routers_end: self.routers = tuple(_get_routers(document_file, validate, self, routers_end, self._get_router_type())) @@ -266,9 +266,6 @@ class NetworkStatusDocument(stem.descriptor.Descriptor): def _get_router_type(self): return RouterStatusEntry
- def _validate_network_status_version(self): - return self.network_status_version == "3" - def get_unrecognized_lines(self): """ Returns any unrecognized trailing lines. @@ -292,23 +289,27 @@ class NetworkStatusDocument(stem.descriptor.Descriptor): header_entries = stem.descriptor._get_descriptor_components(header, validate)[0] footer_entries = stem.descriptor._get_descriptor_components(footer, validate)[0]
- if validate: - if not 'vote-status' in header_entries: - raise ValueError("Network status documents must have a 'vote-status' line to say if they're a vote or consensus") - + all_entries = dict() + all_entries.update(header_entries) + all_entries.update(footer_entries) + + if 'vote-status' in header_entries: is_consensus = header_entries['vote-status'][0][0] == "consensus" is_vote = not is_consensus + else: + if validate: + raise ValueError("Network status documents must have a 'vote-status' line to say if they're a vote or consensus") + + is_consensus, is_vote = True, False + + if validate: self._check_for_missing_and_disallowed_fields(is_consensus, header_entries, footer_entries) self._check_for_misordered_fields(is_consensus, header_entries, footer_entries)
known_fields = [attr[0] for attr in HEADER_STATUS_DOCUMENT_FIELDS + FOOTER_STATUS_DOCUMENT_FIELDS] content = header + '\n' + footer
- entries = dict() - entries.update(header_entries) - entries.update(footer_entries) - - for keyword, values in entries.items(): + for keyword, values in all_entries.items(): value, block_contents = values[0] line = "%s %s" % (keyword, value)
@@ -318,15 +319,28 @@ class NetworkStatusDocument(stem.descriptor.Descriptor): if validate and len(values) > 1 and keyword in known_fields: if not (keyword == 'directory-signature' and is_consensus): raise ValueError("Network status documents can only have a single '%s' line, got %i:\n%s" % (keyword, len(values), content)) + + if keyword == 'network-status-version': + # "network-status-version" version + + self.version = value + + # TODO: Obviously not right when we extend this to parse v2 documents, + # but we'll cross that bridge when we come to it. + + if validate and self.version != "3": + raise ValueError("Expected a version 3 network status documents, got version '%s' instead" % self.version)
def _parse_old(self, raw_content, validate): # preamble content = StringIO(raw_content) read_keyword_line = lambda keyword, optional = False: setattr(self, keyword.replace("-", "_"), _read_keyword_line(keyword, content, validate, optional))
- map(read_keyword_line, ["network-status-version", "vote-status"]) - if validate and not self._validate_network_status_version(): - raise ValueError("Invalid network-status-version: %s" % self.network_status_version) + # ignore things the parse() method handles + _read_keyword_line("network-status-version", content, False, True) + + + map(read_keyword_line, ["vote-status"])
vote = False if self.vote_status == "vote": vote = True @@ -479,7 +493,7 @@ class NetworkStatusDocument(stem.descriptor.Descriptor): if actual != expected: actual_label = ', '.join(actual) expected_label = ', '.join(expected) - raise ValueError("The fields in the document's %s are misordered. It should be '%s' but was '%s'" % (lable, actual_label, expected_label)) + raise ValueError("The fields in the document's %s are misordered. It should be '%s' but was '%s'" % (label, actual_label, expected_label))
class DirectoryAuthority(stem.descriptor.Descriptor): """ @@ -855,7 +869,7 @@ class MicrodescriptorConsensus(NetworkStatusDocument): """ A v3 microdescriptor consensus.
- :var str network_status_version: ***** a document format version. For v3 microdescriptor consensuses this is "3 microdesc" + :var str version: ***** a document format version. For v3 microdescriptor consensuses this is "3 microdesc" :var str vote_status: ***** status of the vote (is "consensus") :var int consensus_method: **~** consensus method used to generate a consensus :var datetime valid_after: ***** time when the consensus becomes valid @@ -879,7 +893,7 @@ class MicrodescriptorConsensus(NetworkStatusDocument): return RouterMicrodescriptor
def _validate_network_status_version(self): - return self.network_status_version == "3 microdesc" + return self.version == "3 microdesc"
class RouterMicrodescriptor(RouterStatusEntry): """ diff --git a/test/integ/descriptor/networkstatus.py b/test/integ/descriptor/networkstatus.py index 4ca8e5c..1777828 100644 --- a/test/integ/descriptor/networkstatus.py +++ b/test/integ/descriptor/networkstatus.py @@ -85,7 +85,7 @@ class TestNetworkStatus(unittest.TestCase): router1 = desc.routers[0] descriptor_file.close()
- self.assertEquals("3", desc.network_status_version) + self.assertEquals("3", desc.version) self.assertEquals("consensus", desc.vote_status) self.assertEquals([], desc.consensus_methods) self.assertEquals(None, desc.published) @@ -177,7 +177,7 @@ I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY= router1 = desc.routers[0] descriptor_file.close()
- self.assertEquals("3", desc.network_status_version) + self.assertEquals("3", desc.version) self.assertEquals("vote", desc.vote_status) self.assertEquals(range(1, 13), desc.consensus_methods) self.assertEquals(_strptime("2012-07-11 23:50:01"), desc.published) diff --git a/test/unit/descriptor/networkstatus/document.py b/test/unit/descriptor/networkstatus/document.py index ef77391..a099fa9 100644 --- a/test/unit/descriptor/networkstatus/document.py +++ b/test/unit/descriptor/networkstatus/document.py @@ -100,7 +100,7 @@ class TestNetworkStatusDocument(unittest.TestCase): sig = DirectorySignature("directory-signature " + NETWORK_STATUS_DOCUMENT_ATTR["directory-signature"])
self.assertEqual((), document.routers) - self.assertEqual("3", document.network_status_version) + self.assertEqual("3", document.version) self.assertEqual("consensus", document.vote_status) self.assertEqual(9, document.consensus_method) self.assertEqual([], document.consensus_methods) @@ -133,7 +133,7 @@ class TestNetworkStatusDocument(unittest.TestCase): sig = DirectorySignature("directory-signature " + NETWORK_STATUS_DOCUMENT_ATTR["directory-signature"])
self.assertEqual((), document.routers) - self.assertEqual("3", document.network_status_version) + self.assertEqual("3", document.version) self.assertEqual("vote", document.vote_status) self.assertEqual(None, document.consensus_method) self.assertEqual([9], document.consensus_methods) @@ -213,5 +213,15 @@ class TestNetworkStatusDocument(unittest.TestCase): content = "\n".join(test_lines) self.assertRaises(ValueError, NetworkStatusDocument, content) NetworkStatusDocument(content, False) # constructs without validation - + + def test_invalid_version(self): + """ + Try parsing a different document version with the v3 parser. + """ + + content = get_network_status_document({"network-status-version": "4"}) + self.assertRaises(ValueError, NetworkStatusDocument, content) + + document = NetworkStatusDocument(content, False) + self.assertEquals("4", document.version)