[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [ooni-probe/master] Add support for starting tests from the HTTP API
commit b2118234e7b85ad4381ef2caddb9ec37e131fba4
Author: Arturo Filastò <arturo@xxxxxxxxxxx>
Date: Fri Apr 1 19:48:14 2016 +0200
Add support for starting tests from the HTTP API
---
ooni/director.py | 2 +-
ooni/web/resources.py | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++
ooni/web/root.py | 40 ++++++++++++
ooni/webui.py | 57 +++++++++++++++++
4 files changed, 270 insertions(+), 1 deletion(-)
diff --git a/ooni/director.py b/ooni/director.py
index d6503f2..9897ffd 100644
--- a/ooni/director.py
+++ b/ooni/director.py
@@ -134,7 +134,7 @@ class Director(object):
elif config.tor.control_port and config.tor_state is None:
yield connect_to_control_port()
- if config.global_options['no-geoip']:
+ if config.global_options.get('no-geoip'):
aux = [False]
if config.global_options.get('annotations') is not None:
annotations = [k.lower() for k in config.global_options['annotations'].keys()]
diff --git a/ooni/web/resources.py b/ooni/web/resources.py
new file mode 100644
index 0000000..336fdc0
--- /dev/null
+++ b/ooni/web/resources.py
@@ -0,0 +1,172 @@
+import json
+from twisted.web import resource
+from twisted.python import usage
+
+from ooni import errors
+from ooni.nettest import NetTestLoader
+
+
+class WuiResource(resource.Resource):
+ isLeaf = True
+ XSRF_HEADER = 'X-XSRF-TOKEN'
+ # XXX set this to true when stable version
+ XSRF_PROTECTION = False
+
+ def __init__(self, director):
+ self.director = director
+ resource.Resource.__init__(self)
+
+ def check_xsrf(self, request):
+ if self.XSRF_PROTECTION is False:
+ return True
+ if request.requestHeaders.hasHeader(self.XSRF_HEADER):
+ return True
+ return False
+
+ def render(self, request):
+ if not self.check_xsrf(request):
+ obj = {
+ 'error_code': 400,
+ 'error_message': ('Missing cross site request forgery '
+ 'header \'{}\''.format(self.XSRF_HEADER))
+ }
+ request.setResponseCode(403)
+ return self.render_json(obj, request)
+ obj = resource.Resource.render(self, request)
+ return self.render_json(obj, request)
+
+ def render_json(self, obj, request):
+ json_string = json.dumps(obj) + "\n"
+ request.setHeader('Content-Type', 'application/json')
+ request.setHeader('Content-Length', len(json_string))
+ return json_string
+
+
+class DecksGenerate(WuiResource):
+ def render_GET(self, request):
+ return {"generate": "deck"}
+
+
+class DecksStart(WuiResource):
+ def __init__(self, director, deck_name):
+ WuiResource.__init__(self, director)
+ self.deck_name = deck_name
+
+ def render_GET(self, request):
+ return {"start": self.deck_name}
+
+
+class DecksStop(WuiResource):
+ def __init__(self, director, deck_id):
+ WuiResource.__init__(self, director)
+ self.deck_id = deck_id
+
+ def render_GET(self, request):
+ return {"stop": self.deck_id}
+
+
+class DecksStatus(WuiResource):
+ def __init__(self, director, deck_name):
+ WuiResource.__init__(self, director)
+ self.deck_name = deck_name
+
+ def render_GET(self, request):
+ return {"deck": self.deck_name}
+
+
+class DecksList(WuiResource):
+ def render_GET(self, request):
+ return {"deck": "list"}
+
+
+def getNetTestLoader(test_options, test_file):
+ """
+ Args:
+ test_options: (dict) containing as keys the option names.
+
+ test_file: (string) the path to the test_file to be run.
+ Returns:
+ an instance of :class:`ooni.nettest.NetTestLoader` with the specified
+ test_file and the specified options.
+ """
+ options = []
+ for k, v in test_options.items():
+ options.append('--'+k)
+ options.append(v)
+
+ net_test_loader = NetTestLoader(options,
+ test_file=test_file)
+ return net_test_loader
+
+class TestsStart(WuiResource):
+ def __init__(self, director, test_name):
+ WuiResource.__init__(self, director)
+ self.test_name = test_name
+
+ def render_POST(self, request):
+ try:
+ net_test = self.director.netTests[self.test_name]
+ except KeyError:
+ request.setResponseCode(500)
+ return {
+ 'error_code': 500,
+ 'error_message': 'Could not find the specified test'
+ }
+ test_options = json.load(request.content)
+ net_test_loader = getNetTestLoader(test_options, net_test['path'])
+ try:
+ net_test_loader.checkOptions()
+ # XXX we actually want to generate the report_filename in a smart
+ # way so that we can know where it is located and learn the results
+ # of the measurement.
+ report_filename = None
+ self.director.startNetTest(net_test_loader, report_filename)
+ except errors.MissingRequiredOption, option_name:
+ request.setResponseCode(500)
+ return {
+ 'error_code': 501,
+ 'error_message': ('Missing required option: '
+ '\'{}\''.format(option_name))
+ }
+ except usage.UsageError:
+ request.setResponseCode(500)
+ return {
+ 'error_code': 502,
+ 'error_message': 'Error in parsing options'
+ }
+ except errors.InsufficientPrivileges:
+ request.setResponseCode(500)
+ return {
+ 'error_code': 503,
+ 'error_message': 'Insufficient priviledges'
+ }
+
+ return {"deck": "list"}
+
+
+class TestsStop(WuiResource):
+ def __init__(self, director, test_id):
+ WuiResource.__init__(self, director)
+ self.test_id = test_id
+
+ def render_GET(self, request):
+ return {"deck": "list"}
+
+
+class TestsStatus(WuiResource):
+ def __init__(self, director, test_id):
+ WuiResource.__init__(self, director)
+ self.test_id = test_id
+
+ def render_GET(self, request):
+ return {"deck": "list"}
+
+
+class TestsList(WuiResource):
+ def render_GET(self, request):
+ return self.director.netTests
+
+
+class Results(WuiResource):
+ def render_GET(self, request):
+ return {"result": "bar"}
diff --git a/ooni/web/root.py b/ooni/web/root.py
new file mode 100644
index 0000000..7ceba4a
--- /dev/null
+++ b/ooni/web/root.py
@@ -0,0 +1,40 @@
+import os
+import re
+from twisted.web import resource, static
+
+from .resources import DecksGenerate, DecksStart, DecksStop
+from .resources import DecksStatus, DecksList, TestsStart
+from .resources import TestsStop, TestsStatus, TestsList
+from .resources import Results
+
+
+class OONIProbeWebRoot(resource.Resource):
+ routes = [
+ ('^/decks/generate$', DecksGenerate),
+ ('^/decks/(.*)/start$', DecksStart),
+ ('^/decks/(.*)/stop$', DecksStop),
+ ('^/decks/(.*)$', DecksStatus),
+ ('^/decks$', DecksList),
+ ('^/tests/(.*)/start$', TestsStart),
+ ('^/tests/(.*)/stop$', TestsStop),
+ ('^/tests/(.*)$', TestsStatus),
+ ('^/tests$', TestsList),
+ ('^/results$', Results)
+ ]
+
+ def __init__(self, config, director):
+ resource.Resource.__init__(self)
+
+ self._director = director
+ self._config = config
+ self._route_map = map(lambda x: (re.compile(x[0]), x[1]), self.routes)
+
+ wui_directory = os.path.join(self._config.data_directory, 'ui', 'app')
+ self._static = static.File(wui_directory)
+
+ def getChild(self, path, request):
+ for route, r in self._route_map:
+ match = route.search(request.path)
+ if match:
+ return r(self._director, *match.groups())
+ return self._static.getChild(path, request)
diff --git a/ooni/webui.py b/ooni/webui.py
new file mode 100644
index 0000000..e2fe450
--- /dev/null
+++ b/ooni/webui.py
@@ -0,0 +1,57 @@
+import os
+
+from twisted.scripts import twistd
+from twisted.python import usage
+from twisted.internet import reactor
+from twisted.web import server
+from twisted.application import service
+
+from ooni.web.root import OONIProbeWebRoot
+from ooni.settings import config
+from ooni.director import Director
+from ooni.utils import log
+
+class WebUI(service.MultiService):
+ portNum = 8822
+ def startService(self):
+ service.MultiService.startService(self)
+ config.set_paths()
+ config.initialize_ooni_home()
+ config.read_config_file()
+ def _started(res):
+ log.msg("Director started")
+ root = server.Site(OONIProbeWebRoot(config, director))
+ self._port = reactor.listenTCP(self.portNum, root)
+ director = Director()
+ d = director.start()
+ d.addCallback(_started)
+ d.addErrback(self._startupFailed)
+
+ def _startupFailed(self, err):
+ log.err("Failed to start the director")
+ log.exception(err)
+ os.abort()
+
+ def stopService(self):
+ if self._port:
+ self._port.stopListening()
+
+class StartOoniprobeWebUIPlugin:
+ tapname = "ooniprobe"
+ def makeService(self, so):
+ return WebUI()
+
+class MyTwistdConfig(twistd.ServerOptions):
+ subCommands = [("StartOoniprobeWebUI", None, usage.Options, "ooniprobe web ui")]
+
+def start():
+ twistd_args = ["--nodaemon"]
+ twistd_config = MyTwistdConfig()
+ twistd_args.append("StartOoniprobeWebUI")
+ try:
+ twistd_config.parseOptions(twistd_args)
+ except usage.error, ue:
+ print("ooniprobe: usage error from twistd: {}\n".format(ue))
+ twistd_config.loadedPlugins = {"StartOoniprobeWebUI": StartOoniprobeWebUIPlugin()}
+ twistd.runApp(twistd_config)
+ return 0
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits