commit c4c91d393ba3956b84d46521e2f58d30584b3ea0
Author: Arturo Filastò <hellais(a)torproject.org>
Date: Thu May 31 03:31:05 2012 +0200
Get the new OONI architecture working properly.
(kudos to the organizers of FreeBird :) )
---
.gitignore | 3 +-
ooni/oonicli.py | 70 ++++++++----------------
ooni/ooniprobe.log | 3 +-
ooni/plugins/.bridget.py.swp | Bin 12288 -> 0 bytes
ooni/plugins/.dnstamper.py.swp | Bin 12288 -> 0 bytes
ooni/plugins/blocking.py | 41 ++++++++++++++
ooni/plugins/bridget.py | 36 ------------
ooni/plugins/dnstamper.py | 22 --------
ooni/plugins/dropin.cache | 90 ++++++++++++++++++++++++++++++-
ooni/plugins/skel.py | 9 +++-
ooni/plugoo/tests.py | 118 ++++++++++++++--------------------------
ooni/plugoo/work.py | 64 +++++++++++++++-------
ooni/skel.py | 10 ----
oonib/oonibackend.py | 8 ++--
14 files changed, 255 insertions(+), 219 deletions(-)
diff --git a/.gitignore b/.gitignore
index 6433d66..8ed47e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,5 @@ proxy-lists/ip-cc.txt
proxy-lists/ips.txt
proxy-lists/italy-dns-ips.txt
proxy-lists/italy-http-ips.txt
-private/*
\ No newline at end of file
+private/*
+ooni/plugins/dropin.cache
diff --git a/ooni/oonicli.py b/ooni/oonicli.py
index d290e68..a0272fb 100755
--- a/ooni/oonicli.py
+++ b/ooni/oonicli.py
@@ -9,7 +9,6 @@
#
import sys
-from plugoo import tests, work, assets
from twisted.python import usage
from twisted.plugin import getPlugins
@@ -19,8 +18,10 @@ from zope.interface.exceptions import BrokenImplementation
from zope.interface.exceptions import BrokenMethodImplementation
from zope.interface.verify import verifyObject
from pprint import pprint
-from logo import getlogo
-import plugins
+
+from ooni.plugoo import tests, work, assets
+from ooni.logo import getlogo
+from ooni import plugins
__version__ = "0.0.1-prealpha"
@@ -46,33 +47,20 @@ def retrieve_plugoo():
plugoo = retrieve_plugoo()
-class StupidAsset(object):
- def __init__(self):
- self.idx = 0
-
- def __iter__(self):
- return self
-
- def next(self):
- if self.idx > 30:
- raise StopIteration
- self.idx += 1
- return self.idx
-
-
def runTest(test, options, global_options):
- asset = None
- if options['asset']:
- print options['asset']
- asset = assets.Asset(options['asset'])
- print asset
- wgen = work.WorkGenerator(asset, plugoo[test].__class__,
- dict(options), start=options['resume'])
+ parallelism = int(global_options['parallelism'])
+ worker = work.Worker(parallelism)
+ test = plugoo[test].__class__
+
+ #if options['asset']:
+ # print options['asset']
+ # asset = assets.Asset(options['asset'])
+ # print asset
- if global_options['parallelism']:
- wgen.size = int(global_options['parallelism'])
- worker = work.Worker(wgen.size)
+ wgen = work.WorkGenerator(test(options, global_options),
+ dict(options),
+ start=options['resume'])
for x in wgen:
worker.push(x)
@@ -86,16 +74,17 @@ class Options(usage.Options):
subCommands.append([test, None, plugoo[test].options, "Run the %s test" % test])
optFlags = [
- ['local', 'l', "If the test should be run locally (without having oonid running)"],
- ['status', 'x', 'Show current state'],
- ['restart', 'r', 'Restart OONI']
+ #['remote', 'r', "If the test should be run remotely (not supported)"],
+ #['status', 'x', 'Show current state'],
+ #['restart', 'r', 'Restart OONI']
]
optParameters = [
['parallelism', 'n', 10, "Specify the number of parallel tests to run"],
- ['target-node', 't', 'localhost:31415', 'Select target node'],
- ['ooninet', 'o', 'localhost:4242', "Select OONI-net address for reporting"],
- ['password', 'p', 'opennetwork', "Specify the password for authentication"],
+ #['target-node', 't', 'localhost:31415', 'Select target node'],
+ ['output', 'o', 'report.log', "Specify output report file"],
+ ['log', 'l', 'oonicli.log', "Specify output log file"],
+ #['password', 'p', 'opennetwork', "Specify the password for authentication"],
]
def opt_version(self):
@@ -116,24 +105,11 @@ class Options(usage.Options):
config = Options()
config.parseOptions()
-if config['status']:
- print "oonid is not running."
- sys.exit(0)
-
-if config['restart']:
- print "Restarting oonid."
- sys.exit(0)
-
if not config.subCommand:
print "Error! No Test Specified."
config.opt_help()
sys.exit(1)
-if config['local']:
- runTest(config.subCommand, config.subOptions, config)
+runTest(config.subCommand, config.subOptions, config)
-else:
- print "This feature is currently not supported. :("
- print "Use -l to run the test locally."
- sys.exit(0)
diff --git a/ooni/ooniprobe.log b/ooni/ooniprobe.log
index d3914dc..700e820 100644
--- a/ooni/ooniprobe.log
+++ b/ooni/ooniprobe.log
@@ -1,2 +1 @@
-2012-05-29 01:32:22,811 ooniprobe INFO Started ooni-probe
-2012-05-29 01:32:22,972 ooniprobe WARNING Soft fail 'module' object has no attribute '__plugoo__'
+2012-05-31 02:27:28,010 ooniprobe INFO Started ooni-probe
diff --git a/ooni/plugins/.bridget.py.swp b/ooni/plugins/.bridget.py.swp
deleted file mode 100644
index 226d826..0000000
Binary files a/ooni/plugins/.bridget.py.swp and /dev/null differ
diff --git a/ooni/plugins/.dnstamper.py.swp b/ooni/plugins/.dnstamper.py.swp
deleted file mode 100644
index 9acb1c9..0000000
Binary files a/ooni/plugins/.dnstamper.py.swp and /dev/null differ
diff --git a/ooni/plugins/blocking.py b/ooni/plugins/blocking.py
new file mode 100644
index 0000000..bbcb1a2
--- /dev/null
+++ b/ooni/plugins/blocking.py
@@ -0,0 +1,41 @@
+from zope.interface import implements
+from twisted.python import usage
+from twisted.plugin import IPlugin
+
+from ooni.plugoo.assets import Asset
+from ooni.plugoo.tests import ITest, TwistedTest
+
+class BlockingArgs(usage.Options):
+ optParameters = [['asset', 'a', None, 'Asset file'],
+ ['resume', 'r', 0, 'Resume at this index'],
+ ['other', 'o', None, 'Other arguments']]
+
+class BlockingTest(TwistedTest):
+ implements(IPlugin, ITest)
+
+ shortName = "blocking"
+ description = "Blocking plugin"
+ requirements = None
+ options = BlockingArgs
+ # Tells this to be blocking.
+ blocking = True
+
+ def control(self, experiment_result, args):
+ print "Experiment Result:", experiment_result
+ print "Args", args
+ return experiment_result
+
+ def experiment(self, args):
+ import urllib
+ req = urllib.urlopen(args['asset'])
+ return {'page': req.readlines()}
+
+ def load_assets(self):
+ if self.local_options:
+ return {'asset': Asset(self.local_options['asset'])}
+ else:
+ return {}
+
+# We need to instantiate it otherwise getPlugins does not detect it
+# XXX Find a way to load plugins without instantiating them.
+blocking = BlockingTest(None, None, None)
diff --git a/ooni/plugins/bridget.py b/ooni/plugins/bridget.py
deleted file mode 100644
index 7271468..0000000
--- a/ooni/plugins/bridget.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from zope.interface import implements
-from twisted.python import usage
-from twisted.plugin import IPlugin
-from plugoo.tests import ITest, TwistedTest
-from twisted.internet import threads
-
-from tests.bridget import BridgeT as BridgeTlegacy
-from tests.bridget import BridgeTAsset as BridgeTAsset
-from ooniprobe import ooni
-
-o = ooni()
-
-class BridgetArgs(usage.Options):
- optParameters = [['asset', 'a', None, 'Asset file'],
- ['resume', 'r', 0, 'Resume at this index'],
- ['bridge', 'b', None, 'Specify a single bridge']]
-
-class BridgeT(TwistedTest):
- implements(IPlugin, ITest)
-
- shortName = "bridget"
- description = "Bridget plugin"
- requirements = None
- options = BridgetArgs
-
- def experiment(self):
- bridget = BridgeTlegacy(o)
- o.logger.info("Starting bridget test")
- print "ASSET:%s " % self.asset
- d = threads.deferToThread(bridget.connect, self.asset)
- d.addCallback(self.d_experiment.callback, None)
- return d
-
-# We need to instantiate it otherwise getPlugins does not detect it
-# XXX Find a way to load plugins without instantiating them.
-bridget = BridgeT(None, None)
diff --git a/ooni/plugins/dnstamper.py b/ooni/plugins/dnstamper.py
deleted file mode 100644
index b8b7c7f..0000000
--- a/ooni/plugins/dnstamper.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from zope.interface import implements
-from twisted.python import usage
-from twisted.plugin import IPlugin
-from plugoo.tests import ITest, TwistedTest
-
-class SkelArgs(usage.Options):
- optParameters = [['hosts', 'f', None, 'Hostname asset file'],
- ['dnsservers', 'd', None, 'DNS server asset file'],
- ['resume', 'r', 0, 'Resume at this index'],
- ['other', 'o', None, 'Other arguments']]
-
-class SkelTest(TwistedTest):
- implements(IPlugin, ITest)
-
- shortName = "skeleton"
- description = "Skeleton plugin"
- requirements = None
- options = SkelArgs
-
-# We need to instantiate it otherwise getPlugins does not detect it
-# XXX Find a way to load plugins without instantiating them.
-skel = SkelTest(None, None)
diff --git a/ooni/plugins/dropin.cache b/ooni/plugins/dropin.cache
index 9258b18..bd26c09 100755
--- a/ooni/plugins/dropin.cache
+++ b/ooni/plugins/dropin.cache
@@ -53,7 +53,7 @@ g5
NtRp24
(dp25
g8
-S'plugins.bridget'
+S'ooni.plugins.bridget'
p26
sg10
Nsg11
@@ -73,4 +73,92 @@ sg21
S'bridget'
p31
sg10
+NsbasbsS'blocking'
+p32
+g3
+(g4
+g5
+NtRp33
+(dp34
+g8
+S'ooni.plugins.blocking'
+p35
+sg10
+Nsg11
+(lp36
+g3
+(g13
+g5
+NtRp37
+(dp38
+g16
+(lp39
+g18
+acooni.plugoo.tests
+ITest
+p40
+asg20
+g33
+sg21
+S'blocking'
+p41
+sg10
+NsbasbsS'httphost'
+p42
+g3
+(g4
+g5
+NtRp43
+(dp44
+g8
+S'ooni.plugins.httphost'
+p45
+sg10
+S'\n HTTP Host based filtering\n *************************\n\n This test detect HTTP Host field\n based filtering.\n'
+p46
+sg11
+(lp47
+g3
+(g13
+g5
+NtRp48
+(dp49
+g16
+(lp50
+g18
+ag40
+asg20
+g43
+sg21
+S'httphost'
+p51
+sg10
+NsbasbsS'tcpscan'
+p52
+g3
+(g4
+g5
+NtRp53
+(dp54
+g8
+S'plugins.tcpscan'
+p55
+sg10
+Nsg11
+(lp56
+g3
+(g13
+g5
+NtRp57
+(dp58
+g16
+(lp59
+g18
+ag40
+asg20
+g53
+sg21
+S'tcpscan'
+p60
+sg10
Nsbasbs.
\ No newline at end of file
diff --git a/ooni/plugins/skel.py b/ooni/plugins/skel.py
index 93bc1bb..00e4311 100644
--- a/ooni/plugins/skel.py
+++ b/ooni/plugins/skel.py
@@ -15,7 +15,14 @@ class SkelTest(TwistedTest):
description = "Skeleton plugin"
requirements = None
options = SkelArgs
+ blocking = False
+
+ def load_assets(self):
+ if self.local_options:
+ return {'asset': open(self.local_options['asset'])}
+ else:
+ return {}
# We need to instantiate it otherwise getPlugins does not detect it
# XXX Find a way to load plugins without instantiating them.
-skel = SkelTest(None, None)
+skel = SkelTest(None, None, None)
diff --git a/ooni/plugoo/tests.py b/ooni/plugoo/tests.py
index 09363a2..75d0765 100644
--- a/ooni/plugoo/tests.py
+++ b/ooni/plugoo/tests.py
@@ -6,9 +6,13 @@ from zope.interface import Interface, Attribute
import logging
import itertools
import gevent
-from twisted.internet import reactor, defer
+
+from twisted.internet import reactor, defer, threads
from twisted.python import failure
-from plugoo.reports import Report
+
+from ooni.plugoo import assets, work
+from ooni.plugoo.reports import Report
+
class Test:
"""
@@ -128,102 +132,64 @@ class ITest(Interface):
#node = Attribute("""This represents the node that will run the test""")
options = Attribute("""These are the arguments to be passed to the test for it's execution""")
- def startTest():
+ blocking = Attribute("""True or False, stating if the test should be run in a thread or not.""")
+
+ def startTest(asset):
"""
Launches the Test with the specified arguments on a node.
"""
-#class HTTPRequestTest(HTTPClient):
-class HTTPRequestTest(object):
- """
- This is an example of how I would like to be able to write a test.
-
- *BEWARE* this actually does not currently work, it's just an example of the
- kind of API that I am attempting to achieve to simplify the writing of
- tests.
+class TwistedTest(object):
+ blocking = False
- implements(ITest)
+ def __init__(self, local_options, global_options, ooninet=None):
+ self.local_options = local_options
+ self.global_options = global_options
+ self.assets = self.load_assets()
+ #self.ooninet = ooninet
- """
- def startTest():
- # The response object should also contain the request
+ def load_assets(self):
"""
- response = {'response': {'headers': ..., 'content': ...,
- 'runtime': ..., 'timestamp': ...},
- 'request': {'headers': ..., 'content', 'timestamp', ...}
- }
- response = self.http_request(address, headers)
- if response.headers['content'].matches("Some string"):
- self.censorship = True
- return response
- else:
- self.censorship = False
- return response
-
+ This method should be overriden by the test writer to provide the logic
+ for loading their assets.
"""
- pass
-
-class TwistedTest(object):
- def __init__(self, asset, arguments, ooninet=None):
- self.asset = asset
- self.arguments = arguments
- self.start_time = datetime.now()
- self._parse_arguments()
- #self.ooninet = ooninet
+ return {'asset': None}
def __repr__(self):
- return "<TwistedTest %s %s>" % (self.arguments, self.asset)
+ return "<TwistedTest %s %s %s>" % (self.options, self.global_options,
+ self.assets)
- def _parse_arguments(self):
- print self.arguments
- if self.arguments and 'test' in self.arguments:
- self.test = self.arguments['test']
-
- def finished(self, result):
+ def finished(self, control):
#self.ooninet.report(result)
- print "FINIHSED"
self.end_time = datetime.now()
+ result = {}
result['start_time'] = self.start_time
result['end_time'] = self.end_time
result['run_time'] = self.end_time - self.start_time
+ result['control'] = control
+ print "FINISHED", result
+ return result
- def _do_experiment(self):
- self.d = defer.maybeDeferred(self.experiment)
- self.d.addCallback(self.control)
+ def _do_experiment(self, args):
+ if self.blocking:
+ self.d = threads.deferToThread(self.experiment, args)
+ else:
+ self.d = defer.maybeDeferred(self.experiment, args)
+
+ self.d.addCallback(self.control, args)
self.d.addCallback(self.finished)
return self.d
- def control(self, exp):
+ def control(self, result, args):
print "Doing control..."
- self.d.callback(result)
+ return result
- def experiment(self):
+ def experiment(self, args):
print "Doing experiment"
- self.d_experiment.callback(None)
-
- def startTest(self):
- print "Starting test %s" % repr(self)
- return self._do_experiment()
-
-class TwistedTestFactory(object):
+ return {}
- test = None
-
- def __init__(self, assets, node,
- idx=0):
- """
- """
- self.assets = assets
- self.node = node
- self.idx = idx
- self.workunit = WorkUnitFactory(assets)
-
- def build_test(self):
- """
- Returns a TwistedTest instance
- """
- workunit = self.workunit.next()
- t = self.test(node, workunit)
- t.factory = self
- return t
+ def startTest(self, args):
+ self.start_time = datetime.now()
+ print "Starting test %s" % self.__class__
+ return self._do_experiment(args)
diff --git a/ooni/plugoo/work.py b/ooni/plugoo/work.py
index 7be4758..55bbd1d 100644
--- a/ooni/plugoo/work.py
+++ b/ooni/plugoo/work.py
@@ -10,15 +10,16 @@
:license: see LICENSE for more details.
"""
-from datetime import datetime
+import itertools
import yaml
+from datetime import datetime
from zope.interface import Interface, Attribute
from twisted.python import failure
from twisted.internet import reactor, defer
-from plugoo.nodes import LocalNode
+from ooni.plugoo.nodes import LocalNode
class Worker(object):
"""
@@ -34,9 +35,9 @@ class Worker(object):
self._running -= 1
if self._running < self.maxconcurrent and self._queued:
workunit, d = self._queued.pop(0)
- for work in workunit:
- self._running += 1
- actuald = work.startTest().addBoth(self._run)
+ asset, test, idx = workunit
+ self._running += 1
+ actuald = test.startTest(asset).addBoth(self._run)
if isinstance(r, failure.Failure):
r.trap()
@@ -44,14 +45,13 @@ class Worker(object):
print r['start_time']
print r['end_time']
print r['run_time']
- print repr(r)
return r
def push(self, workunit):
if self._running < self.maxconcurrent:
- for work in workunit:
- self._running += 1
- work.startTest().addBoth(self._run)
+ asset, test, idx = workunit
+ self._running += 1
+ test.startTest(asset).addBoth(self._run)
return
d = defer.Deferred()
self._queued.append((workunit, d))
@@ -59,6 +59,8 @@ class Worker(object):
class WorkUnit(object):
"""
+ XXX This is currently not implemented for KISS sake.
+
This is an object responsible for completing WorkUnits it will
return its result in a deferred.
@@ -77,21 +79,21 @@ class WorkUnit(object):
test = None
arguments = None
- def __init__(self, asset, test, idx, arguments=None):
+ def __init__(self, asset, assetNames, test, idx):
self.asset = asset
if not asset:
self.assetGenerator = iter([1])
else:
self.assetGenerator = iter(asset)
self.Test = test
- self.arguments = arguments
+ self.assetNames = assetNames
self.idx = idx
def __iter__(self):
return self
def __repr__(self):
- return "<WorkUnit %s %s %s>" % (self.arguments, self.Test, self.idx)
+ return "<WorkUnit %s %s %s>" % (self.assetNames, self.Test, self.idx)
def serialize(self):
"""
@@ -105,7 +107,7 @@ class WorkUnit(object):
"""
try:
asset = self.assetGenerator.next()
- ret = self.Test(asset, self.arguments)
+ ret = self.Test.set_asset(asset)
return ret
except StopIteration:
raise StopIteration
@@ -120,10 +122,12 @@ class WorkGenerator(object):
"""
size = 10
- def __init__(self, asset, test, arguments=None, start=None):
- self.assetGenerator = asset
+ def __init__(self, test, arguments=None, start=None):
self.Test = test
- self.arguments = arguments
+
+ self.assetGenerator = itertools.product(*self.Test.assets.values())
+ self.assetNames = self.Test.assets.keys()
+
self.idx = 0
self.end = False
if start:
@@ -139,12 +143,32 @@ class WorkGenerator(object):
self.idx += 1
def next(self):
+ if not self.assetGenerator:
+ self.end = True
+ return (self.assetNames, self.Test, self.idx)
+
+ try:
+ asset = self.assetGenerator.next()
+ ret = {}
+ for i, v in enumerate(asset):
+ ret[self.assetNames[i]] = v
+ except StopIteration:
+ raise StopIteration
+
+ self.idx += 1
+ print "IDX:%s" % self.idx
+ return (ret, self.Test, self.idx)
+
+ def p_next(self):
+ """
+ XXX This is not used for KISS sake.
+ """
if self.end:
raise StopIteration
if not self.assetGenerator:
self.end = True
- return WorkUnit(None, self.Test, self.idx, self.arguments)
+ return WorkUnit(None, self.assetNames, self.Test, self.idx)
# Plank asset
p_asset = []
@@ -152,11 +176,13 @@ class WorkGenerator(object):
try:
asset = self.assetGenerator.next()
p_asset.append(asset)
- print asset
+ print p_asset
except StopIteration:
+ if self.asset_num > 1:
+ pass
self.end = True
break
self.idx += 1
- return WorkUnit(p_asset, self.Test, self.idx, self.arguments)
+ return WorkUnit(p_asset, self.assetNames, self.Test, self.idx)
diff --git a/ooni/skel.py b/ooni/skel.py
deleted file mode 100644
index 8982106..0000000
--- a/ooni/skel.py
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: UTF-8
-"""
- XXX
- ***
-
- :copyright: (c) 2012 by Arturo Filastò
- :license: see LICENSE for more details.
-"""
-
diff --git a/oonib/oonibackend.py b/oonib/oonibackend.py
index 774a9c5..fc5bbdb 100644
--- a/oonib/oonibackend.py
+++ b/oonib/oonibackend.py
@@ -16,19 +16,19 @@ from twisted.web import resource, server, static
from twisted.web.microdom import escape
from twisted.names import dns
-from backend.httpbackend import HTTPBackend
-from backend.dnsbackend import ProxyDNSServer
+from oonib.httpbackend import HTTPBackend
+from oonib.dnsbackend import ProxyDNSServer
# This tells twisted to set the
server.version = "Apache"
application = service.Application('oonibackend')
serviceCollection = service.IServiceCollection(application)
-internet.TCPServer(8000, server.Site(HTTPBackend())).setServiceParent(serviceCollection)
+internet.TCPServer(2000, server.Site(HTTPBackend())).setServiceParent(serviceCollection)
# Start the DNS Server related services
TCPDNSServer = ProxyDNSServer()
internet.TCPServer(8002, TCPDNSServer).setServiceParent(serviceCollection)
UDPFactory = dns.DNSDatagramProtocol(TCPDNSServer)
-internet.UDPServer(5353, UDPFactory).setServiceParent(serviceCollection)
+internet.UDPServer(5354, UDPFactory).setServiceParent(serviceCollection)