258 lines
9.2 KiB
Plaintext
258 lines
9.2 KiB
Plaintext
tdc - Linux Traffic Control (tc) unit testing suite
|
|
|
|
Author: Lucas Bates - lucasb@mojatatu.com
|
|
|
|
tdc is a Python script to load tc unit tests from a separate JSON file and
|
|
execute them inside a network namespace dedicated to the task.
|
|
|
|
|
|
REQUIREMENTS
|
|
------------
|
|
|
|
* Minimum Python version of 3.4. Earlier 3.X versions may work but are not
|
|
guaranteed.
|
|
|
|
* The kernel must have network namespace support if using nsPlugin
|
|
|
|
* The kernel must have veth support available, as a veth pair is created
|
|
prior to running the tests when using nsPlugin.
|
|
|
|
* The kernel must have the appropriate infrastructure enabled to run all tdc
|
|
unit tests. See the config file in this directory for minimum required
|
|
features. As new tests will be added, config options list will be updated.
|
|
|
|
* All tc-related features being tested must be built in or available as
|
|
modules. To check what is required in current setup run:
|
|
./tdc.py -c
|
|
|
|
Note:
|
|
In the current release, tdc run will abort due to a failure in setup or
|
|
teardown commands - which includes not being able to run a test simply
|
|
because the kernel did not support a specific feature. (This will be
|
|
handled in a future version - the current workaround is to run the tests
|
|
on specific test categories that your kernel supports)
|
|
|
|
|
|
BEFORE YOU RUN
|
|
--------------
|
|
|
|
The path to the tc executable that will be most commonly tested can be defined
|
|
in the tdc_config.py file. Find the 'TC' entry in the NAMES dictionary and
|
|
define the path.
|
|
|
|
If you need to test a different tc executable on the fly, you can do so by
|
|
using the -p option when running tdc:
|
|
./tdc.py -p /path/to/tc
|
|
|
|
|
|
RUNNING TDC
|
|
-----------
|
|
|
|
To use tdc, root privileges are required. This is because the
|
|
commands being tested must be run as root. The code that enforces
|
|
execution by root uid has been moved into a plugin (see PLUGIN
|
|
ARCHITECTURE, below).
|
|
|
|
Tests that use a network device should have nsPlugin.py listed as a
|
|
requirement for that test. nsPlugin executes all commands within a
|
|
network namespace and creates a veth pair which may be used in those test
|
|
cases. To disable execution within the namespace, pass the -N option
|
|
to tdc when starting a test run; the veth pair will still be created
|
|
by the plugin.
|
|
|
|
Running tdc without any arguments will run all tests. Refer to the section
|
|
on command line arguments for more information, or run:
|
|
./tdc.py -h
|
|
|
|
tdc will list the test names as they are being run, and print a summary in
|
|
TAP (Test Anything Protocol) format when they are done. If tests fail,
|
|
output captured from the failing test will be printed immediately following
|
|
the failed test in the TAP output.
|
|
|
|
|
|
OVERVIEW OF TDC EXECUTION
|
|
-------------------------
|
|
|
|
One run of tests is considered a "test suite" (this will be refined in the
|
|
future). A test suite has one or more test cases in it.
|
|
|
|
A test case has four stages:
|
|
|
|
- setup
|
|
- execute
|
|
- verify
|
|
- teardown
|
|
|
|
The setup and teardown stages can run zero or more commands. The setup
|
|
stage does some setup if the test needs it. The teardown stage undoes
|
|
the setup and returns the system to a "neutral" state so any other test
|
|
can be run next. These two stages require any commands run to return
|
|
success, but do not otherwise verify the results.
|
|
|
|
The execute and verify stages each run one command. The execute stage
|
|
tests the return code against one or more acceptable values. The
|
|
verify stage checks the return code for success, and also compares
|
|
the stdout with a regular expression.
|
|
|
|
Each of the commands in any stage will run in a shell instance.
|
|
|
|
|
|
USER-DEFINED CONSTANTS
|
|
----------------------
|
|
|
|
The tdc_config.py file contains multiple values that can be altered to suit
|
|
your needs. Any value in the NAMES dictionary can be altered without affecting
|
|
the tests to be run. These values are used in the tc commands that will be
|
|
executed as part of the test. More will be added as test cases require.
|
|
|
|
Example:
|
|
$TC qdisc add dev $DEV1 ingress
|
|
|
|
The NAMES values are used to substitute into the commands in the test cases.
|
|
|
|
|
|
COMMAND LINE ARGUMENTS
|
|
----------------------
|
|
|
|
Run tdc.py -h to see the full list of available arguments.
|
|
|
|
usage: tdc.py [-h] [-p PATH] [-D DIR [DIR ...]] [-f FILE [FILE ...]]
|
|
[-c [CATG [CATG ...]]] [-e ID [ID ...]] [-l] [-s] [-i] [-v] [-N]
|
|
[-d DEVICE] [-P] [-n] [-V]
|
|
|
|
Linux TC unit tests
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
-p PATH, --path PATH The full path to the tc executable to use
|
|
-v, --verbose Show the commands that are being run
|
|
-N, --notap Suppress tap results for command under test
|
|
-d DEVICE, --device DEVICE
|
|
Execute test cases that use a physical device, where
|
|
DEVICE is its name. (If not defined, tests that require
|
|
a physical device will be skipped)
|
|
-P, --pause Pause execution just before post-suite stage
|
|
|
|
selection:
|
|
select which test cases: files plus directories; filtered by categories
|
|
plus testids
|
|
|
|
-D DIR [DIR ...], --directory DIR [DIR ...]
|
|
Collect tests from the specified directory(ies)
|
|
(default [tc-tests])
|
|
-f FILE [FILE ...], --file FILE [FILE ...]
|
|
Run tests from the specified file(s)
|
|
-c [CATG [CATG ...]], --category [CATG [CATG ...]]
|
|
Run tests only from the specified category/ies, or if
|
|
no category/ies is/are specified, list known
|
|
categories.
|
|
-e ID [ID ...], --execute ID [ID ...]
|
|
Execute the specified test cases with specified IDs
|
|
|
|
action:
|
|
select action to perform on selected test cases
|
|
|
|
-l, --list List all test cases, or those only within the
|
|
specified category
|
|
-s, --show Display the selected test cases
|
|
-i, --id Generate ID numbers for new test cases
|
|
|
|
netns:
|
|
options for nsPlugin (run commands in net namespace)
|
|
|
|
-N, --no-namespace
|
|
Do not run commands in a network namespace.
|
|
|
|
valgrind:
|
|
options for valgrindPlugin (run command under test under Valgrind)
|
|
|
|
-V, --valgrind Run commands under valgrind
|
|
|
|
|
|
PLUGIN ARCHITECTURE
|
|
-------------------
|
|
|
|
There is now a plugin architecture, and some of the functionality that
|
|
was in the tdc.py script has been moved into the plugins.
|
|
|
|
The plugins are in the directory plugin-lib. The are executed from
|
|
directory plugins. Put symbolic links from plugins to plugin-lib,
|
|
and name them according to the order you want them to run. This is not
|
|
necessary if a test case being run requires a specific plugin to work.
|
|
|
|
Example:
|
|
|
|
bjb@bee:~/work/tc-testing$ ls -l plugins
|
|
total 4
|
|
lrwxrwxrwx 1 bjb bjb 27 Oct 4 16:12 10-rootPlugin.py -> ../plugin-lib/rootPlugin.py
|
|
lrwxrwxrwx 1 bjb bjb 25 Oct 12 17:55 20-nsPlugin.py -> ../plugin-lib/nsPlugin.py
|
|
-rwxr-xr-x 1 bjb bjb 0 Sep 29 15:56 __init__.py
|
|
|
|
The plugins are a subclass of TdcPlugin, defined in TdcPlugin.py and
|
|
must be called "SubPlugin" so tdc can find them. They are
|
|
distinguished from each other in the python program by their module
|
|
name.
|
|
|
|
This base class supplies "hooks" to run extra functions. These hooks are as follows:
|
|
|
|
pre- and post-suite
|
|
pre- and post-case
|
|
pre- and post-execute stage
|
|
adjust-command (runs in all stages and receives the stage name)
|
|
|
|
The pre-suite hook receives the number of tests and an array of test ids.
|
|
This allows you to dump out the list of skipped tests in the event of a
|
|
failure during setup or teardown stage.
|
|
|
|
The pre-case hook receives the ordinal number and test id of the current test.
|
|
|
|
The adjust-command hook receives the stage id (see list below) and the
|
|
full command to be executed. This allows for last-minute adjustment
|
|
of the command.
|
|
|
|
The stages are identified by the following strings:
|
|
|
|
- pre (pre-suite)
|
|
- setup
|
|
- command
|
|
- verify
|
|
- teardown
|
|
- post (post-suite)
|
|
|
|
|
|
To write a plugin, you need to inherit from TdcPlugin in
|
|
TdcPlugin.py. To use the plugin, you have to put the
|
|
implementation file in plugin-lib, and add a symbolic link to it from
|
|
plugins. It will be detected at run time and invoked at the
|
|
appropriate times. There are a few examples in the plugin-lib
|
|
directory:
|
|
|
|
- rootPlugin.py:
|
|
implements the enforcement of running as root
|
|
- nsPlugin.py:
|
|
sets up a network namespace and runs all commands in that namespace,
|
|
while also setting up dummy devices to be used in testing.
|
|
- valgrindPlugin.py
|
|
runs each command in the execute stage under valgrind,
|
|
and checks for leaks.
|
|
This plugin will output an extra test for each test in the test file,
|
|
one is the existing output as to whether the test passed or failed,
|
|
and the other is a test whether the command leaked memory or not.
|
|
(This one is a preliminary version, it may not work quite right yet,
|
|
but the overall template is there and it should only need tweaks.)
|
|
- buildebpfPlugin.py:
|
|
builds all programs in $EBPFDIR.
|
|
|
|
|
|
ACKNOWLEDGEMENTS
|
|
----------------
|
|
|
|
Thanks to:
|
|
|
|
Jamal Hadi Salim, for providing valuable test cases
|
|
Keara Leibovitz, who wrote the CLI test driver that I used as a base for the
|
|
first version of the tc testing suite. This work was presented at
|
|
Netdev 1.2 Tokyo in October 2016.
|
|
Samir Hussain, for providing help while I dove into Python for the first time
|
|
and being a second eye for this code.
|