tor-commits
Threads by month
- ----- 2025 -----
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
March 2013
- 21 participants
- 1373 discussions

[ooni-probe/master] Get Director, TaskManager's, NetTest and oonicli to talk
by isis@torproject.org 10 Mar '13
by isis@torproject.org 10 Mar '13
10 Mar '13
commit a31d54acc5d55e7bda567c37799afc8d6e2644af
Author: Arturo Filastò <art(a)fuffa.org>
Date: Wed Jan 16 18:09:39 2013 +0100
Get Director, TaskManager's, NetTest and oonicli to talk
Tests are scheduled through the MeasurementManager, Reports via the
ReporterManager and the Director keeps track of everything that is going on.
Configurable retries and timeouts are possible.
As an example, example_simple.py is run and the reports are written.
---
bin/ooniprobe | 14 ++------
nettests/examples/example_simple.py | 8 +++++
ooni/director.py | 7 +---
ooni/managers.py | 5 ++-
ooni/nettest.py | 58 +++++++++++++++++++++++++++--------
ooni/reporter.py | 20 +++++++-----
ooni/tasks.py | 18 +++++-----
tests/test_director.py | 1 -
tests/test_managers.py | 2 -
9 files changed, 83 insertions(+), 50 deletions(-)
diff --git a/bin/ooniprobe b/bin/ooniprobe
index 36f0152..695b137 100755
--- a/bin/ooniprobe
+++ b/bin/ooniprobe
@@ -1,12 +1,4 @@
#!/usr/bin/env python
-# -*- encoding: utf-8 -*-
-# oonicli
-# -----------
-# Runs ooniprobe tests from command line
-#
-# :authors: Arturo Filastò, Isis Lovecruft
-# :licence: see included LICENSE file
-
import os, sys
import copy_reg
from twisted.internet import reactor
@@ -19,5 +11,7 @@ sys.path.insert(0, os.path.abspath(os.getcwd()))
from ooni.utils.hacks import patched_reduce_ex
copy_reg._reduce_ex = patched_reduce_ex
-from ooni.oonicli import run
-run()
+# from ooni.oonicli import run
+# run()
+from ooni.oonicli import runWithDirector
+runWithDirector()
diff --git a/nettests/examples/example_simple.py b/nettests/examples/example_simple.py
new file mode 100644
index 0000000..24de5a6
--- /dev/null
+++ b/nettests/examples/example_simple.py
@@ -0,0 +1,8 @@
+from twisted.internet import defer
+from ooni import nettest
+
+class MyIP(nettest.NetTestCase):
+ def test_simple(self):
+ self.report['foobar'] = 'antani'
+ return defer.succeed(42)
+
diff --git a/ooni/director.py b/ooni/director.py
index ac53f5c..6fe3859 100644
--- a/ooni/director.py
+++ b/ooni/director.py
@@ -118,24 +118,21 @@ class Director(object):
def measurementSucceeded(self, measurement):
self.totalMeasurementRuntime += measurement.runtime
-
self.successfulMeasurements += 1
-
- return measurement.report.write(measurement)
+ return measurement.testInstance.report
def measurementFailed(self, failure, measurement):
self.totalMeasurementRuntime += measurement.runtime
self.failedMeasurements += 1
self.failures.append((failure, measurement))
+ return failure
def reportEntryFailed(self, failure):
# XXX add failure handling logic
return
def netTestDone(self, result, net_test):
- print result
- print "Completed %s" % net_test
self.activeNetTests.remove(net_test)
def startNetTest(self, net_test_loader, options):
diff --git a/ooni/managers.py b/ooni/managers.py
index 818ae5c..533e6b5 100644
--- a/ooni/managers.py
+++ b/ooni/managers.py
@@ -23,10 +23,13 @@ class TaskManager(object):
self.failures = []
def _failed(self, failure, task):
+ # XXX INFINITE RECURSION LOOP INSIDE OF THIS THING
"""
The has failed to complete, we append it to the end of the task chain
to be re-run once all the currently scheduled tasks have run.
"""
+ print "This task has failed"
+ print failure
self._active_tasks.remove(task)
self.failures.append((failure, task))
@@ -147,8 +150,6 @@ class ReportEntryManager(TaskManager):
retries = 3
concurrency = 20
- director = None
-
def succeeded(self, result, task):
pass
diff --git a/ooni/nettest.py b/ooni/nettest.py
index be1c4b3..f82354b 100644
--- a/ooni/nettest.py
+++ b/ooni/nettest.py
@@ -30,7 +30,7 @@ class NetTestState(object):
self.completedScheduling = False
self.allTasksDone = allTasksDone
- def created(self):
+ def taskCreated(self):
self.tasks += 1
def checkAllTasksDone(self):
@@ -38,7 +38,7 @@ class NetTestState(object):
self.doneTasks == self.tasks:
self.allTasksDone.callback(self.doneTasks)
- def taskDone(self, result):
+ def taskDone(self):
"""
This is called every time a task has finished running.
"""
@@ -238,22 +238,51 @@ class NetTest(object):
self.state.taskDone()
return result
+ def makeMeasurement(self, test_class, test_method, test_input=None):
+ """
+ Creates a new instance of :class:ooni.tasks.Measurement and add's it's
+ callbacks and errbacks.
+
+ Args:
+ test_class:
+ a subclass of :class:ooni.nettest.NetTestCase
+
+ test_method:
+ a string that represents the method to be called on test_class
+
+ test_input:
+ optional argument that represents the input to be passed to the
+ NetTestCase
+
+ """
+ measurement = Measurement(test_class, test_method, test_input)
+ measurement.netTest = self
+
+ measurement.done.addCallback(self.director.measurementSucceeded)
+ measurement.done.addErrback(self.director.measurementFailed, measurement)
+
+ measurement.done.addCallback(self.report.write)
+ measurement.done.addErrback(self.director.reportEntryFailed)
+
+ measurement.done.addBoth(self.doneReport)
+ return measurement
+
def generateMeasurements(self):
"""
This is a generator that yields measurements and registers the
callbacks for when a measurement is successful or has failed.
"""
+ self.report.open()
for test_class, test_method in self.testCases:
- for test_input in test_class.inputs:
- measurement = Measurement(test_class, test_method, test_input)
-
- measurement.done.addCallback(self.director.measurementSucceeded)
- measurement.done.addErrback(self.director.measurementFailed)
-
- measurement.done.addCallback(self.report.write)
- measurement.done.addErrback(self.director.reportEntryFailed)
+ if not test_class.inputs:
+ # XXX this is a bit dirty, refactor me
+ yield self.makeMeasurement(test_class, test_method)
+ self.state.taskCreated()
+ break
- measurement.done.addBoth(self.doneReport)
+ for test_input in test_class.inputs:
+ measurement = self.makeMeasurement(test_class, test_method,
+ test_input)
self.state.taskCreated()
yield measurement
@@ -277,7 +306,10 @@ class NetTest(object):
test_instance._checkRequiredOptions()
test_instance._checkValidOptions()
- klass.inputs = test_instance.getInputProcessor()
+ inputs = test_instance.getInputProcessor()
+ if not inputs:
+ inputs = [None]
+ klass.inputs = inputs
class NetTestCase(object):
"""
@@ -424,7 +456,7 @@ class NetTestCase(object):
return inputProcessorIterator()
- return iter(())
+ return None
def _checkValidOptions(self):
for option in self.localOptions:
diff --git a/ooni/reporter.py b/ooni/reporter.py
index 8a39531..7d33bd1 100644
--- a/ooni/reporter.py
+++ b/ooni/reporter.py
@@ -33,7 +33,6 @@ from ooni import config
from ooni.tasks import ReportEntry
-
class ReporterException(Exception):
pass
@@ -204,13 +203,16 @@ class YAMLReporter(OReporter):
log.debug("Writing report with YAML reporter")
self._write('---\n')
self._write(safe_dump(entry))
+ self._write('...\n')
def createReport(self):
"""
Writes the report header and fire callbacks on self.created
"""
self._writeln("###########################################")
- self._writeln("# OONI Probe Report for %s test" % self.test_name)
+
+ self._writeln("# OONI Probe Report for %s (%s)" % (self.testDetails['test_name'],
+ self.testDetails['test_version']))
self._writeln("# %s" % otime.prettyDateNow())
self._writeln("###########################################")
@@ -390,7 +392,7 @@ class Report(object):
a deferred list that will fire once all the report entries have
been written.
"""
- dl = []
+ l = []
for reporter in self.reporters:
def writeReportEntry(result):
report_write_task = ReportEntry(reporter, measurement)
@@ -398,9 +400,10 @@ class Report(object):
return report_write_task.done
d = reporter.created.addBoth(writeReportEntry)
- dl.append(d)
+ l.append(d)
- return defer.DeferredList(dl)
+ dl = defer.DeferredList(l)
+ return dl
def close(self, _):
"""
@@ -411,9 +414,10 @@ class Report(object):
all the reports have been closed.
"""
- dl = []
+ l = []
for reporter in self.reporters:
d = defer.maybeDeferred(reporter.finish)
- dl.append(d)
- return defer.DeferredList(dl)
+ l.append(d)
+ dl = defer.DeferredList(l)
+ return dl
diff --git a/ooni/tasks.py b/ooni/tasks.py
index bd7e5b8..4f7da4c 100644
--- a/ooni/tasks.py
+++ b/ooni/tasks.py
@@ -103,25 +103,25 @@ class Measurement(TaskWithTimeout):
net_test:
a reference to the net_test object such measurement belongs to.
"""
- self.test_instance = test_class()
- self.test_instance.input = test_input
- self.test_instance.report = {}
- self.test_instance._start_time = time.time()
- self.test_instance._setUp()
- self.test_instance.setUp()
+ self.testInstance = test_class()
+ self.testInstance.input = test_input
+ self.testInstance.report = {}
+ self.testInstance._start_time = time.time()
+ self.testInstance._setUp()
+ self.testInstance.setUp()
- self.net_test_method = getattr(self.test_instance, test_method)
+ self.netTestMethod = getattr(self.testInstance, test_method)
TaskWithTimeout.__init__(self)
def succeeded(self, result):
- return self.netTest.succeeded(self)
+ pass
def failed(self, failure):
pass
def run(self):
- d = self.net_test_method()
+ d = self.netTestMethod()
return d
class ReportEntry(TaskWithTimeout):
diff --git a/tests/test_director.py b/tests/test_director.py
index 409047b..365c11f 100644
--- a/tests/test_director.py
+++ b/tests/test_director.py
@@ -45,7 +45,6 @@ class TestDirector(unittest.TestCase):
@d.addCallback
def done(result):
- print "SOMETHING"
self.assertEqual(self.director.successfulMeasurements, 20)
return d
diff --git a/tests/test_managers.py b/tests/test_managers.py
index 13f1847..601e305 100644
--- a/tests/test_managers.py
+++ b/tests/test_managers.py
@@ -12,8 +12,6 @@ from tests.mocks import MockTaskManager, mockFailure, MockDirector
from tests.mocks import MockNetTest, MockMeasurement, MockSuccessMeasurement
from tests.mocks import MockFailMeasurement, MockFailOnceMeasurement
-from decotrace import traced
-
class TestTaskManager(unittest.TestCase):
timeout = 1
def setUp(self):
1
0
commit 45d63b54388d4e0ff0ba750798f5263d4d1a6701
Author: Arturo Filastò <art(a)fuffa.org>
Date: Tue Jan 15 18:56:58 2013 +0100
Minor refactoring and debugging
Further code cleaning and debugging of a non deterministic bug
Refactoring and bugfixing of Director and NetTest
---
ooni/director.py | 27 +++----
ooni/managers.py | 34 ++++-----
ooni/nettest.py | 191 ++++++++++++++++++++++++++++++++----------------
ooni/oonicli.py | 50 +++++++++++--
ooni/reporter.py | 152 ++++++++++++++------------------------
ooni/tasks.py | 29 +++-----
tests/mocks.py | 18 ++---
tests/test_managers.py | 29 +++++--
tests/test_reporter.py | 14 ++--
9 files changed, 300 insertions(+), 244 deletions(-)
diff --git a/ooni/director.py b/ooni/director.py
index 41100cf..ac53f5c 100644
--- a/ooni/director.py
+++ b/ooni/director.py
@@ -51,6 +51,7 @@ class Director(object):
self.reporters = reporters
self.netTests = []
+ self.activeNetTests = []
self.measurementManager = MeasurementManager()
self.measurementManager.director = self
@@ -132,20 +133,18 @@ class Director(object):
# XXX add failure handling logic
return
- def startMeasurements(self, measurements):
- self.measurementManager.schedule(measurements)
-
- def netTestDone(self, net_test):
+ def netTestDone(self, result, net_test):
+ print result
+ print "Completed %s" % net_test
self.activeNetTests.remove(net_test)
- def startNetTest(self, net_test_file, options):
+ def startNetTest(self, net_test_loader, options):
"""
Create the Report for the NetTest and start the report NetTest.
Args:
- net_test_file:
- is either a file path or a file like object that will be used to
- generate the test_cases.
+ net_test_loader:
+ an instance of :class:ooni.nettest.NetTestLoader
options:
is a dict containing the options to be passed to the chosen net
@@ -153,13 +152,13 @@ class Director(object):
"""
report = Report(self.reporters, self.reportEntryManager)
- net_test = NetTest(net_test_file, options, report)
+ net_test = NetTest(net_test_loader, options, report)
+ net_test.setUpNetTestCases()
net_test.director = self
- self.activeNetTests.append(net_test)
- self.activeNetTests.append(net_test)
+ self.measurementManager.schedule(net_test.generateMeasurements())
- d = net_test.start()
- d.addBoth(self.netTestDone)
- return d
+ self.activeNetTests.append(net_test)
+ net_test.done.addBoth(self.netTestDone, net_test)
+ return net_test.done
diff --git a/ooni/managers.py b/ooni/managers.py
index fa59058..818ae5c 100644
--- a/ooni/managers.py
+++ b/ooni/managers.py
@@ -35,12 +35,12 @@ class TaskManager(object):
makeIterable(task))
else:
# This fires the errback when the task is done but has failed.
- task.done.callback(failure)
-
- self.failed(failure, task)
+ task.done.errback(failure)
self._fillSlots()
+ self.failed(failure, task)
+
def _fillSlots(self):
"""
Called on test completion and schedules measurements to be run for the
@@ -53,6 +53,17 @@ class TaskManager(object):
except StopIteration:
break
+ def _run(self, task):
+ """
+ This gets called to add a task to the list of currently active and
+ running tasks.
+ """
+ self._active_tasks.append(task)
+
+ d = task.start()
+ d.addCallback(self._succeeded, task)
+ d.addErrback(self._failed, task)
+
def _succeeded(self, result, task):
"""
We have successfully completed a measurement.
@@ -65,17 +76,6 @@ class TaskManager(object):
task.done.callback(task)
self.succeeded(result, task)
- def _run(self, task):
- """
- This gets called to add a task to the list of currently active and
- running tasks.
- """
- self._active_tasks.append(task)
-
- d = task.start()
- d.addCallback(self._succeeded, task)
- d.addErrback(self._failed, task)
-
@property
def failedMeasurements(self):
return len(self.failures)
@@ -136,13 +136,11 @@ class MeasurementManager(TaskManager):
retries = 2
concurrency = 10
- director = None
-
def succeeded(self, result, measurement):
- self.director.measurementSucceeded(measurement)
+ pass
def failed(self, failure, measurement):
- self.director.measurementFailed(failure, measurement)
+ pass
class ReportEntryManager(TaskManager):
# XXX tweak these values
diff --git a/ooni/nettest.py b/ooni/nettest.py
index 9146924..be1c4b3 100644
--- a/ooni/nettest.py
+++ b/ooni/nettest.py
@@ -6,6 +6,8 @@ from twisted.python import usage, reflect
from ooni.tasks import Measurement
from ooni.utils import log, checkForRoot, NotRootError
+from ooni import config
+from ooni import otime
from inspect import getmembers
from StringIO import StringIO
@@ -58,70 +60,73 @@ class NetTestState(object):
self.completedScheduling = True
self.checkAllTasksDone()
-class NetTest(object):
- director = None
+class NetTestLoader(object):
method_prefix = 'test'
- def __init__(self, net_test_file, options, report):
- """
- net_test_file:
- is a file object containing the test to be run.
-
- options:
- is a dict containing the options to be passed to the net test.
- """
- self.options = options
- self.report = report
- self.test_cases = self.loadNetTest(net_test_file)
-
- # This will fire when all the measurements have been completed and
- # all the reports are done. Done means that they have either completed
- # successfully or all the possible retries have been reached.
- self.done = defer.Deferred()
-
- self.state = NetTestState(self.done)
-
- def start(self):
- """
- Set up tests and start running.
- Start tests and generate measurements.
- """
- self.setUpNetTestCases()
- self.director.startMeasurements(self.generateMeasurements())
- return self.done
-
- def doneReport(self, result):
- """
- This will get called every time a measurement is done and therefore a
- measurement is done.
-
- The state for the NetTest is informed of the fact that another task has
- reached the done state.
- """
- self.state.taskDone()
- return result
-
- def generateMeasurements(self):
- """
- This is a generator that yields measurements and registers the
- callbacks for when a measurement is successful or has failed.
- """
- for test_class, test_method in self.test_cases:
- for test_input in test_class.inputs:
- measurement = Measurement(test_class, test_method, test_input)
-
- measurement.done.addCallback(self.director.measurementSucceeded)
- measurement.done.addErrback(self.director.measurementFailed)
-
- measurement.done.addCallback(self.report.write)
- measurement.done.addErrback(self.director.reportEntryFailed)
-
- measurement.done.addBoth(self.doneReport)
-
- self.state.taskCreated()
- yield measurement
-
- self.state.allTasksScheduled()
+ def __init__(self, net_test_file):
+ self.testCases = self.loadNetTest(net_test_file)
+ # XXX Remove
+ self.testName = 'fooo'
+ self.testVersion = '0.1'
+
+ @property
+ def testDetails(self):
+ from ooni import __version__ as software_version
+
+ client_geodata = {}
+ if config.probe_ip and (config.privacy.includeip or \
+ config.privacy.includeasn or \
+ config.privacy.includecountry or \
+ config.privacy.includecity):
+ log.msg("We will include some geo data in the report")
+ client_geodata = geodata.IPToLocation(config.probe_ip)
+
+ if config.privacy.includeip:
+ client_geodata['ip'] = config.probe_ip
+ else:
+ client_geodata['ip'] = "127.0.0.1"
+
+ # Here we unset all the client geodata if the option to not include then
+ # has been specified
+ if client_geodata and not config.privacy.includeasn:
+ client_geodata['asn'] = 'AS0'
+ elif 'asn' in client_geodata:
+ # XXX this regexp should probably go inside of geodata
+ client_geodata['asn'] = \
+ re.search('AS\d+', client_geodata['asn']).group(0)
+ log.msg("Your AS number is: %s" % client_geodata['asn'])
+ else:
+ client_geodata['asn'] = None
+
+ if (client_geodata and not config.privacy.includecity) \
+ or ('city' not in client_geodata):
+ client_geodata['city'] = None
+
+ if (client_geodata and not config.privacy.includecountry) \
+ or ('countrycode' not in client_geodata):
+ client_geodata['countrycode'] = None
+
+ test_details = {'start_time': otime.utcTimeNow(),
+ 'probe_asn': client_geodata['asn'],
+ 'probe_cc': client_geodata['countrycode'],
+ 'probe_ip': client_geodata['ip'],
+ 'test_name': self.testName,
+ 'test_version': self.testVersion,
+ 'software_name': 'ooniprobe',
+ 'software_version': software_version
+ }
+ return test_details
+
+
+ @property
+ def usageOptions(self):
+ usage_options = None
+ for test_class, test_method in self.testCases:
+ if not usage_options:
+ usage_options = test_class.usageOptions
+ else:
+ assert usage_options == test_class.usageOptions
+ return usage_options
def loadNetTest(self, net_test_file):
"""
@@ -144,6 +149,10 @@ class NetTest(object):
is either a file path or a file like object that will be used to
generate the test_cases.
"""
+ # XXX
+ # self.testName =
+ # os.path.basename('/foo/bar/python.py').replace('.py','')
+ # self.testVersion = '0.1'
test_cases = None
try:
if os.path.isfile(net_test_file):
@@ -196,13 +205,67 @@ class NetTest(object):
pass
return test_cases
+class NetTest(object):
+ director = None
+
+ def __init__(self, net_test_loader, options, report):
+ """
+ net_test_file:
+ is a file object containing the test to be run.
+
+ options:
+ is a dict containing the options to be passed to the net test.
+ """
+ self.options = options
+ self.report = report
+ self.testCases = net_test_loader.testCases
+
+ # This will fire when all the measurements have been completed and
+ # all the reports are done. Done means that they have either completed
+ # successfully or all the possible retries have been reached.
+ self.done = defer.Deferred()
+
+ self.state = NetTestState(self.done)
+
+ def doneReport(self, result):
+ """
+ This will get called every time a measurement is done and therefore a
+ measurement is done.
+
+ The state for the NetTest is informed of the fact that another task has
+ reached the done state.
+ """
+ self.state.taskDone()
+ return result
+
+ def generateMeasurements(self):
+ """
+ This is a generator that yields measurements and registers the
+ callbacks for when a measurement is successful or has failed.
+ """
+ for test_class, test_method in self.testCases:
+ for test_input in test_class.inputs:
+ measurement = Measurement(test_class, test_method, test_input)
+
+ measurement.done.addCallback(self.director.measurementSucceeded)
+ measurement.done.addErrback(self.director.measurementFailed)
+
+ measurement.done.addCallback(self.report.write)
+ measurement.done.addErrback(self.director.reportEntryFailed)
+
+ measurement.done.addBoth(self.doneReport)
+
+ self.state.taskCreated()
+ yield measurement
+
+ self.state.allTasksScheduled()
def setUpNetTestCases(self):
"""
Call processTest and processOptions methods of each NetTestCase
"""
test_classes = set([])
- for test_class, test_method in self.test_cases:
+ for test_class, test_method in self.testCases:
test_classes.add(test_class)
for klass in test_classes:
@@ -373,7 +436,7 @@ class NetTestCase(object):
for required_option in self.requiredOptions:
log.debug("Checking if %s is present" % required_option)
if required_option not in self.localOptions:
- raise MissingRequiredOption
+ raise MissingRequiredOption(required_option)
def __repr__(self):
return "<%s inputs=%s>" % (self.__class__, self.inputs)
diff --git a/ooni/oonicli.py b/ooni/oonicli.py
index d345a25..1959de8 100644
--- a/ooni/oonicli.py
+++ b/ooni/oonicli.py
@@ -12,9 +12,13 @@ from twisted.python import usage, failure
from twisted.python.util import spewer
from ooni import nettest, runner, reporter, config
+from ooni.director import Director
+from ooni.reporter import YAMLReporter, OONIBReporter
from ooni.inputunit import InputUnitFactory
+from ooni.nettest import NetTestLoader, MissingRequiredOption
+
from ooni.utils import net
from ooni.utils import checkForRoot, NotRootError
from ooni.utils import log
@@ -143,10 +147,8 @@ def errorRunningTests(failure):
log.err("There was an error in running a test")
failure.printTraceback()
-def run():
- """
- Parses command line arguments of test.
- """
+
+def parseOptions():
cmd_line_options = Options()
if len(sys.argv) == 1:
cmd_line_options.getUsage()
@@ -155,9 +157,43 @@ def run():
except usage.UsageError, ue:
raise SystemExit, "%s: %s" % (sys.argv[0], ue)
- log.start(cmd_line_options['logfile'])
+ return dict(cmd_line_options)
+
+def runWithDirector():
+ """
+ Instance the director, parse command line options and start an ooniprobe
+ test!
+ """
+ global_options = parseOptions()
+ config.cmd_line_options = global_options
+
+ log.start(global_options['logfile'])
+
+ net_test_args = global_options.pop('subargs')
+ net_test_file = global_options['test']
+
+ net_test_loader = NetTestLoader(net_test_file)
+ options = net_test_loader.usageOptions()
+ options.parseOptions(net_test_args)
- config.cmd_line_options = cmd_line_options
+ net_test_options = dict(options)
+
+ # reporters = [YAMLReporter, OONIBReporter]
+
+ yaml_reporter = YAMLReporter(net_test_loader.testDetails)
+ reporters = [yaml_reporter]
+
+ director = Director(reporters)
+ try:
+ director.startNetTest(net_test_loader, net_test_options)
+ except MissingRequiredOption, option_name:
+ log.err('Missing required option: "%s"' % option_name)
+ print options.getUsage()
+
+def run():
+ """
+ Parses command line arguments of test.
+ """
if config.privacy.includepcap:
log.msg("Starting")
@@ -197,3 +233,5 @@ def run():
d.addErrback(errorRunningTests)
reactor.run()
+
+
diff --git a/ooni/reporter.py b/ooni/reporter.py
index 80595f9..8a39531 100644
--- a/ooni/reporter.py
+++ b/ooni/reporter.py
@@ -33,6 +33,10 @@ from ooni import config
from ooni.tasks import ReportEntry
+
+class ReporterException(Exception):
+ pass
+
def createPacketReport(packet_list):
"""
Takes as input a packet a list.
@@ -110,60 +114,12 @@ def safe_dump(data, stream=None, **kw):
"""
return yaml.dump_all([data], stream, Dumper=OSafeDumper, **kw)
-def getTestDetails(options):
- from ooni import __version__ as software_version
-
- client_geodata = {}
- if config.probe_ip and (config.privacy.includeip or \
- config.privacy.includeasn or \
- config.privacy.includecountry or \
- config.privacy.includecity):
- log.msg("We will include some geo data in the report")
- client_geodata = geodata.IPToLocation(config.probe_ip)
-
- if config.privacy.includeip:
- client_geodata['ip'] = config.probe_ip
- else:
- client_geodata['ip'] = "127.0.0.1"
-
- # Here we unset all the client geodata if the option to not include then
- # has been specified
- if client_geodata and not config.privacy.includeasn:
- client_geodata['asn'] = 'AS0'
- elif 'asn' in client_geodata:
- # XXX this regexp should probably go inside of geodata
- client_geodata['asn'] = \
- re.search('AS\d+', client_geodata['asn']).group(0)
- log.msg("Your AS number is: %s" % client_geodata['asn'])
- else:
- client_geodata['asn'] = None
-
- if (client_geodata and not config.privacy.includecity) \
- or ('city' not in client_geodata):
- client_geodata['city'] = None
-
- if (client_geodata and not config.privacy.includecountry) \
- or ('countrycode' not in client_geodata):
- client_geodata['countrycode'] = None
-
- test_details = {'start_time': otime.utcTimeNow(),
- 'probe_asn': client_geodata['asn'],
- 'probe_cc': client_geodata['countrycode'],
- 'probe_ip': client_geodata['ip'],
- 'test_name': options['name'],
- 'test_version': options['version'],
- 'software_name': 'ooniprobe',
- 'software_version': software_version
- }
- return test_details
-
class OReporter(object):
- created = defer.Deferred()
+ def __init__(self, test_details):
+ self.created = defer.Deferred()
+ self.testDetails = test_details
- def __init__(self, cmd_line_options):
- self.cmd_line_options = dict(cmd_line_options)
-
- def createReport(self, options):
+ def createReport(self):
"""
Override this with your own logic to implement tests.
"""
@@ -179,7 +135,8 @@ class OReporter(object):
pass
def testDone(self, test, test_name):
- # XXX
+ # XXX put this inside of Report.close
+ # or perhaps put something like this inside of netTestDone
log.msg("Finished running %s" % test_name)
test_report = dict(test.report)
@@ -198,30 +155,37 @@ class OReporter(object):
'report': test_report}
return defer.maybeDeferred(self.writeReportEntry, report)
+class InvalidDestination(ReporterException):
+ pass
+
class YAMLReporter(OReporter):
"""
These are useful functions for reporting to YAML format.
+
+ report_destination:
+ the destination directory of the report
+
"""
- def __init__(self, cmd_line_options):
- if cmd_line_options['reportfile'] is None:
- try:
- test_filename = os.path.basename(cmd_line_options['test'])
- except IndexError:
- raise TestFilenameNotSet
-
- test_name = '.'.join(test_filename.split(".")[:-1])
- frm_str = "report_%s_"+otime.timestamp()+".%s"
- reportfile = frm_str % (test_name, "yamloo")
- else:
- reportfile = cmd_line_options['reportfile']
+ def __init__(self, test_details, report_destination='.'):
+ self.reportDestination = report_destination
+
+ if not os.path.isdir(report_destination):
+ raise InvalidDestination
+
+ report_filename = "report-" + \
+ test_details['test_name'] + "-" + \
+ otime.timestamp() + ".yamloo"
- if os.path.exists(reportfile):
- log.msg("Report already exists with filename %s" % reportfile)
- pushFilenameStack(reportfile)
+ report_path = os.path.join(self.reportDestination, report_filename)
- log.debug("Creating %s" % reportfile)
- self._stream = open(reportfile, 'w+')
- OReporter.__init__(self, cmd_line_options)
+ if os.path.exists(report_path):
+ log.msg("Report already exists with filename %s" % report_path)
+ pushFilenameStack(report_path)
+
+ log.debug("Creating %s" % report_path)
+ self._stream = open(report_path, 'w+')
+
+ OReporter.__init__(self, test_details)
def _writeln(self, line):
self._write("%s\n" % line)
@@ -236,26 +200,25 @@ class YAMLReporter(OReporter):
untilConcludes(self._stream.flush)
def writeReportEntry(self, entry):
+ #XXX: all _write, _writeln inside this call should be atomic
log.debug("Writing report with YAML reporter")
self._write('---\n')
self._write(safe_dump(entry))
- self._write('...\n')
- def createReport(self, options):
+ def createReport(self):
+ """
+ Writes the report header and fire callbacks on self.created
+ """
self._writeln("###########################################")
- self._writeln("# OONI Probe Report for %s test" % options['name'])
+ self._writeln("# OONI Probe Report for %s test" % self.test_name)
self._writeln("# %s" % otime.prettyDateNow())
self._writeln("###########################################")
- test_details = getTestDetails(options)
- test_details['options'] = self.cmd_line_options
-
- self.writeReportEntry(test_details)
+ self.writeReportEntry(self.testDetails)
def finish(self):
self._stream.close()
-
class OONIBReportError(Exception):
pass
@@ -269,8 +232,9 @@ class OONIBTestDetailsLookupError(OONIBReportError):
pass
class OONIBReporter(OReporter):
- def __init__(self, cmd_line_options):
- self.backend_url = cmd_line_options['collector']
+ collector_address = ''
+ def __init__(self, test_details, collector_address):
+ self.collector_address = collector_address
self.report_id = None
from ooni.utils.txagentwithsocks import Agent
@@ -281,7 +245,7 @@ class OONIBReporter(OReporter):
except Exception, e:
log.exception(e)
- OReporter.__init__(self, cmd_line_options)
+ OReporter.__init__(self, test_details)
@defer.inlineCallbacks
def writeReportEntry(self, entry):
@@ -290,7 +254,7 @@ class OONIBReporter(OReporter):
content += safe_dump(entry)
content += '...\n'
- url = self.backend_url + '/report'
+ url = self.collector_address + '/report'
request = {'report_id': self.report_id,
'content': content}
@@ -315,12 +279,7 @@ class OONIBReporter(OReporter):
"""
Creates a report on the oonib collector.
"""
- url = self.backend_url + '/report'
-
- try:
- test_details = getTestDetails(options)
- except Exception, e:
- log.exception(e)
+ url = self.collector_address + '/report'
test_details['options'] = self.cmd_line_options
@@ -336,8 +295,8 @@ class OONIBReporter(OReporter):
request = {'software_name': test_details['software_name'],
'software_version': test_details['software_version'],
'probe_asn': test_details['probe_asn'],
- 'test_name': test_name,
- 'test_version': test_version,
+ 'test_name': test_details['test_name'],
+ 'test_version': test_details['test_version'],
'content': content
}
@@ -391,22 +350,19 @@ class Report(object):
Args:
reporters:
- a list of :class:ooni.reporter.OReporter
+ a list of :class:ooni.reporter.OReporter instances
reportEntryManager:
an instance of :class:ooni.tasks.ReportEntryManager
"""
- self.reporters = []
- for r in reporters:
- reporter = r()
- self.reporters.append(reporter)
-
- self.createReports()
+ self.reporters = reporters
self.done = defer.Deferred()
self.done.addCallback(self.close)
self.reportEntryManager = reportEntryManager
+ # XXX call this when starting test
+ # self.open()
def open(self):
"""
diff --git a/ooni/tasks.py b/ooni/tasks.py
index 28aaca4..bd7e5b8 100644
--- a/ooni/tasks.py
+++ b/ooni/tasks.py
@@ -5,12 +5,13 @@ from twisted.internet import defer, reactor
class BaseTask(object):
_timer = None
+ _running = None
+
def __init__(self):
"""
If you want to schedule a task multiple times, remember to create fresh
instances of it.
"""
- self.running = False
self.failures = 0
self.startTime = time.time()
@@ -33,10 +34,10 @@ class BaseTask(object):
return result
def start(self):
- self.running = defer.maybeDeferred(self.run)
- self.running.addErrback(self._failed)
- self.running.addCallback(self._succeeded)
- return self.running
+ self._running = defer.maybeDeferred(self.run)
+ self._running.addErrback(self._failed)
+ self._running.addCallback(self._succeeded)
+ return self._running
def succeeded(self, result):
"""
@@ -67,8 +68,7 @@ class TaskWithTimeout(BaseTask):
def _timedOut(self):
"""Internal method for handling timeout failure"""
- self.timedOut()
- self.running.errback(TaskTimedOut)
+ self._running.errback(TaskTimedOut)
def _cancelTimer(self):
#import pdb; pdb.set_trace()
@@ -87,13 +87,6 @@ class TaskWithTimeout(BaseTask):
self._timer = self.clock.callLater(self.timeout, self._timedOut)
return BaseTask.start(self)
- def timedOut(self):
- """
- Override this with the operations to happen when the task has timed
- out.
- """
- pass
-
class Measurement(TaskWithTimeout):
def __init__(self, test_class, test_method, test_input):
"""
@@ -116,7 +109,8 @@ class Measurement(TaskWithTimeout):
self.test_instance._start_time = time.time()
self.test_instance._setUp()
self.test_instance.setUp()
- self.test = getattr(self.test_instance, test_method)
+
+ self.net_test_method = getattr(self.test_instance, test_method)
TaskWithTimeout.__init__(self)
@@ -126,11 +120,8 @@ class Measurement(TaskWithTimeout):
def failed(self, failure):
pass
- def timedOut(self):
- self.netTest.timedOut()
-
def run(self):
- d = defer.maybeDeferred(self.test)
+ d = self.net_test_method()
return d
class ReportEntry(TaskWithTimeout):
diff --git a/tests/mocks.py b/tests/mocks.py
index fa57927..b692b39 100644
--- a/tests/mocks.py
+++ b/tests/mocks.py
@@ -17,6 +17,7 @@ class MockMeasurementFailOnce(BaseTask):
class MockMeasurementManager(TaskManager):
def __init__(self):
self.successes = []
+ TaskManager.__init__(self)
def failed(self, failure, task):
pass
@@ -34,8 +35,11 @@ class MockReporter(object):
def createReport(self):
pass
+class MockFailure(Exception):
+ pass
+
## from test_managers
-mockFailure = failure.Failure(Exception('mock'))
+mockFailure = failure.Failure(MockFailure('mock'))
class MockSuccessTask(BaseTask):
def run(self):
@@ -71,15 +75,6 @@ class MockFailTaskWithTimeout(TaskWithTimeout):
def run(self):
return defer.fail(mockFailure)
-class MockTaskManager(TaskManager):
- def __init__(self):
- self.successes = []
-
- def failed(self, failure, task):
- pass
-
- def succeeded(self, result, task):
- self.successes.append((result, task))
class MockNetTest(object):
def __init__(self):
@@ -135,13 +130,14 @@ class MockOReporter(object):
def createReport(self):
pass
-
class MockTaskManager(TaskManager):
def __init__(self):
self.successes = []
+ TaskManager.__init__(self)
def failed(self, failure, task):
pass
def succeeded(self, result, task):
self.successes.append((result, task))
+
diff --git a/tests/test_managers.py b/tests/test_managers.py
index 1e469c7..13f1847 100644
--- a/tests/test_managers.py
+++ b/tests/test_managers.py
@@ -2,21 +2,23 @@ from twisted.trial import unittest
from twisted.python import failure
from twisted.internet import defer, task
-from ooni.tasks import BaseTask, TaskWithTimeout
+from ooni.tasks import BaseTask, TaskWithTimeout, TaskTimedOut
from ooni.managers import TaskManager, MeasurementManager
-from tests.mocks import MockSuccessTask, MockFailTask, MockFailOnceTask
+from tests.mocks import MockSuccessTask, MockFailTask, MockFailOnceTask, MockFailure
from tests.mocks import MockSuccessTaskWithTimeout, MockFailTaskThatTimesOut
from tests.mocks import MockTimeoutOnceTask, MockFailTaskWithTimeout
from tests.mocks import MockTaskManager, mockFailure, MockDirector
from tests.mocks import MockNetTest, MockMeasurement, MockSuccessMeasurement
from tests.mocks import MockFailMeasurement, MockFailOnceMeasurement
+from decotrace import traced
+
class TestTaskManager(unittest.TestCase):
timeout = 1
def setUp(self):
self.measurementManager = MockTaskManager()
- self.measurementManager.concurrency = 10
+ self.measurementManager.concurrency = 20
self.measurementManager.retries = 2
self.measurementManager.start()
@@ -57,6 +59,12 @@ class TestTaskManager(unittest.TestCase):
return d
+ def test_schedule_failing_with_mock_failure_task(self):
+ mock_task = MockFailTask()
+ self.measurementManager.schedule(mock_task)
+ self.assertFailure(mock_task.done, MockFailure)
+ return mock_task.done
+
def test_schedule_successful_one_task(self):
return self.schedule_successful_tasks(MockSuccessTask)
@@ -143,17 +151,22 @@ class TestTaskManager(unittest.TestCase):
return mock_task.done
- def test_task_retry_and_succeed_56_tasks(self):
+ def dd_test_task_retry_and_succeed_56_tasks(self):
+ """
+ XXX this test fails in a non-deterministic manner.
+ """
all_done = []
- for x in range(56):
+ number = 56
+ for x in range(number):
mock_task = MockFailOnceTask()
all_done.append(mock_task.done)
self.measurementManager.schedule(mock_task)
d = defer.DeferredList(all_done)
+
@d.addCallback
def done(res):
- self.assertEqual(len(self.measurementManager.failures), 56)
+ self.assertEqual(len(self.measurementManager.failures), number)
for task_result, task_instance in self.measurementManager.successes:
self.assertEqual(task_result, 42)
@@ -192,11 +205,11 @@ class TestMeasurementManager(unittest.TestCase):
mock_task = MockFailMeasurement(self.mockNetTest)
self.measurementManager.schedule(mock_task)
- @mock_task.done.addCallback
+ @mock_task.done.addErrback
def done(failure):
self.assertEqual(len(self.measurementManager.failures), 3)
- self.assertEqual(failure, (mockFailure, mock_task))
+ self.assertEqual(failure, mockFailure)
self.assertEqual(len(self.mockNetTest.successes), 0)
return mock_task.done
diff --git a/tests/test_reporter.py b/tests/test_reporter.py
index e1c7fca..e99debb 100644
--- a/tests/test_reporter.py
+++ b/tests/test_reporter.py
@@ -3,26 +3,28 @@ from twisted.trial import unittest
from ooni.reporter import Report, YAMLReporter, OONIBReporter
from ooni.managers import ReportEntryManager, TaskManager
-from ooni.nettest import NetTest
+from ooni.nettest import NetTest, NetTestState
-from ooni.tasks import TaskMediator, TaskWithTimeout
+from ooni.tasks import TaskWithTimeout
from tests.mocks import MockOReporter, MockTaskManager
from tests.mocks import MockMeasurement, MockNetTest
mockReportOptions = {'name':'foo_test', 'version': '0.1'}
+class MockState(NetTestState):
+ pass
+
class TestReport(unittest.TestCase):
def setUp(self):
- self.report = Report([MockOReporter])
- self.report.reportEntryManager = MockTaskManager()
+ self.taskManager = MockTaskManager()
+ self.report = Report([MockOReporter], self.taskManager)
+ self.state = MockState()
def test_report_alltasksdone_callback_fires(self):
for m in range(10):
measurement = MockMeasurement(MockNetTest())
self.report.write(measurement)
- self.report.report_mediator.allTasksScheduled()
-
@self.report.done.addCallback
def done(reporters):
self.assertEqual(len(reporters), 1)
1
0

[ooni-probe/master] Get test version from net_test_file and parse all options
by isis@torproject.org 10 Mar '13
by isis@torproject.org 10 Mar '13
10 Mar '13
commit 2da452336aeb1eac53809305661ba727d8daef7c
Author: aagbsn <aagbsn(a)extc.org>
Date: Wed Jan 16 18:22:49 2013 +0000
Get test version from net_test_file and parse all options
Adds helper method _parseNetTestOptions to NetTestLoader in order
to assemble the various options and flags into a single object
Also add functionality to read the test name and version from the
NetTestCase
Check that the required option is not None
localOptions is a dict, and the required option may be None.
This adds a test for this case.
---
ooni/nettest.py | 42 ++++++++++++++++++++++++++++++++----------
1 files changed, 32 insertions(+), 10 deletions(-)
diff --git a/ooni/nettest.py b/ooni/nettest.py
index f82354b..7c4278a 100644
--- a/ooni/nettest.py
+++ b/ooni/nettest.py
@@ -65,9 +65,6 @@ class NetTestLoader(object):
def __init__(self, net_test_file):
self.testCases = self.loadNetTest(net_test_file)
- # XXX Remove
- self.testName = 'fooo'
- self.testVersion = '0.1'
@property
def testDetails(self):
@@ -118,12 +115,36 @@ class NetTestLoader(object):
return test_details
+ def _parseNetTestOptions(self, klass):
+ """
+ Helper method to assemble the options into a single UsageOptions object
+ """
+ usage_options = klass.usageOptions
+
+ if not hasattr(usage_options, 'optParameters'):
+ usage_options.optParameters = []
+
+ if klass.inputFile:
+ usage_options.optParameters.append(klass.inputFile)
+
+ if klass.baseParameters:
+ for parameter in klass.baseParameters:
+ usage_options.optParameters.append(parameter)
+
+ if klass.baseFlags:
+ if not hasattr(usage_options, 'optFlags'):
+ usage_options.optFlags = []
+ for flag in klass.baseFlags:
+ usage_options.optFlags.append(flag)
+
+ return usage_options
+
@property
def usageOptions(self):
usage_options = None
for test_class, test_method in self.testCases:
if not usage_options:
- usage_options = test_class.usageOptions
+ usage_options = self._parseNetTestOptions(test_class)
else:
assert usage_options == test_class.usageOptions
return usage_options
@@ -149,10 +170,6 @@ class NetTestLoader(object):
is either a file path or a file like object that will be used to
generate the test_cases.
"""
- # XXX
- # self.testName =
- # os.path.basename('/foo/bar/python.py').replace('.py','')
- # self.testVersion = '0.1'
test_cases = None
try:
if os.path.isfile(net_test_file):
@@ -168,6 +185,10 @@ class NetTestLoader(object):
if not test_cases:
raise NoTestCasesFound
+ test_class, _ = test_cases[0]
+ self.testVersion = test_class.version
+ self.testName = os.path.basename(net_test_file).strip('.py')
+
return test_cases
def _loadNetTestFromFileObject(self, net_test_string):
@@ -467,8 +488,9 @@ class NetTestCase(object):
def _checkRequiredOptions(self):
for required_option in self.requiredOptions:
log.debug("Checking if %s is present" % required_option)
- if required_option not in self.localOptions:
- raise MissingRequiredOption(required_option)
+ if required_option not in self.localOptions or \
+ self.localOptions[required_option] == None:
+ raise MissingRequiredOption(required_option)
def __repr__(self):
return "<%s inputs=%s>" % (self.__class__, self.inputs)
1
0

