[tor-commits] [stem/master] Implementing a get_consensus() method
atagar at torproject.org
atagar at torproject.org
Mon Jul 22 03:10:17 UTC 2013
commit 5514b2cfd7d313f429bfe1bb8a13c975f7c3413f
Author: Damian Johnson <atagar at torproject.org>
Date: Thu Jul 18 09:59:30 2013 -0700
Implementing a get_consensus() method
Originally this was gonna be a get_network_status(fingerprint) method but
evedently we can't request individual router status entries. Understandable
since signatures are for the whole document but still a bit of a pity from an
API perspective. Oh well.
---
stem/descriptor/__init__.py | 2 +-
stem/descriptor/remote.py | 60 +++++++++++++++++++++++++++++----------
test/integ/descriptor/remote.py | 32 ++++++++++++++++++++-
test/runner.py | 1 +
4 files changed, 78 insertions(+), 17 deletions(-)
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index a527329..82f846d 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -132,7 +132,7 @@ def parse_file(descriptor_file, descriptor_type = None, validate = True, documen
:param bool validate: checks the validity of the descriptor's content if
**True**, skips these checks otherwise
:param stem.descriptor.__init__.DocumentHandler document_handler: method in
- which to parse :class:`~stem.descriptor.networkstatus.NetworkStatusDocument`
+ which to parse the :class:`~stem.descriptor.networkstatus.NetworkStatusDocument`
:returns: iterator for :class:`~stem.descriptor.__init__.Descriptor` instances in the file
diff --git a/stem/descriptor/remote.py b/stem/descriptor/remote.py
index a18d991..66e6d85 100644
--- a/stem/descriptor/remote.py
+++ b/stem/descriptor/remote.py
@@ -82,6 +82,8 @@ class Query(object):
:var str resource: resource being fetched, such as '/tor/status-vote/current/consensus.z'
:var str descriptor_type: type of descriptors being fetched, see
:func:`~stem.descriptor.__init__.parse_file`
+ :param stem.descriptor.__init__.DocumentHandler document_handler: method in
+ which to parse the :class:`~stem.descriptor.networkstatus.NetworkStatusDocument`
:var list endpoints: (address, dirport) tuples of the authority or mirror
we're querying, this uses authorities if undefined
@@ -101,9 +103,10 @@ class Query(object):
finished
"""
- def __init__(self, resource, descriptor_type, endpoints = None, retries = 2, fall_back_to_authority = True, timeout = None, start = True):
+ def __init__(self, resource, descriptor_type, endpoints = None, retries = 2, fall_back_to_authority = True, timeout = None, start = True, document_handler = stem.descriptor.DocumentHandler.ENTRIES):
self.resource = resource
self.descriptor_type = descriptor_type
+ self.document_handler = document_handler
self.endpoints = endpoints if endpoints else []
self.retries = retries
@@ -220,7 +223,7 @@ class Query(object):
response = io.BytesIO(response.read().strip())
- self._results = stem.descriptor.parse_file(response, self.descriptor_type)
+ self._results = stem.descriptor.parse_file(response, self.descriptor_type, document_handler = self.document_handler)
log.trace("Descriptors retrieved from '%s' in %0.2fs" % (self.download_url, self.runtime))
except:
exc = sys.exc_info()[1]
@@ -278,14 +281,14 @@ class DescriptorDownloader(object):
resource = '/tor/server/all'
+ if isinstance(fingerprints, str):
+ fingerprints = [fingerprints]
+
if fingerprints:
- if isinstance(fingerprints, str):
- resource = '/tor/server/fp/%s' % fingerprints
- else:
- if len(fingerprints) > MAX_BATCH_SIZE:
- raise ValueError("Unable to request more than %i descriptors at a time by their fingerprints" % MAX_BATCH_SIZE)
+ if len(fingerprints) > MAX_BATCH_SIZE:
+ raise ValueError("Unable to request more than %i descriptors at a time by their fingerprints" % MAX_BATCH_SIZE)
- resource = '/tor/server/fp/%s' % '+'.join(fingerprints)
+ resource = '/tor/server/fp/%s' % '+'.join(fingerprints)
return self._query(resource, 'server-descriptor 1.0')
@@ -306,18 +309,44 @@ class DescriptorDownloader(object):
resource = '/tor/extra/all'
+ if isinstance(fingerprints, str):
+ fingerprints = [fingerprints]
+
if fingerprints:
- if isinstance(fingerprints, str):
- resource = '/tor/extra/fp/%s' % fingerprints
- else:
- if len(fingerprints) > MAX_BATCH_SIZE:
- raise ValueError("Unable to request more than %i descriptors at a time by their fingerprints" % MAX_BATCH_SIZE)
+ if len(fingerprints) > MAX_BATCH_SIZE:
+ raise ValueError("Unable to request more than %i descriptors at a time by their fingerprints" % MAX_BATCH_SIZE)
- resource = '/tor/extra/fp/%s' % '+'.join(fingerprints)
+ resource = '/tor/extra/fp/%s' % '+'.join(fingerprints)
return self._query(resource, 'extra-info 1.0')
- def _query(self, resource, descriptor_type):
+ def get_consensus(self, document_handler = stem.descriptor.DocumentHandler.ENTRIES, authority_v3ident = None):
+ """
+ Provides the present router status entries.
+
+ :param stem.descriptor.__init__.DocumentHandler document_handler: method in
+ which to parse the :class:`~stem.descriptor.networkstatus.NetworkStatusDocumentV3`
+ :param str authority_v3ident: fingerprint of the authority key for which
+ to get the consensus, see `'v3ident' in tor's config.c
+ <https://gitweb.torproject.org/tor.git/blob/f631b73:/src/or/config.c#l816>`_
+ for the values.
+
+ :returns: :class:`~stem.descriptor.remote.Query` for the router status
+ entries
+ """
+
+ resource = '/tor/status-vote/current/consensus'
+
+ if authority_v3ident:
+ resource += '/%s' % authority_v3ident
+
+ return self._query(
+ resource,
+ 'network-status-consensus-3 1.0',
+ document_handler = document_handler,
+ )
+
+ def _query(self, resource, descriptor_type, document_handler = stem.descriptor.DocumentHandler.ENTRIES):
"""
Issues a request for the given resource.
"""
@@ -330,4 +359,5 @@ class DescriptorDownloader(object):
fall_back_to_authority = self.fall_back_to_authority,
timeout = self.timeout,
start = self.start_when_requested,
+ document_handler = document_handler,
)
diff --git a/test/integ/descriptor/remote.py b/test/integ/descriptor/remote.py
index 62348ab..9273b61 100644
--- a/test/integ/descriptor/remote.py
+++ b/test/integ/descriptor/remote.py
@@ -4,9 +4,10 @@ Integration tests for stem.descriptor.remote.
import unittest
-import stem.descriptor.server_descriptor
import stem.descriptor.extrainfo_descriptor
import stem.descriptor.remote
+import stem.descriptor.router_status_entry
+import stem.descriptor.server_descriptor
import test.runner
# Required to prevent unmarshal error when running this test alone.
@@ -26,6 +27,8 @@ class TestDescriptorReader(unittest.TestCase):
if test.runner.require_online(self):
return
+ elif test.runner.only_run_once(self, "test_using_authorities"):
+ return
queries = []
@@ -51,6 +54,11 @@ class TestDescriptorReader(unittest.TestCase):
Exercises the downloader's get_server_descriptors() method.
"""
+ if test.runner.require_online(self):
+ return
+ elif test.runner.only_run_once(self, "test_get_server_descriptors"):
+ return
+
downloader = stem.descriptor.remote.DescriptorDownloader()
# Fetch a single descriptor and a batch. I'd love to also exercise
@@ -82,6 +90,11 @@ class TestDescriptorReader(unittest.TestCase):
Exercises the downloader's get_extrainfo_descriptors() method.
"""
+ if test.runner.require_online(self):
+ return
+ elif test.runner.only_run_once(self, "test_get_extrainfo_descriptors"):
+ return
+
downloader = stem.descriptor.remote.DescriptorDownloader()
single_query = downloader.get_extrainfo_descriptors('9695DFC35FFEB861329B9F1AB04C46397020CE31')
@@ -101,4 +114,21 @@ class TestDescriptorReader(unittest.TestCase):
self.assertEqual(2, len(list(multiple_query)))
+ def test_get_consensus(self):
+ """
+ Exercises the downloader's get_consensus() method.
+ """
+
+ if test.runner.require_online(self):
+ return
+ elif test.runner.only_run_once(self, "test_get_consensus"):
+ return
+
+ downloader = stem.descriptor.remote.DescriptorDownloader()
+
+ consensus_query = downloader.get_consensus()
+ consensus_query.run()
+ consensus = list(consensus_query)
+ self.assertTrue(len(consensus) > 50)
+ self.assertTrue(isinstance(consensus[0], stem.descriptor.router_status_entry.RouterStatusEntryV3))
diff --git a/test/runner.py b/test/runner.py
index 9bd62f7..a1eda9e 100644
--- a/test/runner.py
+++ b/test/runner.py
@@ -16,6 +16,7 @@ about the tor test instance they're running against.
require_control - skips the test unless tor provides a controller endpoint
require_version - skips the test unless we meet a tor version requirement
require_online - skips unless targets allow for online tests
+ only_run_once - skip the test if it has been ran before
exercise_controller - basic sanity check that a controller connection can be used
get_runner - Singleton for fetching our runtime context.
More information about the tor-commits
mailing list