1*4882a593Smuzhiyuntdc - Linux Traffic Control (tc) unit testing suite 2*4882a593Smuzhiyun 3*4882a593SmuzhiyunAuthor: Lucas Bates - lucasb@mojatatu.com 4*4882a593Smuzhiyun 5*4882a593Smuzhiyuntdc is a Python script to load tc unit tests from a separate JSON file and 6*4882a593Smuzhiyunexecute them inside a network namespace dedicated to the task. 7*4882a593Smuzhiyun 8*4882a593Smuzhiyun 9*4882a593SmuzhiyunREQUIREMENTS 10*4882a593Smuzhiyun------------ 11*4882a593Smuzhiyun 12*4882a593Smuzhiyun* Minimum Python version of 3.4. Earlier 3.X versions may work but are not 13*4882a593Smuzhiyun guaranteed. 14*4882a593Smuzhiyun 15*4882a593Smuzhiyun* The kernel must have network namespace support if using nsPlugin 16*4882a593Smuzhiyun 17*4882a593Smuzhiyun* The kernel must have veth support available, as a veth pair is created 18*4882a593Smuzhiyun prior to running the tests when using nsPlugin. 19*4882a593Smuzhiyun 20*4882a593Smuzhiyun* The kernel must have the appropriate infrastructure enabled to run all tdc 21*4882a593Smuzhiyun unit tests. See the config file in this directory for minimum required 22*4882a593Smuzhiyun features. As new tests will be added, config options list will be updated. 23*4882a593Smuzhiyun 24*4882a593Smuzhiyun* All tc-related features being tested must be built in or available as 25*4882a593Smuzhiyun modules. To check what is required in current setup run: 26*4882a593Smuzhiyun ./tdc.py -c 27*4882a593Smuzhiyun 28*4882a593Smuzhiyun Note: 29*4882a593Smuzhiyun In the current release, tdc run will abort due to a failure in setup or 30*4882a593Smuzhiyun teardown commands - which includes not being able to run a test simply 31*4882a593Smuzhiyun because the kernel did not support a specific feature. (This will be 32*4882a593Smuzhiyun handled in a future version - the current workaround is to run the tests 33*4882a593Smuzhiyun on specific test categories that your kernel supports) 34*4882a593Smuzhiyun 35*4882a593Smuzhiyun 36*4882a593SmuzhiyunBEFORE YOU RUN 37*4882a593Smuzhiyun-------------- 38*4882a593Smuzhiyun 39*4882a593SmuzhiyunThe path to the tc executable that will be most commonly tested can be defined 40*4882a593Smuzhiyunin the tdc_config.py file. Find the 'TC' entry in the NAMES dictionary and 41*4882a593Smuzhiyundefine the path. 42*4882a593Smuzhiyun 43*4882a593SmuzhiyunIf you need to test a different tc executable on the fly, you can do so by 44*4882a593Smuzhiyunusing the -p option when running tdc: 45*4882a593Smuzhiyun ./tdc.py -p /path/to/tc 46*4882a593Smuzhiyun 47*4882a593Smuzhiyun 48*4882a593SmuzhiyunRUNNING TDC 49*4882a593Smuzhiyun----------- 50*4882a593Smuzhiyun 51*4882a593SmuzhiyunTo use tdc, root privileges are required. This is because the 52*4882a593Smuzhiyuncommands being tested must be run as root. The code that enforces 53*4882a593Smuzhiyunexecution by root uid has been moved into a plugin (see PLUGIN 54*4882a593SmuzhiyunARCHITECTURE, below). 55*4882a593Smuzhiyun 56*4882a593SmuzhiyunTests that use a network device should have nsPlugin.py listed as a 57*4882a593Smuzhiyunrequirement for that test. nsPlugin executes all commands within a 58*4882a593Smuzhiyunnetwork namespace and creates a veth pair which may be used in those test 59*4882a593Smuzhiyuncases. To disable execution within the namespace, pass the -N option 60*4882a593Smuzhiyunto tdc when starting a test run; the veth pair will still be created 61*4882a593Smuzhiyunby the plugin. 62*4882a593Smuzhiyun 63*4882a593SmuzhiyunRunning tdc without any arguments will run all tests. Refer to the section 64*4882a593Smuzhiyunon command line arguments for more information, or run: 65*4882a593Smuzhiyun ./tdc.py -h 66*4882a593Smuzhiyun 67*4882a593Smuzhiyuntdc will list the test names as they are being run, and print a summary in 68*4882a593SmuzhiyunTAP (Test Anything Protocol) format when they are done. If tests fail, 69*4882a593Smuzhiyunoutput captured from the failing test will be printed immediately following 70*4882a593Smuzhiyunthe failed test in the TAP output. 71*4882a593Smuzhiyun 72*4882a593Smuzhiyun 73*4882a593SmuzhiyunOVERVIEW OF TDC EXECUTION 74*4882a593Smuzhiyun------------------------- 75*4882a593Smuzhiyun 76*4882a593SmuzhiyunOne run of tests is considered a "test suite" (this will be refined in the 77*4882a593Smuzhiyunfuture). A test suite has one or more test cases in it. 78*4882a593Smuzhiyun 79*4882a593SmuzhiyunA test case has four stages: 80*4882a593Smuzhiyun 81*4882a593Smuzhiyun - setup 82*4882a593Smuzhiyun - execute 83*4882a593Smuzhiyun - verify 84*4882a593Smuzhiyun - teardown 85*4882a593Smuzhiyun 86*4882a593SmuzhiyunThe setup and teardown stages can run zero or more commands. The setup 87*4882a593Smuzhiyunstage does some setup if the test needs it. The teardown stage undoes 88*4882a593Smuzhiyunthe setup and returns the system to a "neutral" state so any other test 89*4882a593Smuzhiyuncan be run next. These two stages require any commands run to return 90*4882a593Smuzhiyunsuccess, but do not otherwise verify the results. 91*4882a593Smuzhiyun 92*4882a593SmuzhiyunThe execute and verify stages each run one command. The execute stage 93*4882a593Smuzhiyuntests the return code against one or more acceptable values. The 94*4882a593Smuzhiyunverify stage checks the return code for success, and also compares 95*4882a593Smuzhiyunthe stdout with a regular expression. 96*4882a593Smuzhiyun 97*4882a593SmuzhiyunEach of the commands in any stage will run in a shell instance. 98*4882a593Smuzhiyun 99*4882a593Smuzhiyun 100*4882a593SmuzhiyunUSER-DEFINED CONSTANTS 101*4882a593Smuzhiyun---------------------- 102*4882a593Smuzhiyun 103*4882a593SmuzhiyunThe tdc_config.py file contains multiple values that can be altered to suit 104*4882a593Smuzhiyunyour needs. Any value in the NAMES dictionary can be altered without affecting 105*4882a593Smuzhiyunthe tests to be run. These values are used in the tc commands that will be 106*4882a593Smuzhiyunexecuted as part of the test. More will be added as test cases require. 107*4882a593Smuzhiyun 108*4882a593SmuzhiyunExample: 109*4882a593Smuzhiyun $TC qdisc add dev $DEV1 ingress 110*4882a593Smuzhiyun 111*4882a593SmuzhiyunThe NAMES values are used to substitute into the commands in the test cases. 112*4882a593Smuzhiyun 113*4882a593Smuzhiyun 114*4882a593SmuzhiyunCOMMAND LINE ARGUMENTS 115*4882a593Smuzhiyun---------------------- 116*4882a593Smuzhiyun 117*4882a593SmuzhiyunRun tdc.py -h to see the full list of available arguments. 118*4882a593Smuzhiyun 119*4882a593Smuzhiyunusage: tdc.py [-h] [-p PATH] [-D DIR [DIR ...]] [-f FILE [FILE ...]] 120*4882a593Smuzhiyun [-c [CATG [CATG ...]]] [-e ID [ID ...]] [-l] [-s] [-i] [-v] [-N] 121*4882a593Smuzhiyun [-d DEVICE] [-P] [-n] [-V] 122*4882a593Smuzhiyun 123*4882a593SmuzhiyunLinux TC unit tests 124*4882a593Smuzhiyun 125*4882a593Smuzhiyunoptional arguments: 126*4882a593Smuzhiyun -h, --help show this help message and exit 127*4882a593Smuzhiyun -p PATH, --path PATH The full path to the tc executable to use 128*4882a593Smuzhiyun -v, --verbose Show the commands that are being run 129*4882a593Smuzhiyun -N, --notap Suppress tap results for command under test 130*4882a593Smuzhiyun -d DEVICE, --device DEVICE 131*4882a593Smuzhiyun Execute test cases that use a physical device, where 132*4882a593Smuzhiyun DEVICE is its name. (If not defined, tests that require 133*4882a593Smuzhiyun a physical device will be skipped) 134*4882a593Smuzhiyun -P, --pause Pause execution just before post-suite stage 135*4882a593Smuzhiyun 136*4882a593Smuzhiyunselection: 137*4882a593Smuzhiyun select which test cases: files plus directories; filtered by categories 138*4882a593Smuzhiyun plus testids 139*4882a593Smuzhiyun 140*4882a593Smuzhiyun -D DIR [DIR ...], --directory DIR [DIR ...] 141*4882a593Smuzhiyun Collect tests from the specified directory(ies) 142*4882a593Smuzhiyun (default [tc-tests]) 143*4882a593Smuzhiyun -f FILE [FILE ...], --file FILE [FILE ...] 144*4882a593Smuzhiyun Run tests from the specified file(s) 145*4882a593Smuzhiyun -c [CATG [CATG ...]], --category [CATG [CATG ...]] 146*4882a593Smuzhiyun Run tests only from the specified category/ies, or if 147*4882a593Smuzhiyun no category/ies is/are specified, list known 148*4882a593Smuzhiyun categories. 149*4882a593Smuzhiyun -e ID [ID ...], --execute ID [ID ...] 150*4882a593Smuzhiyun Execute the specified test cases with specified IDs 151*4882a593Smuzhiyun 152*4882a593Smuzhiyunaction: 153*4882a593Smuzhiyun select action to perform on selected test cases 154*4882a593Smuzhiyun 155*4882a593Smuzhiyun -l, --list List all test cases, or those only within the 156*4882a593Smuzhiyun specified category 157*4882a593Smuzhiyun -s, --show Display the selected test cases 158*4882a593Smuzhiyun -i, --id Generate ID numbers for new test cases 159*4882a593Smuzhiyun 160*4882a593Smuzhiyunnetns: 161*4882a593Smuzhiyun options for nsPlugin (run commands in net namespace) 162*4882a593Smuzhiyun 163*4882a593Smuzhiyun -N, --no-namespace 164*4882a593Smuzhiyun Do not run commands in a network namespace. 165*4882a593Smuzhiyun 166*4882a593Smuzhiyunvalgrind: 167*4882a593Smuzhiyun options for valgrindPlugin (run command under test under Valgrind) 168*4882a593Smuzhiyun 169*4882a593Smuzhiyun -V, --valgrind Run commands under valgrind 170*4882a593Smuzhiyun 171*4882a593Smuzhiyun 172*4882a593SmuzhiyunPLUGIN ARCHITECTURE 173*4882a593Smuzhiyun------------------- 174*4882a593Smuzhiyun 175*4882a593SmuzhiyunThere is now a plugin architecture, and some of the functionality that 176*4882a593Smuzhiyunwas in the tdc.py script has been moved into the plugins. 177*4882a593Smuzhiyun 178*4882a593SmuzhiyunThe plugins are in the directory plugin-lib. The are executed from 179*4882a593Smuzhiyundirectory plugins. Put symbolic links from plugins to plugin-lib, 180*4882a593Smuzhiyunand name them according to the order you want them to run. This is not 181*4882a593Smuzhiyunnecessary if a test case being run requires a specific plugin to work. 182*4882a593Smuzhiyun 183*4882a593SmuzhiyunExample: 184*4882a593Smuzhiyun 185*4882a593Smuzhiyunbjb@bee:~/work/tc-testing$ ls -l plugins 186*4882a593Smuzhiyuntotal 4 187*4882a593Smuzhiyunlrwxrwxrwx 1 bjb bjb 27 Oct 4 16:12 10-rootPlugin.py -> ../plugin-lib/rootPlugin.py 188*4882a593Smuzhiyunlrwxrwxrwx 1 bjb bjb 25 Oct 12 17:55 20-nsPlugin.py -> ../plugin-lib/nsPlugin.py 189*4882a593Smuzhiyun-rwxr-xr-x 1 bjb bjb 0 Sep 29 15:56 __init__.py 190*4882a593Smuzhiyun 191*4882a593SmuzhiyunThe plugins are a subclass of TdcPlugin, defined in TdcPlugin.py and 192*4882a593Smuzhiyunmust be called "SubPlugin" so tdc can find them. They are 193*4882a593Smuzhiyundistinguished from each other in the python program by their module 194*4882a593Smuzhiyunname. 195*4882a593Smuzhiyun 196*4882a593SmuzhiyunThis base class supplies "hooks" to run extra functions. These hooks are as follows: 197*4882a593Smuzhiyun 198*4882a593Smuzhiyunpre- and post-suite 199*4882a593Smuzhiyunpre- and post-case 200*4882a593Smuzhiyunpre- and post-execute stage 201*4882a593Smuzhiyunadjust-command (runs in all stages and receives the stage name) 202*4882a593Smuzhiyun 203*4882a593SmuzhiyunThe pre-suite hook receives the number of tests and an array of test ids. 204*4882a593SmuzhiyunThis allows you to dump out the list of skipped tests in the event of a 205*4882a593Smuzhiyunfailure during setup or teardown stage. 206*4882a593Smuzhiyun 207*4882a593SmuzhiyunThe pre-case hook receives the ordinal number and test id of the current test. 208*4882a593Smuzhiyun 209*4882a593SmuzhiyunThe adjust-command hook receives the stage id (see list below) and the 210*4882a593Smuzhiyunfull command to be executed. This allows for last-minute adjustment 211*4882a593Smuzhiyunof the command. 212*4882a593Smuzhiyun 213*4882a593SmuzhiyunThe stages are identified by the following strings: 214*4882a593Smuzhiyun 215*4882a593Smuzhiyun - pre (pre-suite) 216*4882a593Smuzhiyun - setup 217*4882a593Smuzhiyun - command 218*4882a593Smuzhiyun - verify 219*4882a593Smuzhiyun - teardown 220*4882a593Smuzhiyun - post (post-suite) 221*4882a593Smuzhiyun 222*4882a593Smuzhiyun 223*4882a593SmuzhiyunTo write a plugin, you need to inherit from TdcPlugin in 224*4882a593SmuzhiyunTdcPlugin.py. To use the plugin, you have to put the 225*4882a593Smuzhiyunimplementation file in plugin-lib, and add a symbolic link to it from 226*4882a593Smuzhiyunplugins. It will be detected at run time and invoked at the 227*4882a593Smuzhiyunappropriate times. There are a few examples in the plugin-lib 228*4882a593Smuzhiyundirectory: 229*4882a593Smuzhiyun 230*4882a593Smuzhiyun - rootPlugin.py: 231*4882a593Smuzhiyun implements the enforcement of running as root 232*4882a593Smuzhiyun - nsPlugin.py: 233*4882a593Smuzhiyun sets up a network namespace and runs all commands in that namespace, 234*4882a593Smuzhiyun while also setting up dummy devices to be used in testing. 235*4882a593Smuzhiyun - valgrindPlugin.py 236*4882a593Smuzhiyun runs each command in the execute stage under valgrind, 237*4882a593Smuzhiyun and checks for leaks. 238*4882a593Smuzhiyun This plugin will output an extra test for each test in the test file, 239*4882a593Smuzhiyun one is the existing output as to whether the test passed or failed, 240*4882a593Smuzhiyun and the other is a test whether the command leaked memory or not. 241*4882a593Smuzhiyun (This one is a preliminary version, it may not work quite right yet, 242*4882a593Smuzhiyun but the overall template is there and it should only need tweaks.) 243*4882a593Smuzhiyun - buildebpfPlugin.py: 244*4882a593Smuzhiyun builds all programs in $EBPFDIR. 245*4882a593Smuzhiyun 246*4882a593Smuzhiyun 247*4882a593SmuzhiyunACKNOWLEDGEMENTS 248*4882a593Smuzhiyun---------------- 249*4882a593Smuzhiyun 250*4882a593SmuzhiyunThanks to: 251*4882a593Smuzhiyun 252*4882a593SmuzhiyunJamal Hadi Salim, for providing valuable test cases 253*4882a593SmuzhiyunKeara Leibovitz, who wrote the CLI test driver that I used as a base for the 254*4882a593Smuzhiyun first version of the tc testing suite. This work was presented at 255*4882a593Smuzhiyun Netdev 1.2 Tokyo in October 2016. 256*4882a593SmuzhiyunSamir Hussain, for providing help while I dove into Python for the first time 257*4882a593Smuzhiyun and being a second eye for this code. 258