[ooni-probe/master] Nettests are now running properly with the TaskManager
by isis@torproject.org 10 Mar '13
by isis@torproject.org 10 Mar '13
10 Mar '13
commit cf274700c3e43f86d30a7fd5eb5cd093dbfc4e0a
Author: Arturo Filastò <art(a)fuffa.org>
Date: Wed Jan 16 20:58:29 2013 +0100
Nettests are now running properly with the TaskManager
Issue retries, make tests timeout, etc.
---
ooni/director.py | 4 ++-
ooni/errors.py | 2 +-
ooni/nettest.py | 97 +++++++++++++++++++++++++----------------------------
ooni/reporter.py | 2 -
4 files changed, 50 insertions(+), 55 deletions(-)
diff --git a/ooni/director.py b/ooni/director.py
index 6fe3859..80cf22c 100644
--- a/ooni/director.py
+++ b/ooni/director.py
@@ -1,6 +1,6 @@
from ooni.managers import ReportEntryManager, MeasurementManager
from ooni.reporter import Report
-
+from ooni.utils import log
from ooni.nettest import NetTest
class Director(object):
@@ -117,11 +117,13 @@ class Director(object):
self.totalMeasurements += 1
def measurementSucceeded(self, measurement):
+ log.msg("Successfully completed measurement: %s" % measurement)
self.totalMeasurementRuntime += measurement.runtime
self.successfulMeasurements += 1
return measurement.testInstance.report
def measurementFailed(self, failure, measurement):
+ log.msg("Failed doing measurement: %s" % measurement)
self.totalMeasurementRuntime += measurement.runtime
self.failedMeasurements += 1
diff --git a/ooni/errors.py b/ooni/errors.py
index c7ecfae..cd4a136 100644
--- a/ooni/errors.py
+++ b/ooni/errors.py
@@ -14,7 +14,7 @@ from txsocksx.errors import HostUnreachable, ConnectionRefused
from txsocksx.errors import TTLExpired, CommandNotSupported
from socket import gaierror
-
+from ooni.utils import log
def handleAllFailures(failure):
"""
Here we make sure to trap all the failures that are supported by the
diff --git a/ooni/nettest.py b/ooni/nettest.py
index 7c4278a..728d093 100644
--- a/ooni/nettest.py
+++ b/ooni/nettest.py
@@ -15,51 +15,6 @@ from StringIO import StringIO
class NoTestCasesFound(Exception):
pass
-class NetTestState(object):
- def __init__(self, allTasksDone):
- """
- This keeps track of the state of a running NetTests case.
-
- Args:
- allTasksDone is a deferred that will get fired once all the NetTest
- cases have reached a final done state.
- """
- self.doneTasks = 0
- self.tasks = 0
-
- self.completedScheduling = False
- self.allTasksDone = allTasksDone
-
- def taskCreated(self):
- self.tasks += 1
-
- def checkAllTasksDone(self):
- if self.completedScheduling and \
- self.doneTasks == self.tasks:
- self.allTasksDone.callback(self.doneTasks)
-
- def taskDone(self):
- """
- This is called every time a task has finished running.
- """
- self.doneTasks += 1
- self.checkAllTasksDone()
-
- def allTasksScheduled(self):
- """
- This should be called once all the tasks that need to run have been
- scheduled.
-
- XXX this is ghetto.
- The reason for which we are calling allTasksDone inside of the
- allTasksScheduled method is called after all tasks are done, then we
- will run into a race condition. The race is that we don't end up
- checking that all the tasks are complete because no task is to be
- scheduled.
- """
- self.completedScheduling = True
- self.checkAllTasksDone()
-
class NetTestLoader(object):
method_prefix = 'test'
@@ -226,6 +181,51 @@ class NetTestLoader(object):
pass
return test_cases
+class NetTestState(object):
+ def __init__(self, allTasksDone):
+ """
+ This keeps track of the state of a running NetTests case.
+
+ Args:
+ allTasksDone is a deferred that will get fired once all the NetTest
+ cases have reached a final done state.
+ """
+ self.doneTasks = 0
+ self.tasks = 0
+
+ self.completedScheduling = False
+ self.allTasksDone = allTasksDone
+
+ def taskCreated(self):
+ self.tasks += 1
+
+ def checkAllTasksDone(self):
+ if self.completedScheduling and \
+ self.doneTasks == self.tasks:
+ self.allTasksDone.callback(self.doneTasks)
+
+ def taskDone(self):
+ """
+ This is called every time a task has finished running.
+ """
+ self.doneTasks += 1
+ self.checkAllTasksDone()
+
+ def allTasksScheduled(self):
+ """
+ This should be called once all the tasks that need to run have been
+ scheduled.
+
+ XXX this is ghetto.
+ The reason for which we are calling allTasksDone inside of the
+ allTasksScheduled method is called after all tasks are done, then we
+ will run into a race condition. The race is that we don't end up
+ checking that all the tasks are complete because no task is to be
+ scheduled.
+ """
+ self.completedScheduling = True
+ self.checkAllTasksDone()
+
class NetTest(object):
director = None
@@ -295,12 +295,7 @@ class NetTest(object):
"""
self.report.open()
for test_class, test_method in self.testCases:
- if not test_class.inputs:
- # XXX this is a bit dirty, refactor me
- yield self.makeMeasurement(test_class, test_method)
- self.state.taskCreated()
- break
-
+ log.debug("Running %s %s" % (test_class, test_method))
for test_input in test_class.inputs:
measurement = self.makeMeasurement(test_class, test_method,
test_input)
diff --git a/ooni/reporter.py b/ooni/reporter.py
index 7d33bd1..30c4817 100644
--- a/ooni/reporter.py
+++ b/ooni/reporter.py
@@ -363,8 +363,6 @@ class Report(object):
self.done.addCallback(self.close)
self.reportEntryManager = reportEntryManager
- # XXX call this when starting test
- # self.open()
def open(self):
"""
1
0

[ooni-probe/master] Refactoring of the architecture of the taskManager
by isis@torproject.org 10 Mar '13
by isis@torproject.org 10 Mar '13
10 Mar '13
commit 813230cc416338740bf72acb04b97042ebd5261a
Author: Arturo Filastò <art(a)fuffa.org>
Date: Tue Jan 15 18:01:45 2013 +0100
Refactoring of the architecture of the taskManager
Minimize coupling, make clearer the chain of responsibilities
---
ooni/director.py | 23 ++++++++--
ooni/managers.py | 36 +++++---------
ooni/nettest.py | 123 +++++++++++++++++++++++++++++++++---------------
ooni/reporter.py | 70 +++++++++++++++++++--------
ooni/tasks.py | 65 ++-----------------------
tests/test_nettest.py | 3 +
6 files changed, 174 insertions(+), 146 deletions(-)
diff --git a/ooni/director.py b/ooni/director.py
index 582bb84..41100cf 100644
--- a/ooni/director.py
+++ b/ooni/director.py
@@ -120,13 +120,25 @@ class Director(object):
self.successfulMeasurements += 1
+ return measurement.report.write(measurement)
+
def measurementFailed(self, failure, measurement):
self.totalMeasurementRuntime += measurement.runtime
self.failedMeasurements += 1
self.failures.append((failure, measurement))
- def startTest(self, net_test_file, options):
+ def reportEntryFailed(self, failure):
+ # XXX add failure handling logic
+ return
+
+ def startMeasurements(self, measurements):
+ self.measurementManager.schedule(measurements)
+
+ def netTestDone(self, net_test):
+ self.activeNetTests.remove(net_test)
+
+ def startNetTest(self, net_test_file, options):
"""
Create the Report for the NetTest and start the report NetTest.
@@ -139,12 +151,15 @@ class Director(object):
is a dict containing the options to be passed to the chosen net
test.
"""
- report = Report(self.reporters)
- report.reportEntryManager = self.reportEntryManager
+ report = Report(self.reporters, self.reportEntryManager)
net_test = NetTest(net_test_file, options, report)
- net_test.measurementManager = self.measurementManager
+ net_test.director = self
+
+ self.activeNetTests.append(net_test)
+ self.activeNetTests.append(net_test)
d = net_test.start()
+ d.addBoth(self.netTestDone)
return d
diff --git a/ooni/managers.py b/ooni/managers.py
index 3983705..fa59058 100644
--- a/ooni/managers.py
+++ b/ooni/managers.py
@@ -15,13 +15,12 @@ def makeIterable(item):
class TaskManager(object):
retries = 2
- failures = []
concurrency = 10
- completedTasks = 0
-
- _tasks = iter(())
- _active_tasks = []
+ def __init__(self):
+ self._tasks = iter(())
+ self._active_tasks = []
+ self.failures = []
def _failed(self, failure, task):
"""
@@ -35,7 +34,8 @@ class TaskManager(object):
self._tasks = itertools.chain(self._tasks,
makeIterable(task))
else:
- task.done.callback((failure, task))
+ # This fires the errback when the task is done but has failed.
+ task.done.callback(failure)
self.failed(failure, task)
@@ -58,11 +58,11 @@ class TaskManager(object):
We have successfully completed a measurement.
"""
self._active_tasks.remove(task)
- self.completedTasks += 1
self._fillSlots()
- task.done.callback(result)
+ # Fires the done deferred when the task has completed
+ task.done.callback(task)
self.succeeded(result, task)
def _run(self, task):
@@ -105,12 +105,6 @@ class TaskManager(object):
self._fillSlots()
- def started(self, task):
- """
- This hook will get called every time a task has been started.
- """
- pass
-
def failed(self, failure, task):
"""
This hoook is called every time a task has failed.
@@ -138,28 +132,24 @@ class MeasurementManager(TaskManager):
NetTest on the contrary is aware of the typology of measurements that it is
dispatching as they are logically grouped by test file.
"""
+ # XXX tweak these values
retries = 2
-
- failures = []
concurrency = 10
director = None
- def started(self, measurement):
- self.director.measurementStarted(measurement)
-
def succeeded(self, result, measurement):
self.director.measurementSucceeded(measurement)
def failed(self, failure, measurement):
self.director.measurementFailed(failure, measurement)
-
class ReportEntryManager(TaskManager):
- director = None
+ # XXX tweak these values
+ retries = 3
+ concurrency = 20
- def started(self, task):
- pass
+ director = None
def succeeded(self, result, task):
pass
diff --git a/ooni/nettest.py b/ooni/nettest.py
index d6cadc3..9146924 100644
--- a/ooni/nettest.py
+++ b/ooni/nettest.py
@@ -4,7 +4,7 @@ from twisted.internet import defer, reactor
from twisted.trial.runner import filenameToModule
from twisted.python import usage, reflect
-from ooni.tasks import Measurement, TaskMediator
+from ooni.tasks import Measurement
from ooni.utils import log, checkForRoot, NotRootError
from inspect import getmembers
@@ -13,8 +13,53 @@ from StringIO import StringIO
class NoTestCasesFound(Exception):
pass
+class NetTestState(object):
+ def __init__(self, allTasksDone):
+ """
+ This keeps track of the state of a running NetTests case.
+
+ Args:
+ allTasksDone is a deferred that will get fired once all the NetTest
+ cases have reached a final done state.
+ """
+ self.doneTasks = 0
+ self.tasks = 0
+
+ self.completedScheduling = False
+ self.allTasksDone = allTasksDone
+
+ def created(self):
+ self.tasks += 1
+
+ def checkAllTasksDone(self):
+ if self.completedScheduling and \
+ self.doneTasks == self.tasks:
+ self.allTasksDone.callback(self.doneTasks)
+
+ def taskDone(self, result):
+ """
+ This is called every time a task has finished running.
+ """
+ self.doneTasks += 1
+ self.checkAllTasksDone()
+
+ def allTasksScheduled(self):
+ """
+ This should be called once all the tasks that need to run have been
+ scheduled.
+
+ XXX this is ghetto.
+ The reason for which we are calling allTasksDone inside of the
+ allTasksScheduled method is called after all tasks are done, then we
+ will run into a race condition. The race is that we don't end up
+ checking that all the tasks are complete because no task is to be
+ scheduled.
+ """
+ self.completedScheduling = True
+ self.checkAllTasksDone()
+
class NetTest(object):
- measurementManager = None
+ director = None
method_prefix = 'test'
def __init__(self, net_test_file, options, report):
@@ -29,17 +74,12 @@ class NetTest(object):
self.report = report
self.test_cases = self.loadNetTest(net_test_file)
- self.allMeasurementsDone = defer.Deferred()
- self.allReportsDone = defer.Deferred()
-
- # This should fire when all the measurements have been completed and
+ # This will fire when all the measurements have been completed and
# all the reports are done. Done means that they have either completed
# successfully or all the possible retries have been reached.
- self.done = defer.DeferredList([self.allMeasurementsDone,
- self.allReportsDone])
+ self.done = defer.Deferred()
- # XXX Fire the done when also all the reporting tasks have been completed.
- # self.done = self.allMeasurementsDone
+ self.state = NetTestState(self.done)
def start(self):
"""
@@ -47,10 +87,42 @@ class NetTest(object):
Start tests and generate measurements.
"""
self.setUpNetTestCases()
- self.measurementManager.schedule(self.generateMeasurements())
-
+ self.director.startMeasurements(self.generateMeasurements())
return self.done
+ def doneReport(self, result):
+ """
+ This will get called every time a measurement is done and therefore a
+ measurement is done.
+
+ The state for the NetTest is informed of the fact that another task has
+ reached the done state.
+ """
+ self.state.taskDone()
+ return result
+
+ def generateMeasurements(self):
+ """
+ This is a generator that yields measurements and registers the
+ callbacks for when a measurement is successful or has failed.
+ """
+ for test_class, test_method in self.test_cases:
+ for test_input in test_class.inputs:
+ measurement = Measurement(test_class, test_method, test_input)
+
+ measurement.done.addCallback(self.director.measurementSucceeded)
+ measurement.done.addErrback(self.director.measurementFailed)
+
+ measurement.done.addCallback(self.report.write)
+ measurement.done.addErrback(self.director.reportEntryFailed)
+
+ measurement.done.addBoth(self.doneReport)
+
+ self.state.taskCreated()
+ yield measurement
+
+ self.state.allTasksScheduled()
+
def loadNetTest(self, net_test_file):
"""
Creates all the necessary test_cases (a list of tuples containing the
@@ -124,33 +196,6 @@ class NetTest(object):
pass
return test_cases
- def succeeded(self, measurement):
- """
- This gets called when a measurement has succeeded.
- """
- self.report.write(measurement)
-
- def generateMeasurements(self):
- """
- This is a generator that yields measurements and sets their timeout
- value and their netTest attribute.
- """
- task_mediator = TaskMediator(self.allMeasurementsDone)
- for test_class, test_method in self.test_cases:
- for test_input in test_class.inputs:
- measurement = Measurement(test_class, test_method,
- test_input, self, task_mediator)
- measurement.netTest = self
- yield measurement
- task_mediator.allTasksScheduled()
-
- @task_mediator.allTasksDone.addCallback
- def done(result):
- """
- Once all the MeasurementsTasks have been completed all the report
- tasks will have been scheduled.
- """
- self.report.report_mediator.allTasksScheduled()
def setUpNetTestCases(self):
"""
diff --git a/ooni/reporter.py b/ooni/reporter.py
index d24edcc..80595f9 100644
--- a/ooni/reporter.py
+++ b/ooni/reporter.py
@@ -31,7 +31,7 @@ from ooni.utils.net import BodyReceiver, StringProducer, userAgents
from ooni import config
-from ooni.tasks import ReportEntry, TaskMediator
+from ooni.tasks import ReportEntry
def createPacketReport(packet_list):
"""
@@ -158,6 +158,8 @@ def getTestDetails(options):
return test_details
class OReporter(object):
+ created = defer.Deferred()
+
def __init__(self, cmd_line_options):
self.cmd_line_options = dict(cmd_line_options)
@@ -380,15 +382,19 @@ class ReportClosed(Exception):
pass
class Report(object):
- reportEntryManager = None
-
- def __init__(self, reporters):
+ def __init__(self, reporters, reportEntryManager):
"""
- This will instantiate all the reporters and add them to the list of
- available reporters.
+ This is an abstraction layer on top of all the configured reporters.
+
+ It allows to lazily write to the reporters that are to be used.
+
+ Args:
+
+ reporters:
+ a list of :class:ooni.reporter.OReporter
- net_test:
- is a reference to the net_test to which the report object belongs to.
+ reportEntryManager:
+ an instance of :class:ooni.tasks.ReportEntryManager
"""
self.reporters = []
for r in reporters:
@@ -398,13 +404,14 @@ class Report(object):
self.createReports()
self.done = defer.Deferred()
- self.done.addCallback(self.finish)
+ self.done.addCallback(self.close)
- self.report_mediator = TaskMediator(self.done)
+ self.reportEntryManager = reportEntryManager
- def createReports(self):
+ def open(self):
"""
- This will create all the reports that need to be created.
+ This will create all the reports that need to be created and fires the
+ created callback of the reporter whose report got created.
"""
for reporter in self.reporters:
d = defer.maybeDeferred(reporter.createReport)
@@ -413,20 +420,41 @@ class Report(object):
def write(self, measurement):
"""
This is a lazy call that will write to all the reporters by waiting on
- the created callback to fire.
+ them to be created.
- The report_write_task is created before we attach the callback so that
- the report mediator is aware of the total number of created reportEntry
- tasks.
+ Will return a deferred that will fire once the report for the specified
+ measurement have been written to all the reporters.
+
+ Args:
+
+ measurement:
+ an instance of :class:ooni.tasks.Measurement
+
+ Returns:
+ a deferred list that will fire once all the report entries have
+ been written.
"""
+ dl = []
for reporter in self.reporters:
- report_write_task = ReportEntry(reporter, measurement,
- self.report_mediator)
- @reporter.created.addCallback
- def cb(result):
+ def writeReportEntry(result):
+ report_write_task = ReportEntry(reporter, measurement)
self.reportEntryManager.schedule(report_write_task)
+ return report_write_task.done
- def finish(self, result):
+ d = reporter.created.addBoth(writeReportEntry)
+ dl.append(d)
+
+ return defer.DeferredList(dl)
+
+ def close(self, _):
+ """
+ Close the report by calling it's finish method.
+
+ Returns:
+ a :class:twisted.internet.defer.DeferredList that will fire when
+ all the reports have been closed.
+
+ """
dl = []
for reporter in self.reporters:
d = defer.maybeDeferred(reporter.finish)
diff --git a/ooni/tasks.py b/ooni/tasks.py
index f2a9bae..28aaca4 100644
--- a/ooni/tasks.py
+++ b/ooni/tasks.py
@@ -5,7 +5,7 @@ from twisted.internet import defer, reactor
class BaseTask(object):
_timer = None
- def __init__(self, mediator=None):
+ def __init__(self):
"""
If you want to schedule a task multiple times, remember to create fresh
instances of it.
@@ -19,10 +19,8 @@ class BaseTask(object):
# This is a deferred that gets called when a test has reached it's
# final status, this means: all retries have been attempted or the test
# has successfully executed.
+ # Such deferred will be called on completion by the TaskManager.
self.done = defer.Deferred()
- if mediator:
- mediator.created()
- self.done.addCallback(mediator.taskDone)
def _failed(self, failure):
self.failures += 1
@@ -97,8 +95,7 @@ class TaskWithTimeout(BaseTask):
pass
class Measurement(TaskWithTimeout):
- def __init__(self, test_class, test_method, test_input, net_test,
- mediator):
+ def __init__(self, test_class, test_method, test_input):
"""
test_class:
is the class, subclass of NetTestCase, of the test to be run
@@ -121,9 +118,7 @@ class Measurement(TaskWithTimeout):
self.test_instance.setUp()
self.test = getattr(self.test_instance, test_method)
- self.netTest = net_test
-
- TaskWithTimeout.__init__(self, mediator)
+ TaskWithTimeout.__init__(self)
def succeeded(self, result):
return self.netTest.succeeded(self)
@@ -139,60 +134,12 @@ class Measurement(TaskWithTimeout):
return d
class ReportEntry(TaskWithTimeout):
- def __init__(self, reporter, measurement, task_mediator):
+ def __init__(self, reporter, measurement):
self.reporter = reporter
self.measurement = measurement
- TaskWithTimeout.__init__(self, task_mediator)
+ TaskWithTimeout.__init__(self)
def run(self):
return self.reporter.writeReportEntry(self.measurement)
-
-class TaskMediator(object):
- def __init__(self, allTasksDone):
- """
- This implements a Mediator/Observer pattern to keep track of when Tasks
- that are logically linked together have all reached a final done stage.
-
- Args:
- allTasksDone is a deferred that will get fired once all the tasks
- have been completed.
- """
- self.doneTasks = 0
- self.tasks = 0
-
- self.completedScheduling = False
- self.allTasksDone = allTasksDone
-
- def created(self):
- self.tasks += 1
-
- def checkAllTasksDone(self):
- if self.completedScheduling and \
- self.doneTasks == self.tasks:
- self.allTasksDone.callback(self.doneTasks)
-
- def taskDone(self, result):
- """
- This is called every time a task has finished running.
- """
- self.doneTasks += 1
- self.checkAllTasksDone()
-
- def allTasksScheduled(self):
- """
- This should be called once all the tasks that need to run have been
- scheduled.
-
- XXX this is ghetto.
- The reason for which we are calling allTasksDone inside of the
- allTasksScheduled method is called after all tasks are done, then we
- will run into a race condition. The race is that we don't end up
- checking that all the tasks are complete because no task is to be
- scheduled.
- """
- self.completedScheduling = True
- self.checkAllTasksDone()
-
-
diff --git a/tests/test_nettest.py b/tests/test_nettest.py
index 3c7bdf6..a029fae 100644
--- a/tests/test_nettest.py
+++ b/tests/test_nettest.py
@@ -10,6 +10,8 @@ from ooni.nettest import FailureToLoadNetTest
from ooni.tasks import BaseTask
from ooni.utils import NotRootError
+from ooni.director import Director
+
from ooni.managers import TaskManager
from tests.mocks import MockMeasurement, MockMeasurementFailOnce
@@ -223,6 +225,7 @@ class TestNetTest(unittest.TestCase):
net_test = NetTest(StringIO(net_test_string_with_file),
dummyOptionsWithFile, MockReporter())
net_test.measurementManager = MockMeasurementManager()
+ net_test.director = Director()
d = net_test.start()
@d.addCallback
1
0
commit 8a889335fea991c69afbe001e4584615b484eafb
Author: Arturo Filastò <art(a)fuffa.org>
Date: Wed Jan 16 21:00:12 2013 +0100
Start the reactor in the Director
---
ooni/oonicli.py | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/ooni/oonicli.py b/ooni/oonicli.py
index 1959de8..ac1b080 100644
--- a/ooni/oonicli.py
+++ b/ooni/oonicli.py
@@ -190,6 +190,8 @@ def runWithDirector():
log.err('Missing required option: "%s"' % option_name)
print options.getUsage()
+ reactor.run()
+
def run():
"""
Parses command line arguments of test.
1
0

[ooni-probe/master] Add a shutdown hook for when everything is done
by isis@torproject.org 10 Mar '13
by isis@torproject.org 10 Mar '13
10 Mar '13
commit 96b6c644955114f17d15da924ad39a5b7c3583d7
Author: Arturo Filastò <art(a)fuffa.org>
Date: Wed Jan 16 21:02:55 2013 +0100
Add a shutdown hook for when everything is done
---
ooni/oonicli.py | 13 ++++++++++---
1 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/ooni/oonicli.py b/ooni/oonicli.py
index ac1b080..71de7e1 100644
--- a/ooni/oonicli.py
+++ b/ooni/oonicli.py
@@ -159,6 +159,13 @@ def parseOptions():
return dict(cmd_line_options)
+def shutdown(result):
+ """
+ This will get called once all the operations that need to be done in the
+ current oonicli session have been completed.
+ """
+ reator.stop()
+
def runWithDirector():
"""
Instance the director, parse command line options and start an ooniprobe
@@ -185,13 +192,13 @@ def runWithDirector():
director = Director(reporters)
try:
- director.startNetTest(net_test_loader, net_test_options)
+ d = director.startNetTest(net_test_loader, net_test_options)
+ d.addBoth(shutdown)
+ reactor.run()
except MissingRequiredOption, option_name:
log.err('Missing required option: "%s"' % option_name)
print options.getUsage()
- reactor.run()
-
def run():
"""
Parses command line arguments of test.
1
0
commit 8e52a8854892ee53585fc75f9d8fad2dc04bbe24
Author: Arturo Filastò <art(a)fuffa.org>
Date: Wed Jan 16 21:07:49 2013 +0100
Remove now unused and dead code
We no longer need inputunit. nettesttask was never needed
---
ooni/inputunit.py | 85 ------------------
ooni/nettesttask.py | 118 -------------------------
ooni/runner.py | 242 ---------------------------------------------------
3 files changed, 0 insertions(+), 445 deletions(-)
diff --git a/ooni/inputunit.py b/ooni/inputunit.py
deleted file mode 100644
index fb85d9e..0000000
--- a/ooni/inputunit.py
+++ /dev/null
@@ -1,85 +0,0 @@
-#-*- coding: utf-8 -*-
-#
-# inputunit.py
-# -------------
-# IN here we have functions related to the creation of input
-# units. Input units are how the inputs to be fed to tests are
-# split up into.
-#
-# :authors: Arturo Filastò
-# :license: see included LICENSE file
-from math import ceil
-
-class InputUnitFactory(object):
- """
- This is a factory that takes the size of input units to be generated a set
- of units that is a python iterable item and outputs InputUnit objects
- containing inputUnitSize elements.
-
- This object is a python iterable, this means that it does not need to keep
- all the elements in memory to be able to produce InputUnits.
- """
- inputUnitSize = 10
- length = None
- def __init__(self, inputs=[]):
- """
- Args:
- inputs (iterable): inputs *must* be an iterable.
- """
- self._inputs = iter(inputs)
- self.inputs = iter(inputs)
- self._ended = False
-
- def __iter__(self):
- return self
-
- def __len__(self):
- """
- Returns the number of input units in the input unit factory.
- """
- if not self.length:
- self.length = ceil(float(sum(1 for _ in self._inputs))/self.inputUnitSize)
- return self.length
-
- def next(self):
- input_unit_elements = []
-
- if self._ended:
- raise StopIteration
-
- for i in xrange(self.inputUnitSize):
- try:
- input_unit_elements.append(self.inputs.next())
- except StopIteration:
- self._ended = True
- break
-
- if not input_unit_elements:
- raise StopIteration
-
- return InputUnit(input_unit_elements)
-
-class InputUnit(object):
- """
- This is a python iterable object that contains the input elements to be
- passed onto a TestCase.
- """
- def __init__(self, inputs=[]):
- self._inputs = iter(inputs)
-
- def __str__(self):
- return "<%s inputs=%s>" % (self.__class__, self._inputs)
-
- def __add__(self, inputs):
- for i in inputs:
- self._inputs.append(i)
-
- def __iter__(self):
- return self
-
- def next(self):
- return self._inputs.next()
-
- def append(self, input):
- self._inputs.append(input)
-
diff --git a/ooni/nettesttask.py b/ooni/nettesttask.py
deleted file mode 100644
index 48c5108..0000000
--- a/ooni/nettesttask.py
+++ /dev/null
@@ -1,118 +0,0 @@
-from twisted.internet.task import CooperativeTask
-from twisted.internet import defer
-from ooni.reporter import OONIBReporter, YAMLReporter, OONIBReportError
-from ooni.utils import log
-import time
-from twisted.internet.task import cooperate
-
-class NetTestTask(CooperativeTask):
- """
- The object produced by a NetTestTaskFactory.
-
- A NetTestTask wraps a test_ callable with its options and input unit.
-
- """
- def __init__(self, test_case, test_input, oonib_reporter=None, yaml_reporter=None):
- test_class, test_method = test_case
- #log.debug("Running %s with %s..." % (test_method, test_input))
- self.oonib_reporter = oonib_reporter
- self.oonib_reporter = yaml_reporter
- self.test_instance = test_class()
- self.test_instance.input = test_input
- self.test_instance.report = {}
- self.test_instance._start_time = time.time()
- self.test_instance._setUp()
- self.test_instance.setUp()
- self.test = getattr(self.test_instance, test_method)
-
- # XXX: override CoordinatedTask methods
- def start(self): #???
- d = defer.maybeDeferred(self.test)
- d.addCallback(self.test_done)
- d.addErrback(self.test_error)
- return d
-
- def write_report(self):
- if not self.oonib_reporter:
- return self.yaml_reporter.testDone(self.test_instance, str(self.test))
- d1 = self.oonib_reporter.testDone(self.test_instance, str(self.test))
- d2 = self.yaml_reporter.testDone(self.test_instance, str(self.test))
- dl = defer.DeferredList([d1, d2])
- @dl.addErrback
- def reportingFailed(failure):
- log.err("Error in reporting %s" % self.test)
- log.exception(failure)
- return dl
-
- def test_done(self, result):
- log.msg("Finished running %s" % self.test)
- log.debug("Deferred callback result: %s" % result)
- return self.write_report()
-
- def test_error(self, failure):
- log.err("Error in running %s" % self.test)
- log.exception(failure)
- return self.write_report()
-
- #XXX: does not implement tests_done!
-
-class NetTestTaskFactory(object):
- def __init__(self, test_cases, input_unit_list):
- self.input_unit_list = input_unit_list
- self.inputs = self.generate_inputs()
- self.test_cases = test_cases
-
- def __iter__(self):
- return self
-
- def next(self):
- return self.inputs.next()
- # XXX: raise exception or fire callback when inputs are exhausted
-
- def generate_inputs(self):
- for input_unit in self.input_unit_list:
- for test_case in self.test_cases:
- yield NetTestTask(test_case, input_unit)
-
-(a)defer.inlineCallbacks
-def runTestCases(test_cases, options, cmd_line_options):
-
- log.debug("Running %s" % test_cases)
- log.debug("Options %s" % options)
- log.debug("cmd_line_options %s" % dict(cmd_line_options))
-
- test_inputs = options['inputs']
-
- oonib_reporter = OONIBReporter(cmd_line_options)
- yaml_reporter = YAMLReporter(cmd_line_options)
-
- if cmd_line_options['collector']:
- log.msg("Using remote collector, please be patient while we create the report.")
- try:
- yield oonib_reporter.createReport(options)
- except OONIBReportError:
- log.err("Error in creating new report")
- log.msg("We will only create reports to a file")
- oonib_reporter = None
- else:
- oonib_reporter = None
-
- yield yaml_reporter.createReport(options)
- log.msg("Reporting to file %s" % yaml_reporter._stream.name)
-
- nettest_task_factory = NetTestTaskFactory(test_cases, test_inputs)
-
- #XXX: resume is not supported!
- try:
- #XXX: override the default cooperator, set up own scheduler
- #XXX: add callback when tasks are all exhausted
- for nettest_task in nettest_task_factory.generate_inputs():
- nettest_task.yaml_reporter = yaml_reporter
- nettest_task.oonib_reporter = oonib_reporter
- log.debug("Running %s with input unit %s" % (nettest_task,
- nettest_task.test_instance.input))
- # feed the cooperator
- nettest_task.start()
-
- except Exception:
- log.exception("Problem in running test")
diff --git a/ooni/runner.py b/ooni/runner.py
index a602234..21c3e0e 100644
--- a/ooni/runner.py
+++ b/ooni/runner.py
@@ -27,248 +27,6 @@ from ooni.utils import log, checkForRoot, pushFilenameStack
from ooni.utils import NotRootError, Storage
from ooni.utils.net import randomFreePort
-def processTest(obj, cmd_line_options):
- """
- Process the parameters and :class:`twisted.python.usage.Options` of a
- :class:`ooni.nettest.Nettest`.
-
- :param obj:
- An uninstantiated old test, which should be a subclass of
- :class:`ooni.plugoo.tests.OONITest`.
-
- :param cmd_line_options:
- A configured and instantiated :class:`twisted.python.usage.Options`
- class.
-
- """
- if not hasattr(obj.usageOptions, 'optParameters'):
- obj.usageOptions.optParameters = []
-
- if obj.inputFile:
- obj.usageOptions.optParameters.append(obj.inputFile)
-
- if obj.baseParameters:
- for parameter in obj.baseParameters:
- obj.usageOptions.optParameters.append(parameter)
-
- if obj.baseFlags:
- if not hasattr(obj.usageOptions, 'optFlags'):
- obj.usageOptions.optFlags = []
- for flag in obj.baseFlags:
- obj.usageOptions.optFlags.append(flag)
-
- options = obj.usageOptions()
-
- options.parseOptions(cmd_line_options['subargs'])
- obj.localOptions = options
-
- if obj.inputFile:
- obj.inputFilename = options[obj.inputFile[0]]
-
- try:
- log.debug("processing options")
- tmp_test_case_object = obj()
- tmp_test_case_object._checkRequiredOptions()
-
- except usage.UsageError, e:
- test_name = tmp_test_case_object.name
- log.err("There was an error in running %s!" % test_name)
- log.err("%s" % e)
- options.opt_help()
- raise usage.UsageError("Error in parsing command line args for %s" % test_name)
-
- if obj.requiresRoot:
- try:
- checkForRoot()
- except NotRootError:
- log.err("%s requires root to run" % obj.name)
- sys.exit(1)
-
- return obj
-
-def isTestCase(obj):
- try:
- return issubclass(obj, NetTestCase)
- except TypeError:
- return False
-
-def findTestClassesFromFile(cmd_line_options):
- """
- Takes as input the command line config parameters and returns the test
- case classes.
-
- :param filename:
- the absolute path to the file containing the ooniprobe test classes
-
- :return:
- A list of class objects found in a file or module given on the
- commandline.
- """
- filename = cmd_line_options['test']
- classes = []
- module = filenameToModule(filename)
- for name, val in inspect.getmembers(module):
- if isTestCase(val):
- classes.append(processTest(val, cmd_line_options))
- return classes
-
-def makeTestCases(klass, tests, method_prefix):
- """
- Takes a class some tests and returns the test cases. method_prefix is how
- the test case functions should be prefixed with.
- """
- cases = []
- for test in tests:
- cases.append((klass, method_prefix+test))
- return cases
-
-class NoTestCasesFound(Exception):
- pass
-
-def loadTestsAndOptions(classes, cmd_line_options):
- """
- Takes a list of test classes and returns their testcases and options.
- """
- method_prefix = 'test'
- options = None
- test_cases = []
-
- for klass in classes:
- tests = reflect.prefixedMethodNames(klass, method_prefix)
- if tests:
- test_cases = makeTestCases(klass, tests, method_prefix)
-
- test_klass = klass()
- options = test_klass._processOptions()
-
- if not test_cases:
- raise NoTestCasesFound
-
- return test_cases, options
-
-def runTestCasesWithInput(test_cases, test_input, yaml_reporter,
- oonib_reporter=None):
- """
- Runs in parallel all the test methods that are inside of the specified test case.
- Reporting happens every time a Test Method has concluded running.
- Once all the test methods have been called we check to see if the
- postProcessing class method returns something. If it does return something
- we will write this as another entry inside of the report called post_processing.
-
- Args:
-
- test_cases (list): A list of tuples containing the test_class (a
- class) and the test_method (a string)
-
- test_input (instance): Any instance that will be passed as input to
- the test.
-
- yaml_reporter: An instance of :class:ooni.reporter.YAMLReporter
-
- oonib_reporter: An instance of :class:ooni.reporter.OONIBReporter. If
- this is set to none then we will only report to the YAML reporter.
-
- """
-
- # This is used to store a copy of all the test reports
- tests_report = {}
-
- def write_report(test_instance, test_name):
- if not oonib_reporter:
- return yaml_reporter.testDone(test_instance, test_name)
- d1 = oonib_reporter.testDone(test_instance, test_name)
- d2 = yaml_reporter.testDone(test_instance, test_name)
- dl = defer.DeferredList([d1, d2])
- @dl.addErrback
- def reportingFailed(failure):
- log.err("Error in reporting %s" % test_name)
- log.exception(failure)
- return dl
-
- def test_done(result, test_instance, test_name):
- log.msg("Finished running %s" % test_name)
- log.debug("Deferred callback result: %s" % result)
- tests_report[test_name] = dict(test_instance.report)
- return write_report(test_instance, test_name)
-
- def test_error(failure, test_instance, test_name):
- log.err("Error in running %s" % test_name)
- log.exception(failure)
- return write_report(test_instance, test_name)
-
- def tests_done(result, test_class):
- test_instance = test_class()
- test_instance.report = {}
- test_instance.input = None
- test_instance._start_time = time.time()
- post = getattr(test_instance, 'postProcessor')
- try:
- post_processing = post(tests_report)
- if not oonib_reporter:
- return yaml_reporter.testDone(test_instance, 'summary')
- d1 = oonib_reporter.testDone(test_instance, 'summary')
- d2 = yaml_reporter.testDone(test_instance, 'summary')
- return defer.DeferredList([d1, d2])
- except NoPostProcessor:
- log.debug("No post processor configured")
- return
-
- dl = []
- for test_case in test_cases:
- log.debug("Processing %s" % test_case[1])
- test_class = test_case[0]
- test_method = test_case[1]
-
- log.debug("Running %s with %s..." % (test_method, test_input))
-
- test_instance = test_class()
- test_instance.input = test_input
- test_instance.report = {}
- # use this to keep track of the test runtime
- test_instance._start_time = time.time()
- # call setups on the test
- test_instance._setUp()
- test_instance.setUp()
- test = getattr(test_instance, test_method)
-
- d = defer.maybeDeferred(test)
- d.addCallback(test_done, test_instance, test_method)
- d.addErrback(test_error, test_instance, test_method)
- dl.append(d)
-
- test_methods_d = defer.DeferredList(dl)
- test_methods_d.addCallback(tests_done, test_cases[0][0])
- @test_methods_d.addErrback
- def deferredListFailed(failure):
- log.err("Error Test Method Deferred List")
- log.exception(failure)
-
- return test_methods_d
-
-def runTestCasesWithInputUnit(test_cases, input_unit, yaml_reporter,
- oonib_reporter):
- """
- Runs the Test Cases that are given as input parallely.
- A Test Case is a subclass of ooni.nettest.NetTestCase and a list of
- methods.
-
- The deferred list will fire once all the test methods have been
- run once per item in the input unit.
-
- test_cases: A list of tuples containing the test class and the test method as a string.
-
- input_unit: A generator that yields an input per iteration
-
- """
- log.debug("Running test cases with input unit")
- dl = []
- for test_input in input_unit:
- log.debug("Running test with this input %s" % test_input)
- d = runTestCasesWithInput(test_cases,
- test_input, yaml_reporter, oonib_reporter)
- dl.append(d)
- return defer.DeferredList(dl)
-
class InvalidResumeFile(Exception):
pass
1
0
commit a7e2f269ada33f88e9f4bd5309efc29f04d86702
Author: Arturo Filastò <art(a)fuffa.org>
Date: Wed Jan 16 21:07:49 2013 +0100
Remove now unused and dead code
We no longer need inputunit. nettesttask was never needed
---
ooni/runner.py | 9 ++++-----
1 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/ooni/runner.py b/ooni/runner.py
index bf51068..9b884a9 100644
--- a/ooni/runner.py
+++ b/ooni/runner.py
@@ -8,13 +8,12 @@ from twisted.internet import defer
from twisted.internet import reactor
from ooni import config
-
from ooni.reporter import OONIBReporter, YAMLReporter, OONIBReportError
-
from ooni.inputunit import InputUnitFactory
-
-from ooni.utils import log
-from ooni.utils import Storage
+from ooni.nettest import NetTestCase, NoPostProcessor
+from ooni.utils import log, checkForRoot, pushFilenameStack
+from ooni.utils import NotRootError, Storage
+from ooni.utils.net import randomFreePort
class InvalidResumeFile(Exception):
pass
1
0

[ooni-probe/master] Remove unused imports inside of ooni, oonicli and runner
by isis@torproject.org 10 Mar '13
by isis@torproject.org 10 Mar '13
10 Mar '13
commit c5634f7f3f4b5cabd67af4a04045250ad13a6416
Author: Arturo Filastò <art(a)fuffa.org>
Date: Wed Jan 16 21:23:05 2013 +0100
Remove unused imports inside of ooni, oonicli and runner
---
ooni/__init__.py | 2 --
ooni/oonicli.py | 12 ++----------
ooni/runner.py | 10 ++--------
3 files changed, 4 insertions(+), 20 deletions(-)
diff --git a/ooni/__init__.py b/ooni/__init__.py
index 5261982..5405c17 100644
--- a/ooni/__init__.py
+++ b/ooni/__init__.py
@@ -1,12 +1,10 @@
# -*- encoding: utf-8 -*-
from . import config
-from . import inputunit
from . import kit
from . import nettest
from . import oonicli
from . import reporter
-from . import runner
from . import templates
from . import utils
diff --git a/ooni/oonicli.py b/ooni/oonicli.py
index 71de7e1..d11a9f7 100644
--- a/ooni/oonicli.py
+++ b/ooni/oonicli.py
@@ -2,25 +2,19 @@
import sys
import os
-import random
import time
import yaml
from twisted.internet import defer, reactor, task
-from twisted.application import app
-from twisted.python import usage, failure
+from twisted.python import usage
from twisted.python.util import spewer
-from ooni import nettest, runner, reporter, config
+from ooni import config
from ooni.director import Director
from ooni.reporter import YAMLReporter, OONIBReporter
-from ooni.inputunit import InputUnitFactory
-
from ooni.nettest import NetTestLoader, MissingRequiredOption
-from ooni.utils import net
-from ooni.utils import checkForRoot, NotRootError
from ooni.utils import log
class Options(usage.Options):
@@ -242,5 +236,3 @@ def run():
d.addErrback(errorRunningTests)
reactor.run()
-
-
diff --git a/ooni/runner.py b/ooni/runner.py
index 21c3e0e..d56dc26 100644
--- a/ooni/runner.py
+++ b/ooni/runner.py
@@ -1,19 +1,14 @@
import os
import sys
import time
-import inspect
-import traceback
-import itertools
import random
import yaml
-from twisted.python import reflect, usage
from twisted.internet import defer
-from twisted.trial.runner import filenameToModule
-from twisted.internet import reactor, threads
+from twisted.internet import reactor
-from txtorcon import TorProtocolFactory, TorConfig
+from txtorcon import TorConfig
from txtorcon import TorState, launch_tor
from ooni import config
@@ -21,7 +16,6 @@ from ooni import config
from ooni.reporter import OONIBReporter, YAMLReporter, OONIBReportError
from ooni.inputunit import InputUnitFactory
-from ooni.nettest import NetTestCase, NoPostProcessor
from ooni.utils import log, checkForRoot, pushFilenameStack
from ooni.utils import NotRootError, Storage
1
0