Measurement Developer API¶
nettest
Module¶
-
class
ooni.nettest.
NetTest
(net_test_loader, report)[source]¶ Bases:
object
-
director
= None¶
-
doneReport
(report_results)[source]¶ This will get called every time a report 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.
-
generateMeasurements
()[source]¶ This is a generator that yields measurements and registers the callbacks for when a measurement is successful or has failed.
-
makeMeasurement
(test_instance, test_method, test_input=None)[source]¶ 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
-
-
class
ooni.nettest.
NetTestCase
[source]¶ Bases:
object
This is the base of the OONI nettest universe. When you write a nettest you will subclass this object.
inputs: can be set to a static set of inputs. All the tests (the methods starting with the “test” prefix) will be run once per input. At every run the _input_ attribute of the TestCase instance will be set to the value of the current iteration over inputs. Any python iterable object can be set to inputs.
inputFile: attribute should be set to an array containing the command line argument that should be used as the input file. Such array looks like this:
["commandlinearg", "c", "default value" "The description"]
The second value of such arrray is the shorthand for the command line arg. The user will then be able to specify inputs to the test via:
ooniprobe mytest.py --commandlinearg path/to/file.txt
or
ooniprobe mytest.py -c path/to/file.txt
inputProcessor: should be set to a function that takes as argument a filename and it will return the input to be passed to the test instance.
name: should be set to the name of the test.
author: should contain the name and contact details for the test author. The format for such string is as follows:
The Name <email@example.com>
version: is the version string of the test.
requiresRoot: set to True if the test must be run as root.
- usageOptions: a subclass of twisted.python.usage.Options for processing
of command line arguments
localOptions: contains the parsed command line arguments.
Quirks: Every class that is prefixed with test must return a twisted.internet.defer.Deferred.
-
baseFlags
= None¶
-
baseParameters
= None¶
-
description
= 'Sorry, this test has no description :('¶
-
displaySummary
(summary)[source]¶ This gets called after the test has run to allow printing out of a summary of the test run.
-
getInputProcessor
()[source]¶ This method must be called after all options are validated by _checkValidOptions and _checkRequiredOptions, which ensure that if the inputFile is a required option it will be present.
We check to see if it’s possible to have an input file and if the user has specified such file.
If the operations to be done here are network related or blocking, they should be wrapped in a deferred. That is the return value of this method should be a
twisted.internet.defer.Deferred
.- Returns:
- a generator that will yield one item from the file based on the inputProcessor.
-
inputFile
= None¶
-
inputFileSpecified
¶ - Returns:
- True
- when inputFile is supported and is specified
- False
- when input is either not support or not specified
-
inputFilename
= None¶
-
inputProcessor
(filename)[source]¶ You may replace this with your own custom input processor. It takes as input a file name.
An inputProcessor is an iterator that will yield one item from the file and takes as argument a filename.
This can be useful when you have some input data that is in a certain format and you want to set the input attribute of the test to something that you will be able to properly process.
For example you may wish to have an input processor that will allow you to ignore comments in files. This can be easily achieved like so:
fp = open(filename) for x in fp.xreadlines(): if x.startswith("#"): continue yield x.strip() fp.close()
Other fun stuff is also possible.
-
inputs
= None¶
-
localOptions
= {}¶
-
name
= 'This test is nameless'¶
-
optParameters
= None¶
-
postProcessor
(measurements)[source]¶ Subclass this to do post processing tasks that are to occur once all the test methods have been called once per input. postProcessing works exactly like test methods, in the sense that anything that gets written to the object self.report[] will be added to the final test report. You should also place in this method any logic that is required for generating the summary.
-
report
= {}¶
-
requiredOptions
= []¶
-
requiredTestHelpers
= {}¶
-
requirements
()[source]¶ Place in here logic that will be executed before the test is to be run. If some condition is not met then you should raise an exception.
-
requiresRoot
= False¶
-
requiresTor
= False¶
-
usageOptions
¶ alias of
Options
-
version
= '0.0.0'¶
-
class
ooni.nettest.
NetTestLoader
(options, test_file=None, test_string=None)[source]¶ Bases:
object
-
collector
= None¶
-
inputFiles
¶
-
loadNetTestString
(net_test_string)[source]¶ Load NetTest from a string. WARNING input to this function MUST be sanitized and NEVER take untrusted input. Failure to do so will result in code exec.
net_test_string:
a string that contains the net test to be run.
-
method_prefix
= 'test'¶
-
reportID
= None¶
-
requiredTestHelpers
¶
-
requiresTor
= False¶
-
setupTestCases
(test_cases)[source]¶ Creates all the necessary test_cases (a list of tuples containing the NetTestCase (test_class, test_method))
- example:
[(test_classA, test_method1), (test_classA, test_method2), (test_classA, test_method3), (test_classA, test_method4), (test_classA, test_method5),
(test_classB, test_method1), (test_classB, test_method2)]
Note: the inputs must be valid for test_classA and test_classB.
- net_test_file:
- is either a file path or a file like object that will be used to generate the test_cases.
-
testDetails
¶
-
usageOptions
¶
-
-
class
ooni.nettest.
NetTestState
(allTasksDone)[source]¶ Bases:
object
-
allTasksScheduled
()[source]¶ 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.
-
-
ooni.nettest.
getNetTestInformation
(net_test_file)[source]¶ Returns a dict containing:
- {
‘id’: the test filename excluding the .py extension, ‘name’: the full name of the test, ‘description’: the description of the test, ‘version’: version number of this test, ‘arguments’: a dict containing as keys the supported arguments and as
values the argument description.
}
-
ooni.nettest.
getOption
(opt_parameter, required_options, type='text')[source]¶ - Arguments:
- usage_options: a list as should be the optParameters of an UsageOptions
- class.
- required_options: a list containing the strings of the options that are
- required.
type: a string containing the type of the option.
- Returns:
- a dict containing
- {
- ‘description’: the description of the option, ‘default’: the default value of the option, ‘required’: True|False if the option is required or not, ‘type’: the type of the option (‘text’ or ‘file’)
}
settings
Module¶
oonicli
Module¶
-
class
ooni.oonicli.
Options
[source]¶ Bases:
twisted.python.usage.Options
-
compData
= <twisted.python.usage.Completions object>¶
-
longdesc
= 'ooniprobe loads and executes a suite or a set of suites of network tests. These are loaded from modules, packages and files listed on the command line'¶
-
optFlags
= [['help', 'h'], ['resume', 'r'], ['no-collector', 'n'], ['no-geoip', 'g'], ['list', 's'], ['printdeck', 'p'], ['verbose', 'v']]¶
-
optParameters
= [['reportfile', 'o', None, 'report file name'], ['testdeck', 'i', None, 'Specify as input a test deck: a yaml file containing the tests to run and their arguments'], ['collector', 'c', None, 'Address of the collector of test results. This option should not be used, but you should always use a bouncer.'], ['bouncer', 'b', 'httpo://nkvphnp3p6agi5qq.onion', 'Address of the bouncer for test helpers.'], ['logfile', 'l', None, 'log file name'], ['pcapfile', 'O', None, 'pcap file name'], ['configfile', 'f', None, 'Specify a path to the ooniprobe configuration file'], ['datadir', 'd', None, 'Specify a path to the ooniprobe data directory'], ['annotations', 'a', None, 'Annotate the report with a key:value[, key:value] format.']]¶
-
opt_spew
()[source]¶ Print an insanely verbose log of everything that happens. Useful when debugging freezes or locks in complex code.
-
synopsis
= 'sphinx-build [options] [path to test].py\n '¶
-
tracer
= None¶
-
reporter
Module¶
-
class
ooni.reporter.
OONIBReportLog
(file_name=None)[source]¶ Bases:
object
Used to keep track of report creation on a collector backend.
-
reports_in_progress
¶
-
reports_incomplete
¶
-
reports_to_upload
¶
-
-
class
ooni.reporter.
OONIBReporter
(test_details, collector_address)[source]¶ Bases:
ooni.reporter.OReporter
-
class
ooni.reporter.
OSafeDumper
(stream, default_style=None, default_flow_style=None, canonical=None, indent=None, width=None, allow_unicode=None, line_break=None, encoding=None, explicit_start=None, explicit_end=None, version=None, tags=None)[source]¶ Bases:
yaml.emitter.Emitter
,yaml.serializer.Serializer
,ooni.reporter.OSafeRepresenter
,yaml.resolver.Resolver
This is a modification of the YAML Safe Dumper to use our own Safe Representer that supports complex numbers.
-
class
ooni.reporter.
OSafeRepresenter
(default_style=None, default_flow_style=None)[source]¶ Bases:
yaml.representer.SafeRepresenter
This is a custom YAML representer that allows us to represent reports safely. It extends the SafeRepresenter to be able to also represent complex numbers and scapy packet.
-
represent_data
(data)[source]¶ This is very hackish. There is for sure a better way either by using the add_multi_representer or add_representer, the issue though lies in the fact that Scapy packets are metaclasses that leads to yaml.representer.get_classobj_bases to not be able to properly get the base of class of a Scapy packet. XXX fully debug this problem
-
yaml_representers
= {<type 'int'>: <unbound method SafeRepresenter.represent_int>, <type 'unicode'>: <unbound method SafeRepresenter.represent_unicode>, <type 'long'>: <unbound method SafeRepresenter.represent_long>, <type 'float'>: <unbound method SafeRepresenter.represent_float>, <type 'NoneType'>: <unbound method SafeRepresenter.represent_none>, <type 'dict'>: <unbound method SafeRepresenter.represent_dict>, <type 'bool'>: <unbound method SafeRepresenter.represent_bool>, <type 'datetime.date'>: <unbound method SafeRepresenter.represent_date>, <type 'complex'>: <unbound method OSafeRepresenter.represent_complex>, <type 'tuple'>: <unbound method SafeRepresenter.represent_list>, <type 'list'>: <unbound method SafeRepresenter.represent_list>, <type 'datetime.datetime'>: <unbound method SafeRepresenter.represent_datetime>, <type 'set'>: <unbound method SafeRepresenter.represent_set>, <type 'str'>: <unbound method SafeRepresenter.represent_str>, None: <unbound method SafeRepresenter.represent_undefined>}¶
-
-
class
ooni.reporter.
Report
(test_details, report_filename, reportEntryManager, collector_address=None)[source]¶ Bases:
object
-
close
()[source]¶ 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.
-
open
()[source]¶ This will create all the reports that need to be created and fires the created callback of the reporter whose report got created.
-
write
(measurement)[source]¶ 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 that will fire once all the report entries have been written or errbacks when no more reporters
-
-
class
ooni.reporter.
YAMLReporter
(test_details, report_destination='.', report_filename=None)[source]¶ Bases:
ooni.reporter.OReporter
These are useful functions for reporting to YAML format.
- report_destination:
- the destination directory of the report