[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [ooni-probe/master] Do a big cleanup
commit d68e5f4f8edffbcd7e9c9fc5ce22030472f401a1
Author: Arturo Filastò <art@xxxxxxxxxxxxxx>
Date: Mon Jul 9 11:54:37 2012 +0200
Do a big cleanup
* Better specify test writing interface
* Move example plugins to appropriate directory
* Implement RFC3339 timestamp, as spec
* Make sure all the defaults tests run and output something even without arguments
* Fix bug in worker
---
ooni/date.py | 17 ++++-
ooni/example_plugins/examplescapy.py | 49 ++++++++++++++
ooni/example_plugins/skel.py | 29 ++++++++
ooni/lib/Makefile | 8 ++-
ooni/ooniprobe.py | 1 -
ooni/plugins/blocking.py | 6 +-
ooni/plugins/chinatrigger.py | 2 +-
ooni/plugins/examplescapy.py | 49 --------------
ooni/plugins/httphost.py | 11 ++--
ooni/plugins/httpt.py | 5 +-
ooni/plugins/skel.py | 29 --------
ooni/plugins/tcpconnect.py | 9 ++-
ooni/plugoo/interface.py | 31 ++++++++-
ooni/plugoo/reports.py | 2 +-
ooni/plugoo/tests.py | 29 +++++---
ooni/plugoo/work.py | 118 ++++++++++++++++------------------
ooni/protocols/http.py | 5 +-
17 files changed, 223 insertions(+), 177 deletions(-)
diff --git a/ooni/date.py b/ooni/date.py
index 6f83191..59ec1f8 100644
--- a/ooni/date.py
+++ b/ooni/date.py
@@ -1,11 +1,22 @@
+from ooni.lib.rfc3339 import rfc3339
from datetime import datetime
+class odate(datetime):
+ def __str__(self):
+ return rfc3339(self)
+
+ def __repr__(self):
+ return "'%s'" % rfc3339(self)
+
+ def from_rfc(self, datestr):
+ pass
+
+def now():
+ return odate.utcnow()
+
def pretty_date():
cur_time = datetime.utcnow()
d_format = "%d %B %Y %H:%M:%S"
pretty = cur_time.strftime(d_format)
return pretty
-def now():
- return datetime.utcnow()
-
diff --git a/ooni/example_plugins/examplescapy.py b/ooni/example_plugins/examplescapy.py
new file mode 100644
index 0000000..aa6d81b
--- /dev/null
+++ b/ooni/example_plugins/examplescapy.py
@@ -0,0 +1,49 @@
+import random
+from zope.interface import implements
+from twisted.python import usage
+from twisted.plugin import IPlugin
+from twisted.internet import protocol, defer
+from ooni.plugoo.tests import ITest, OONITest
+from ooni.plugoo.assets import Asset
+from ooni import log
+from ooni.protocols.scapy import ScapyTest
+
+from ooni.lib.txscapy import txsr, txsend
+
+class scapyArgs(usage.Options):
+ optParameters = []
+
+class ExampleScapyTest(ScapyTest):
+ """
+ An example of writing a scapy Test
+ """
+ implements(IPlugin, ITest)
+
+ shortName = "example_scapy"
+ description = "An example of a scapy test"
+ requirements = None
+ options = scapyArgs
+ blocking = False
+
+ receive = True
+ pcapfile = 'example_scapy.pcap'
+ def initialize(self, reactor=None):
+ if not self.reactor:
+ from twisted.internet import reactor
+ self.reactor = reactor
+
+ self.request = {}
+ self.response = {}
+
+ def build_packets(self):
+ """
+ Override this method to build scapy packets.
+ """
+ from scapy.all import IP, TCP
+ return IP()/TCP()
+
+ def load_assets(self):
+ return {}
+
+examplescapy = ExampleScapyTest(None, None, None)
+
diff --git a/ooni/example_plugins/skel.py b/ooni/example_plugins/skel.py
new file mode 100644
index 0000000..de95b48
--- /dev/null
+++ b/ooni/example_plugins/skel.py
@@ -0,0 +1,29 @@
+from zope.interface import implements
+from twisted.python import usage
+from twisted.plugin import IPlugin
+from plugoo.tests import ITest, OONITest
+from ooni import log
+
+class SkelArgs(usage.Options):
+ optParameters = [['asset', 'a', None, 'Asset file'],
+ ['resume', 'r', 0, 'Resume at this index'],
+ ['other', 'o', None, 'Other arguments']]
+
+class SkelTest(OONITest):
+ implements(IPlugin, ITest)
+
+ shortName = "skeleton"
+ 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, None)
diff --git a/ooni/lib/Makefile b/ooni/lib/Makefile
index 37abe05..a498a35 100644
--- a/ooni/lib/Makefile
+++ b/ooni/lib/Makefile
@@ -1,4 +1,4 @@
-all: txtorcon txtraceroute txscapy
+all: txtorcon txtraceroute txscapy rfc3339
txtraceroute:
echo "Processing dependency txtraceroute..."
@@ -18,3 +18,9 @@ txscapy:
mv txscapy.git/txscapy.py txscapy.py
rm -rf txscapy.git
+rfc3339:
+ echo "Processing RFC3339 dependency"
+ hg clone https://bitbucket.org/henry/rfc3339 rfc3339
+ mv rfc3339/rfc3339.py rfc3339.py
+ rf -rf rfc3339
+
diff --git a/ooni/ooniprobe.py b/ooni/ooniprobe.py
index 662956a..3eb70f6 100755
--- a/ooni/ooniprobe.py
+++ b/ooni/ooniprobe.py
@@ -71,7 +71,6 @@ def runTest(test, options, global_options):
reactor=reactor),
dict(options),
start=resume)
-
for x in wgen:
worker.push(x)
diff --git a/ooni/plugins/blocking.py b/ooni/plugins/blocking.py
index b6b63b6..6ac3c91 100644
--- a/ooni/plugins/blocking.py
+++ b/ooni/plugins/blocking.py
@@ -27,11 +27,13 @@ class BlockingTest(OONITest):
def experiment(self, args):
import urllib
- req = urllib.urlopen(args['asset'])
+ url = 'https://torproject.org/' if not 'asset' in args else args['asset']
+ req = urllib.urlopen(url)
+
return {'page': req.readlines()}
def load_assets(self):
- if self.local_options:
+ if self.local_options and self.local_options['asset']:
return {'asset': Asset(self.local_options['asset'])}
else:
return {}
diff --git a/ooni/plugins/chinatrigger.py b/ooni/plugins/chinatrigger.py
index 8a2418a..4f2dc8c 100644
--- a/ooni/plugins/chinatrigger.py
+++ b/ooni/plugins/chinatrigger.py
@@ -32,7 +32,7 @@ class ChinaTriggerTest(ScapyTest):
shortName = "chinatrigger"
description = "Triggers the chinese probes into scanning"
- requirements = None
+ requirements = ['root']
options = scapyArgs
blocking = False
diff --git a/ooni/plugins/examplescapy.py b/ooni/plugins/examplescapy.py
deleted file mode 100644
index aa6d81b..0000000
--- a/ooni/plugins/examplescapy.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import random
-from zope.interface import implements
-from twisted.python import usage
-from twisted.plugin import IPlugin
-from twisted.internet import protocol, defer
-from ooni.plugoo.tests import ITest, OONITest
-from ooni.plugoo.assets import Asset
-from ooni import log
-from ooni.protocols.scapy import ScapyTest
-
-from ooni.lib.txscapy import txsr, txsend
-
-class scapyArgs(usage.Options):
- optParameters = []
-
-class ExampleScapyTest(ScapyTest):
- """
- An example of writing a scapy Test
- """
- implements(IPlugin, ITest)
-
- shortName = "example_scapy"
- description = "An example of a scapy test"
- requirements = None
- options = scapyArgs
- blocking = False
-
- receive = True
- pcapfile = 'example_scapy.pcap'
- def initialize(self, reactor=None):
- if not self.reactor:
- from twisted.internet import reactor
- self.reactor = reactor
-
- self.request = {}
- self.response = {}
-
- def build_packets(self):
- """
- Override this method to build scapy packets.
- """
- from scapy.all import IP, TCP
- return IP()/TCP()
-
- def load_assets(self):
- return {}
-
-examplescapy = ExampleScapyTest(None, None, None)
-
diff --git a/ooni/plugins/httphost.py b/ooni/plugins/httphost.py
index 45ab587..ccadff9 100644
--- a/ooni/plugins/httphost.py
+++ b/ooni/plugins/httphost.py
@@ -23,7 +23,7 @@ from ooni.plugoo.tests import ITest, OONITest
class HTTPHostArgs(usage.Options):
optParameters = [['asset', 'a', None, 'Asset file'],
- ['controlserver', 'c', None, 'Specify the control server'],
+ ['controlserver', 'c', 'google.com', 'Specify the control server'],
['resume', 'r', 0, 'Resume at this index'],
['other', 'o', None, 'Other arguments']]
def control(self, experiment_result, args):
@@ -61,7 +61,7 @@ class HTTPHostTest(OONITest):
def check_response(self, response):
soup = BeautifulSoup(response)
- if soup.head.title.string == "WikiLeaks":
+ if soup.head.title.string == "Blocked":
# Response indicates censorship
return True
else:
@@ -107,13 +107,14 @@ class HTTPHostTest(OONITest):
def experiment(self, args):
control_server = self.local_options['controlserver']
- censored = self.httplib_test(control_server, args['asset'])
+ url = 'http://torproject.org/' if not 'asset' in args else args['asset']
+ censored = self.httplib_test(control_server, url)
return {'control': control_server,
- 'host': args['asset'],
+ 'host': url,
'censored': censored}
def load_assets(self):
- if self.local_options:
+ if self.local_options and self.local_options['asset']:
return {'asset': Asset(self.local_options['asset'])}
else:
return {}
diff --git a/ooni/plugins/httpt.py b/ooni/plugins/httpt.py
index 3f92019..d0ede0f 100644
--- a/ooni/plugins/httpt.py
+++ b/ooni/plugins/httpt.py
@@ -12,7 +12,8 @@ from ooni.protocols import http
from ooni import log
class httptArgs(usage.Options):
- optParameters = [['urls', 'u', None, 'Urls file'],
+ optParameters = [['urls', 'f', None, 'Urls file'],
+ ['url', 'u', 'http://torproject.org/', 'Test single site'],
['resume', 'r', 0, 'Resume at this index']]
class httptTest(http.HTTPTest):
@@ -30,7 +31,7 @@ class httptTest(http.HTTPTest):
return {}
def load_assets(self):
- if self.local_options:
+ if self.local_options and self.local_options['urls']:
return {'url': Asset(self.local_options['urls'])}
else:
return {}
diff --git a/ooni/plugins/skel.py b/ooni/plugins/skel.py
deleted file mode 100644
index de95b48..0000000
--- a/ooni/plugins/skel.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from zope.interface import implements
-from twisted.python import usage
-from twisted.plugin import IPlugin
-from plugoo.tests import ITest, OONITest
-from ooni import log
-
-class SkelArgs(usage.Options):
- optParameters = [['asset', 'a', None, 'Asset file'],
- ['resume', 'r', 0, 'Resume at this index'],
- ['other', 'o', None, 'Other arguments']]
-
-class SkelTest(OONITest):
- implements(IPlugin, ITest)
-
- shortName = "skeleton"
- 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, None)
diff --git a/ooni/plugins/tcpconnect.py b/ooni/plugins/tcpconnect.py
index 75f32c1..27df08d 100644
--- a/ooni/plugins/tcpconnect.py
+++ b/ooni/plugins/tcpconnect.py
@@ -15,7 +15,7 @@ from ooni.plugoo.assets import Asset
from ooni import log
class tcpconnectArgs(usage.Options):
- optParameters = [['blabla', 'a', None, 'Asset file'],
+ optParameters = [['asset', 'a', None, 'File containing IP:PORT combinations, one per line.'],
['resume', 'r', 0, 'Resume at this index']]
class tcpconnectTest(OONITest):
@@ -28,7 +28,10 @@ class tcpconnectTest(OONITest):
blocking = False
def experiment(self, args):
- host, port = args['blabla'].split(':')
+ try:
+ host, port = args['asset'].split(':')
+ except:
+ raise Exception("Error in parsing asset. Wrong format?")
class DummyFactory(Factory):
def buildProtocol(self, addr):
return Protocol()
@@ -53,7 +56,7 @@ class tcpconnectTest(OONITest):
def load_assets(self):
if self.local_options:
- return {'blabla': Asset(self.local_options['blabla'])}
+ return {'asset': Asset(self.local_options['asset'])}
else:
return {}
diff --git a/ooni/plugoo/interface.py b/ooni/plugoo/interface.py
index 68cff1a..c3b3855 100644
--- a/ooni/plugoo/interface.py
+++ b/ooni/plugoo/interface.py
@@ -10,14 +10,37 @@ class ITest(Interface):
requirements = Attribute("""What is required to run this this test, for example raw socket access or UDP or TCP""")
- #deferred = Attribute("""This will be fired on test completion""")
- #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""")
blocking = Attribute("""True or False, stating if the test should be run in a thread or not.""")
- def startTest(asset):
+ def control(experiment_result, args):
"""
- Launches the Test with the specified arguments on a node.
+ @param experiment_result: The result returned by the experiment method.
+
+ @param args: the keys of this dict are the names of the assets passed in
+ from load_assets. The value is one item of the asset.
+
+ Must return a dict containing what should be written to the report.
+ Anything returned by control ends up inside of the YAMLOONI report.
+ """
+
+ def experiment(args):
+ """
+ Perform all the operations that are necessary to running a test.
+
+ @param args: the keys of this dict are the names of the assets passed in
+ from load_assets. The value is one item of the asset.
+
+ Must return a dict containing the values to be passed to control.
+ """
+
+ def load_assets():
+ """
+ Load the assets that should be passed to the Test. These are the inputs
+ to the OONI test.
+ Must return a dict that has as keys the asset names and values the
+ asset contents.
+ If the test does not have any assets it should return an empty dict.
"""
diff --git a/ooni/plugoo/reports.py b/ooni/plugoo/reports.py
index 6140294..63bc3f5 100644
--- a/ooni/plugoo/reports.py
+++ b/ooni/plugoo/reports.py
@@ -44,7 +44,7 @@ class Report:
header += "# %s\n\n" % pretty_date
self._write_to_report(header)
# XXX replace this with something proper
- test_details = {'start_time': date.now(),
+ test_details = {'start_time': str(date.now()),
'asn': 'ASN-1234',
'test_name': self.testname,
'addr': '1234'}
diff --git a/ooni/plugoo/tests.py b/ooni/plugoo/tests.py
index 608cee3..a3e9150 100644
--- a/ooni/plugoo/tests.py
+++ b/ooni/plugoo/tests.py
@@ -1,16 +1,14 @@
import os
-from datetime import datetime
import yaml
from zope.interface import Interface, Attribute
import logging
import itertools
-import gevent
-
from twisted.internet import reactor, defer, threads
from twisted.python import failure
from ooni import log
+from ooni import date
from ooni.plugoo import assets, work
from ooni.plugoo.reports import Report
from ooni.plugoo.interface import ITest
@@ -38,7 +36,7 @@ class OONITest(object):
This method should be overriden by the test writer to provide the logic
for loading their assets.
"""
- return {'asset': None}
+ return {}
def __repr__(self):
return "<OONITest %s %s %s>" % (self.options, self.global_options,
@@ -46,11 +44,11 @@ class OONITest(object):
def finished(self, control):
#self.ooninet.report(result)
- self.end_time = datetime.now()
+ self.end_time = date.now()
result = self.result
- result['start_time'] = self.start_time
- result['end_time'] = self.end_time
- result['run_time'] = self.end_time - self.start_time
+ result['start_time'] = str(self.start_time)
+ result['end_time'] = str(self.end_time)
+ result['run_time'] = str(self.end_time - self.start_time)
result['control'] = control
log.msg("FINISHED %s" % result)
self.report(result)
@@ -68,14 +66,23 @@ class OONITest(object):
def control(self, result, args):
log.msg("Doing control")
- return result
+
+ if self.blocking:
+ return result
+
+ def end(cb):
+ return result
+ d = defer.Deferred()
+ d.addCallback(end)
+ return d
def experiment(self, args):
log.msg("Doing experiment")
- return {}
+ d = defer.Deferred()
+ return d
def startTest(self, args):
- self.start_time = datetime.now()
+ self.start_time = date.now()
log.msg("Starting test %s" % self.__class__)
return self._do_experiment(args)
diff --git a/ooni/plugoo/work.py b/ooni/plugoo/work.py
index 7e9be0c..a3fb168 100644
--- a/ooni/plugoo/work.py
+++ b/ooni/plugoo/work.py
@@ -40,12 +40,7 @@ class Worker(object):
if isinstance(r, failure.Failure):
r.trap()
- print r['start_time']
- print r['end_time']
- print r['run_time']
-
if self._running == 0 and not self._queued:
- print "I am done."
reactor.stop()
return r
@@ -60,62 +55,6 @@ class Worker(object):
self._queued.append((workunit, d))
return d
-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.
-
- The execution of a unit of work should be Atomic.
-
- Reporting to the OONI-net happens on completion of a Unit of Work.
-
- @Node node: This represents the node associated with the Work Unit
- @Asset asset: This is the asset associated with the Work Unit
- @Test test: This represents the Test to be with the specified assets
- @ivar arguments: These are the extra attributes to be passsed to the Test
- """
-
- node = None
- asset = None
- test = None
- 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.assetNames = assetNames
- self.idx = idx
-
- def __iter__(self):
- return self
-
- def __repr__(self):
- return "<WorkUnit %s %s %s>" % (self.assetNames, self.Test, self.idx)
-
- def serialize(self):
- """
- Serialize this unit of work for RPC activity.
- """
- return yaml.dump(self)
-
- def next(self):
- """
- Launches the Unit of Work with the specified assets on the node.
- """
- try:
- asset = self.assetGenerator.next()
- ret = self.Test.set_asset(asset)
- return ret
- except StopIteration:
- raise StopIteration
-
-
class WorkGenerator(object):
"""
Factory responsible for creating units of work.
@@ -129,7 +68,6 @@ class WorkGenerator(object):
self.Test = test
if self.Test.assets and self.Test.assets.values()[0]:
- print self.Test.assets
self.assetGenerator = itertools.product(*self.Test.assets.values())
else:
self.assetGenerator = None
@@ -156,7 +94,7 @@ class WorkGenerator(object):
if not self.assetGenerator:
self.end = True
- return (self.assetNames, self.Test, self.idx)
+ return ({}, self.Test, self.idx)
try:
asset = self.assetGenerator.next()
@@ -196,3 +134,57 @@ class WorkGenerator(object):
self.idx += 1
return WorkUnit(p_asset, self.assetNames, self.Test, self.idx)
+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.
+
+ The execution of a unit of work should be Atomic.
+
+ Reporting to the OONI-net happens on completion of a Unit of Work.
+
+ @Node node: This represents the node associated with the Work Unit
+ @Asset asset: This is the asset associated with the Work Unit
+ @Test test: This represents the Test to be with the specified assets
+ @ivar arguments: These are the extra attributes to be passsed to the Test
+ """
+
+ node = None
+ asset = None
+ test = None
+ 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.assetNames = assetNames
+ self.idx = idx
+
+ def __iter__(self):
+ return self
+
+ def __repr__(self):
+ return "<WorkUnit %s %s %s>" % (self.assetNames, self.Test, self.idx)
+
+ def serialize(self):
+ """
+ Serialize this unit of work for RPC activity.
+ """
+ return yaml.dump(self)
+
+ def next(self):
+ """
+ Launches the Unit of Work with the specified assets on the node.
+ """
+ try:
+ asset = self.assetGenerator.next()
+ ret = self.Test.set_asset(asset)
+ return ret
+ except StopIteration:
+ raise StopIteration
diff --git a/ooni/protocols/http.py b/ooni/protocols/http.py
index a2081bd..835735b 100644
--- a/ooni/protocols/http.py
+++ b/ooni/protocols/http.py
@@ -30,7 +30,6 @@ class BodyReceiver(protocol.Protocol):
def connectionLost(self, reason):
self.finished.callback(self.data)
-
from twisted.web.http_headers import Headers
class HTTPTest(OONITest):
"""
@@ -77,7 +76,9 @@ class HTTPTest(OONITest):
def experiment(self, args):
log.msg("Running experiment")
- d = self.build_request(args['url'])
+ url = self.local_options['url'] if 'url' not in args else args['url']
+
+ d = self.build_request(url)
def finished(data):
return data
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits