commit 23513eb220ed195ee73fcadd6fb826c3581810fc
Author: Damian Johnson <[email protected]>
Date: Sat Oct 13 19:09:06 2012 -0700
Adding a tutorial to our sphinx front page
Tutorial for basic stem usage, with tests for the examples we provide. Our
documentation is still incredibly beginner unfriendly, but at least this
gives
them a place to start.
On a side note one of our integ tests kinda sorta killed our test instance
by
calling...
controller.signal("INT")
We didn't notice this because it happened in our very last test.
---
docs/index.rst | 125 ++++++++++++++++++++++++++--
test/integ/control/controller.py | 28 ++++++-
test/integ/descriptor/server_descriptor.py | 29 +++++++
3 files changed, 173 insertions(+), 9 deletions(-)
diff --git a/docs/index.rst b/docs/index.rst
index 32b3d71..197977d 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,13 +1,126 @@
-.. Stem documentation master file, created by
- sphinx-quickstart on Thu May 31 09:56:13 2012.
- You can adapt this file completely to your liking, but it should at least
- contain the root `toctree` directive.
-
Welcome to Stem!
================
Stem is a python controller library for `Tor <https://www.torproject.org/>`_.
Like its predecessor, `TorCtl
<https://www.torproject.org/getinvolved/volunteer.html.en#project-torctl>`_, it
uses Tor's `control protocol
<https://gitweb.torproject.org/torspec.git/blob/HEAD:/control-spec.txt>`_ to
help developers program against the Tor process, enabling them to build things
similar to `Vidalia
<https://www.torproject.org/getinvolved/volunteer.html.en#project-vidalia>`_
and `arm <http://www.atagar.com/arm/>`_.
+Getting started with any new library can be rather daunting, so lets get our
feet wet by jumping straight in with a tutorial...
+
+The Little Relay that Could
+---------------------------
+
+Lets say you just set up your very first `Tor relay
<https://www.torproject.org/docs/tor-doc-relay.html.en>`_. Thank you! Now you
want to write a script that tells you how much it is being used.
+
+First, for any script we write to be able to talk with our relay it'll need to
have a control port available. This is a port that's usually only available on
localhost and protected by either a password or authentication cookie.
+
+Look at your `torrc <https://www.torproject.org/docs/faq.html.en#torrc>`_ for
the following configuration options...
+
+::
+
+ # This provides a port for the script we write to talk to. If you set this
+ # then be sure to also have either set the CookieAuthentication flag *or*
+ # provide a HashedControlPassword!
+
+ ControlPort 9051
+
+ # This will make Tor write an authentication cookie file. Anything that can
+ # read that file can connect to Tor. If you're going to run this script with
+ # the same user as Tor then this is the easiest method of authentication to
+ # use.
+
+ CookieAuthentication 1
+
+ # Alternatively we can authenticate with a password. To set a password first
+ # get its hash...
+ #
+ # % tor --hash-password "my_password"
+ # 16:E600ADC1B52C80BB6022A0E999A7734571A451EB6AE50FED489B72E3DF
+ #
+ # ... and use that for the HashedControlPassword in our torrc.
+
+ HashedControlPassword
16:E600ADC1B52C80BB6022A0E999A7734571A451EB6AE50FED489B72E3DF
+
+You'll need to restart Tor or issue a SIGHUP for these new settings to take
effect. Now lets write a script that tells us how many bytes Tor has sent and
received...
+
+::
+
+ from stem.control import Controller
+
+ controller = Controller.from_port(control_port = 9051)
+ controller.authenticate() # provide the password here if you set one
+
+ bytes_read = controller.get_info("traffic/read")
+ bytes_written = controller.get_info("traffic/written")
+
+ print "My Tor relay has read %s bytes and written %s." % (bytes_read,
bytes_written)
+
+::
+
+ % python example.py
+ My Tor relay has read 33406 bytes and written 29649.
+
+Congratulations! You've written your first controller script.
+
+Mirror Mirror on the Wall
+-------------------------
+
+A script that tells us our contributed bandwidth is neat and all, but now lets
figure out who the *biggest* exit relays are.
+
+Information about the Tor relay network come from documents called
**descriptors**. Descriptors can come from a few things...
+
+1. The Tor control port with GETINFO options like **desc/all-recent** and
**ns/all**.
+2. Files in Tor's data directory, like **cached-descriptors** and
**cached-consensus**.
+3. The descriptor archive on `Tor's metrics site
<https://metrics.torproject.org/data.html>`_.
+
+We've already used the control port, so for this example we'll use the cached
files directly. First locate Tor's data directory. If your torrc has a
DataDirectory line then that's the spot. If not then check Tor's man page for
the default location.
+
+Tor has several descriptor types. For bandwidth information we'll go to the
server descriptors, which are located in the **cached-descriptors** file. These
have somewhat infrequently changing information published by the relays
themselves.
+
+To read this file we'll use the
:class:`~stem.descriptor.reader.DescriptorReader`, a class designed to read
descriptor files.
+
+::
+
+ import sys
+ from stem.descriptor.reader import DescriptorReader
+
+ bw_to_relay = {} # mapping of observed bandwidth to the relay nicknames
+
+ with DescriptorReader(["/home/atagar/.tor/cached-descriptors"]) as reader:
+ for desc in reader:
+ if desc.exit_policy.is_exiting_allowed():
+ bw_to_relay.setdefault(desc.observed_bandwidth,
[]).append(desc.nickname)
+
+ sorted_bw = reversed(sorted(bw_to_relay.keys()))
+
+ # prints the top fifteen relays
+
+ count = 1
+ for bw_value in sorted_bw:
+ for nickname in bw_to_relay[bw_value]:
+ print "%i. %s (%i bytes/s)" % (count, nickname, bw_value)
+ count += 1
+
+ if count > 15:
+ sys.exit()
+
+::
+
+ % python example.py
+ 1. herngaard (42939655 bytes/s)
+ 2. chaoscomputerclub19 (42402911 bytes/s)
+ 3. chaoscomputerclub18 (41967097 bytes/s)
+ 4. chaoscomputerclub20 (40882989 bytes/s)
+ 5. wannabe (40514411 bytes/s)
+ 6. dorrisdeebrown (40349829 bytes/s)
+ 7. manning2 (40057719 bytes/s)
+ 8. chaoscomputerclub21 (38701399 bytes/s)
+ 9. TorLand1 (37983627 bytes/s)
+ 10. bolobolo1 (37676580 bytes/s)
+ 11. manning1 (37117034 bytes/s)
+ 12. gorz (35760527 bytes/s)
+ 13. ndnr1 (26595129 bytes/s)
+ 14. politkovskaja2 (26149682 bytes/s)
+ 15. wau (25929953 bytes/s)
+
:mod:`stem.connection`
----------------------
@@ -16,7 +129,7 @@ Connecting and authenticating to a Tor process.
:mod:`stem.control`
----------------------
-Provides the :class:`stem.control.Controller` class which, as the name
implies, is used for talking with and controlling a Tor instance. As a user
this is the primary class that you'll need.
+Provides the :class:`~stem.control.Controller` class which, as the name
implies, is used for talking with and controlling a Tor instance. As a user
this is the primary class that you'll need.
:mod:`stem.socket`
------------------
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index 7c6708f..36ac14a 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -18,6 +18,31 @@ import test.runner
import test.util
class TestController(unittest.TestCase):
+ def test_tutorial(self):
+ """
+ Tests the tutorial from our front page.
+ """
+
+ if test.runner.require_control(self): return
+ if test.runner.require_version(self, stem.version.Version("0.2.3.1")):
return # uses a relatively new feature
+ elif not test.runner.Torrc.PORT in test.runner.get_runner().get_options():
+ test.runner.skip(self, "no port")
+ return
+
+ from stem.control import Controller
+
+ controller = Controller.from_port(control_port = test.runner.CONTROL_PORT)
+
+ try:
+ controller.authenticate(test.runner.CONTROL_PASSWORD)
+
+ bytes_read = controller.get_info("traffic/read")
+ bytes_written = controller.get_info("traffic/written")
+
+ printed_msg = "My Tor relay has read %s bytes and written %s." %
(bytes_read, bytes_written)
+ finally:
+ controller.close()
+
def test_from_port(self):
"""
Basic sanity check for the from_port constructor.
@@ -362,9 +387,6 @@ class TestController(unittest.TestCase):
# invalid signals
self.assertRaises(stem.socket.InvalidArguments, controller.signal,
"FOOBAR")
-
- controller.signal("INT")
- self.assertRaises(stem.socket.SocketClosed, controller.msg, "GETINFO
version")
def test_extendcircuit(self):
if test.runner.require_control(self): return
diff --git a/test/integ/descriptor/server_descriptor.py
b/test/integ/descriptor/server_descriptor.py
index 55e6545..21d0b33 100644
--- a/test/integ/descriptor/server_descriptor.py
+++ b/test/integ/descriptor/server_descriptor.py
@@ -16,6 +16,35 @@ import test.runner
import test.integ.descriptor
class TestServerDescriptor(unittest.TestCase):
+ def test_tutorial(self):
+ """
+ Runs the tutorial example for parsing server descriptors. We use our
+ metrics consensus rather than the cached one so it won't take overly long.
+ """
+
+ from stem.descriptor.reader import DescriptorReader
+
+ bw_to_relay = {} # mapping of observed bandwidth to the relay nicknames
+
+ descriptor_path = test.integ.descriptor.get_resource("example_descriptor")
+ with DescriptorReader([descriptor_path]) as reader:
+ for desc in reader:
+ if desc.exit_policy.is_exiting_allowed():
+ bw_to_relay.setdefault(desc.observed_bandwidth,
[]).append(desc.nickname)
+
+ sorted_bw = reversed(sorted(bw_to_relay.keys()))
+
+ # prints the top fifteen relays
+
+ count = 1
+ for bw_value in sorted_bw:
+ for nickname in bw_to_relay[bw_value]:
+ printed_line = "%i. %s (%i bytes/s)" % (count, nickname, bw_value)
+ count += 1
+
+ if count > 15:
+ return
+
def test_metrics_descriptor(self):
"""
Parses and checks our results against a server descriptor from metrics.
_______________________________________________
tor-commits mailing list
[email protected]
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits