blob: 4f7c76a69394eee2208d86102f4eb463379db6d1 [file] [log] [blame]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001#!/usr/bin/env python
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01003from __future__ import print_function
4import gc
5import sys
6import os
7import select
Damjan Marionf56b77a2016-10-03 19:44:57 +02008import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +02009import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020010import time
Klement Sekera3658adc2017-06-07 08:19:47 +020011import faulthandler
Klement Sekera6a6f4f72017-11-09 09:16:39 +010012import random
Dave Wallace42996c02018-02-26 14:40:13 -050013import copy
Klement Sekerae4504c62016-12-08 10:16:41 +010014from collections import deque
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010015from threading import Thread, Event
Klement Sekera909a6a12017-08-08 04:33:53 +020016from inspect import getdoc, isclass
Klement Sekerab91017a2017-02-09 06:04:36 +010017from traceback import format_exception
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010018from logging import FileHandler, DEBUG, Formatter
19from scapy.packet import Raw
Klement Sekera13a83ef2018-03-21 12:35:51 +010020from hook import StepHook, PollHook, VppDiedError
Klement Sekeraf62ae122016-10-11 11:47:09 +020021from vpp_pg_interface import VppPGInterface
Klement Sekeradab231a2016-12-21 08:50:14 +010022from vpp_sub_interface import VppSubInterface
Matej Klotton0178d522016-11-04 11:11:44 +010023from vpp_lo_interface import VppLoInterface
Klement Sekeraf62ae122016-10-11 11:47:09 +020024from vpp_papi_provider import VppPapiProvider
Klement Sekera05742262018-03-14 18:14:49 +010025from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
26 getLogger, colorize
Klement Sekera10db26f2017-01-11 08:16:53 +010027from vpp_object import VppObjectRegistry
Klement Sekera31da2e32018-06-24 22:49:55 +020028from util import ppp
Klement Sekerad81ae412018-05-16 10:52:54 +020029from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
30from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
31from scapy.layers.inet6 import ICMPv6EchoReply
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010032if os.name == 'posix' and sys.version_info[0] < 3:
33 # using subprocess32 is recommended by python official documentation
34 # @ https://docs.python.org/2/library/subprocess.html
35 import subprocess32 as subprocess
36else:
37 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020038
Klement Sekerad81ae412018-05-16 10:52:54 +020039
Klement Sekeraebbaf552018-02-17 13:41:33 +010040debug_framework = False
41if os.getenv('TEST_DEBUG', "0") == "1":
42 debug_framework = True
43 import debug_internal
44
45
Klement Sekeraf62ae122016-10-11 11:47:09 +020046"""
47 Test framework module.
48
49 The module provides a set of tools for constructing and running tests and
50 representing the results.
51"""
52
Klement Sekeraf62ae122016-10-11 11:47:09 +020053
Damjan Marionf56b77a2016-10-03 19:44:57 +020054class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020055 """Private class to create packet info object.
56
57 Help process information about the next packet.
58 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020059 """
Matej Klotton86d87c42016-11-11 11:38:55 +010060 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020061 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010062 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020063 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010064 #: Store the index of the destination packet generator interface
65 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020066 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010067 #: Store expected ip version
68 ip = -1
69 #: Store expected upper protocol
70 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010071 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020072 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020073
Matej Klotton16a14cd2016-12-07 15:09:13 +010074 def __eq__(self, other):
75 index = self.index == other.index
76 src = self.src == other.src
77 dst = self.dst == other.dst
78 data = self.data == other.data
79 return index and src and dst and data
80
Klement Sekeraf62ae122016-10-11 11:47:09 +020081
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010082def pump_output(testclass):
83 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +010084 stdout_fragment = ""
85 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -040086 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010087 readable = select.select([testclass.vpp.stdout.fileno(),
88 testclass.vpp.stderr.fileno(),
89 testclass.pump_thread_wakeup_pipe[0]],
90 [], [])[0]
91 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +010092 read = os.read(testclass.vpp.stdout.fileno(), 102400)
93 if len(read) > 0:
94 split = read.splitlines(True)
95 if len(stdout_fragment) > 0:
96 split[0] = "%s%s" % (stdout_fragment, split[0])
97 if len(split) > 0 and split[-1].endswith("\n"):
98 limit = None
99 else:
100 limit = -1
101 stdout_fragment = split[-1]
102 testclass.vpp_stdout_deque.extend(split[:limit])
103 if not testclass.cache_vpp_output:
104 for line in split[:limit]:
105 testclass.logger.debug(
106 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100107 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100108 read = os.read(testclass.vpp.stderr.fileno(), 102400)
109 if len(read) > 0:
110 split = read.splitlines(True)
111 if len(stderr_fragment) > 0:
112 split[0] = "%s%s" % (stderr_fragment, split[0])
113 if len(split) > 0 and split[-1].endswith("\n"):
114 limit = None
115 else:
116 limit = -1
117 stderr_fragment = split[-1]
118 testclass.vpp_stderr_deque.extend(split[:limit])
119 if not testclass.cache_vpp_output:
120 for line in split[:limit]:
121 testclass.logger.debug(
122 "VPP STDERR: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100123 # ignoring the dummy pipe here intentionally - the flag will take care
124 # of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200125
126
Klement Sekera87134932017-03-07 11:39:27 +0100127def running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100128 s = os.getenv("EXTENDED_TESTS", "n")
129 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100130
131
Klement Sekerad3e671e2017-09-29 12:36:37 +0200132def running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100133 os_id = os.getenv("OS_ID", "")
134 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200135
136
Klement Sekera909a6a12017-08-08 04:33:53 +0200137class KeepAliveReporter(object):
138 """
139 Singleton object which reports test start to parent process
140 """
141 _shared_state = {}
142
143 def __init__(self):
144 self.__dict__ = self._shared_state
145
146 @property
147 def pipe(self):
148 return self._pipe
149
150 @pipe.setter
151 def pipe(self, pipe):
152 if hasattr(self, '_pipe'):
153 raise Exception("Internal error - pipe should only be set once.")
154 self._pipe = pipe
155
156 def send_keep_alive(self, test):
157 """
158 Write current test tmpdir & desc to keep-alive pipe to signal liveness
159 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200160 if self.pipe is None:
161 # if not running forked..
162 return
163
Klement Sekera909a6a12017-08-08 04:33:53 +0200164 if isclass(test):
165 desc = test.__name__
166 else:
167 desc = test.shortDescription()
168 if not desc:
169 desc = str(test)
170
Dave Wallacee2efd122017-09-30 22:04:21 -0400171 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200172
173
Damjan Marionf56b77a2016-10-03 19:44:57 +0200174class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100175 """This subclass is a base class for VPP test cases that are implemented as
176 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200177 """
178
179 @property
180 def packet_infos(self):
181 """List of packet infos"""
182 return self._packet_infos
183
Klement Sekeradab231a2016-12-21 08:50:14 +0100184 @classmethod
185 def get_packet_count_for_if_idx(cls, dst_if_index):
186 """Get the number of packet info for specified destination if index"""
187 if dst_if_index in cls._packet_count_for_dst_if_idx:
188 return cls._packet_count_for_dst_if_idx[dst_if_index]
189 else:
190 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200191
192 @classmethod
193 def instance(cls):
194 """Return the instance of this testcase"""
195 return cls.test_instance
196
Damjan Marionf56b77a2016-10-03 19:44:57 +0200197 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200198 def set_debug_flags(cls, d):
199 cls.debug_core = False
200 cls.debug_gdb = False
201 cls.debug_gdbserver = False
202 if d is None:
203 return
204 dl = d.lower()
205 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200206 cls.debug_core = True
207 elif dl == "gdb":
208 cls.debug_gdb = True
209 elif dl == "gdbserver":
210 cls.debug_gdbserver = True
211 else:
212 raise Exception("Unrecognized DEBUG option: '%s'" % d)
213
214 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200215 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200216 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100217 s = os.getenv("STEP", "n")
218 cls.step = True if s.lower() in ("y", "yes", "1") else False
219 d = os.getenv("DEBUG", None)
220 c = os.getenv("CACHE_OUTPUT", "1")
221 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200222 cls.set_debug_flags(d)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200223 cls.vpp_bin = os.getenv('VPP_TEST_BIN', "vpp")
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100224 cls.plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100225 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
226 plugin_path = None
227 if cls.plugin_path is not None:
228 if cls.extern_plugin_path is not None:
229 plugin_path = "%s:%s" % (
230 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100231 else:
232 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100233 elif cls.extern_plugin_path is not None:
234 plugin_path = cls.extern_plugin_path
Klement Sekera01bbbe92016-11-02 09:25:05 +0100235 debug_cli = ""
236 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
237 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100238 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100239 size = os.getenv("COREDUMP_SIZE")
240 if size is not None:
241 coredump_size = "coredump-size %s" % size
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100242 if coredump_size is None:
Dave Wallacee2efd122017-09-30 22:04:21 -0400243 coredump_size = "coredump-size unlimited"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100244 cls.vpp_cmdline = [cls.vpp_bin, "unix",
Dave Wallacee2efd122017-09-30 22:04:21 -0400245 "{", "nodaemon", debug_cli, "full-coredump",
246 coredump_size, "}", "api-trace", "{", "on", "}",
Damjan Marion374e2c52017-03-09 20:38:15 +0100247 "api-segment", "{", "prefix", cls.shm_prefix, "}",
248 "plugins", "{", "plugin", "dpdk_plugin.so", "{",
Dave Barach8b5dc4f2018-07-23 18:00:54 -0400249 "disable", "}", "plugin", "unittest_plugin.so",
250 "{", "enable", "}", "}", ]
Klement Sekera47e275b2017-03-21 08:21:25 +0100251 if plugin_path is not None:
252 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekera277b89c2016-10-28 13:20:27 +0200253 cls.logger.info("vpp_cmdline: %s" % cls.vpp_cmdline)
254
255 @classmethod
256 def wait_for_enter(cls):
257 if cls.debug_gdbserver:
258 print(double_line_delim)
259 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
260 elif cls.debug_gdb:
261 print(double_line_delim)
262 print("Spawned VPP with PID: %d" % cls.vpp.pid)
263 else:
264 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
265 return
266 print(single_line_delim)
267 print("You can debug the VPP using e.g.:")
268 if cls.debug_gdbserver:
269 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
270 print("Now is the time to attach a gdb by running the above "
271 "command, set up breakpoints etc. and then resume VPP from "
272 "within gdb by issuing the 'continue' command")
273 elif cls.debug_gdb:
274 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
275 print("Now is the time to attach a gdb by running the above "
276 "command and set up breakpoints etc.")
277 print(single_line_delim)
Klement Sekerae1ace192018-03-23 21:54:12 +0100278 raw_input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200279
280 @classmethod
281 def run_vpp(cls):
282 cmdline = cls.vpp_cmdline
283
284 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100285 gdbserver = '/usr/bin/gdbserver'
286 if not os.path.isfile(gdbserver) or \
287 not os.access(gdbserver, os.X_OK):
288 raise Exception("gdbserver binary '%s' does not exist or is "
289 "not executable" % gdbserver)
290
291 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200292 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
293
Klement Sekera931be3a2016-11-03 05:36:01 +0100294 try:
295 cls.vpp = subprocess.Popen(cmdline,
296 stdout=subprocess.PIPE,
297 stderr=subprocess.PIPE,
298 bufsize=1)
299 except Exception as e:
300 cls.logger.critical("Couldn't start vpp: %s" % e)
301 raise
302
Klement Sekera277b89c2016-10-28 13:20:27 +0200303 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100304
Damjan Marionf56b77a2016-10-03 19:44:57 +0200305 @classmethod
306 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200307 """
308 Perform class setup before running the testcase
309 Remove shared memory files, start vpp and connect the vpp-api
310 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100311 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100312 random.seed()
Klement Sekera277b89c2016-10-28 13:20:27 +0200313 cls.logger = getLogger(cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200314 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200315 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera027dbd52017-04-11 06:01:53 +0200316 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
317 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100318 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
319 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200320 cls.file_handler.setLevel(DEBUG)
321 cls.logger.addHandler(cls.file_handler)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200322 cls.shm_prefix = cls.tempdir.split("/")[-1]
323 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200324 cls.logger.info("Temporary dir is %s, shm prefix is %s",
325 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200326 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100327 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100328 cls._captures = []
329 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200330 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100331 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100332 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200333 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200334 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200335 # need to catch exceptions here because if we raise, then the cleanup
336 # doesn't get called and we might end with a zombie vpp
337 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200338 cls.run_vpp()
Dave Wallacee2efd122017-09-30 22:04:21 -0400339 cls.reporter.send_keep_alive(cls)
Klement Sekerae4504c62016-12-08 10:16:41 +0100340 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100341 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100342 cls.pump_thread_stop_flag = Event()
343 cls.pump_thread_wakeup_pipe = os.pipe()
344 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100345 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100346 cls.pump_thread.start()
Klement Sekera7bb873a2016-11-18 07:38:42 +0100347 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100348 if cls.step:
349 hook = StepHook(cls)
350 else:
351 hook = PollHook(cls)
352 cls.vapi.register_hook(hook)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100353 cls.sleep(0.1, "after vpp startup, before initial poll")
Klement Sekera3747c752017-04-10 06:30:17 +0200354 try:
355 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100356 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200357 cls.vpp_startup_failed = True
358 cls.logger.critical(
359 "VPP died shortly after startup, check the"
360 " output to standard error for possible cause")
361 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100362 try:
363 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100364 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100365 try:
366 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100367 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100368 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100369 if cls.debug_gdbserver:
370 print(colorize("You're running VPP inside gdbserver but "
371 "VPP-API connection failed, did you forget "
372 "to 'continue' VPP from within gdb?", RED))
373 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100374 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100375 try:
376 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100377 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100378 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100379 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200380
Damjan Marionf56b77a2016-10-03 19:44:57 +0200381 @classmethod
382 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200383 """
384 Disconnect vpp-api, kill vpp and cleanup shared memory files
385 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200386 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
387 cls.vpp.poll()
388 if cls.vpp.returncode is None:
389 print(double_line_delim)
390 print("VPP or GDB server is still running")
391 print(single_line_delim)
Klement Sekerae1ace192018-03-23 21:54:12 +0100392 raw_input("When done debugging, press ENTER to kill the "
393 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200394
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100395 cls.pump_thread_stop_flag.set()
Neale Ranns16782362018-07-23 05:35:56 -0400396 os.write(cls.pump_thread_wakeup_pipe[1], 'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100397 if hasattr(cls, 'pump_thread'):
398 cls.logger.debug("Waiting for pump thread to stop")
399 cls.pump_thread.join()
400 if hasattr(cls, 'vpp_stderr_reader_thread'):
401 cls.logger.debug("Waiting for stdderr pump to stop")
402 cls.vpp_stderr_reader_thread.join()
403
Klement Sekeraf62ae122016-10-11 11:47:09 +0200404 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100405 if hasattr(cls, 'vapi'):
406 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100407 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200408 cls.vpp.poll()
409 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100410 cls.logger.debug("Sending TERM to vpp")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200411 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100412 cls.logger.debug("Waiting for vpp to die")
413 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200414 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200415
Klement Sekera3747c752017-04-10 06:30:17 +0200416 if cls.vpp_startup_failed:
417 stdout_log = cls.logger.info
418 stderr_log = cls.logger.critical
419 else:
420 stdout_log = cls.logger.info
421 stderr_log = cls.logger.info
422
Klement Sekerae4504c62016-12-08 10:16:41 +0100423 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200424 stdout_log(single_line_delim)
425 stdout_log('VPP output to stdout while running %s:', cls.__name__)
426 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100427 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200428 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
429 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200430 stdout_log('\n%s', vpp_output)
431 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200432
Klement Sekerae4504c62016-12-08 10:16:41 +0100433 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200434 stderr_log(single_line_delim)
435 stderr_log('VPP output to stderr while running %s:', cls.__name__)
436 stderr_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100437 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200438 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
439 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200440 stderr_log('\n%s', vpp_output)
441 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200442
Damjan Marionf56b77a2016-10-03 19:44:57 +0200443 @classmethod
444 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200445 """ Perform final cleanup after running all tests in this test-case """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200446 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200447 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100448 cls.reset_packet_infos()
449 if debug_framework:
450 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200451
Damjan Marionf56b77a2016-10-03 19:44:57 +0200452 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200453 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100454 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
455 (self.__class__.__name__, self._testMethodName,
456 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200457 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200458 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700459 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200460 self.logger.info(self.vapi.ppcli("show hardware"))
461 self.logger.info(self.vapi.ppcli("show error"))
462 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800463 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100464 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500465 # Save/Dump VPP api trace log
466 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
467 tmp_api_trace = "/tmp/%s" % api_trace
468 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
469 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
470 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
471 vpp_api_trace_log))
472 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500473 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500474 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100475 else:
476 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200477
Damjan Marionf56b77a2016-10-03 19:44:57 +0200478 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200479 """ Clear trace before running each test"""
Klement Sekera909a6a12017-08-08 04:33:53 +0200480 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100481 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
482 (self.__class__.__name__, self._testMethodName,
483 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100484 if self.vpp_dead:
485 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100486 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100487 self.vpp_stdout_deque.append(
488 "--- test setUp() for %s.%s(%s) starts here ---\n" %
489 (self.__class__.__name__, self._testMethodName,
490 self._testMethodDoc))
491 self.vpp_stderr_deque.append(
492 "--- test setUp() for %s.%s(%s) starts here ---\n" %
493 (self.__class__.__name__, self._testMethodName,
494 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200495 self.vapi.cli("clear trace")
496 # store the test instance inside the test class - so that objects
497 # holding the class can access instance methods (like assertEqual)
498 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200499
Damjan Marionf56b77a2016-10-03 19:44:57 +0200500 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200501 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200502 """
503 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200504
Klement Sekera75e7d132017-09-20 08:26:30 +0200505 :param interfaces: iterable interface indexes (if None,
506 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200507
Klement Sekeraf62ae122016-10-11 11:47:09 +0200508 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200509 if interfaces is None:
510 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200511 for i in interfaces:
512 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200513
Damjan Marionf56b77a2016-10-03 19:44:57 +0200514 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100515 def register_capture(cls, cap_name):
516 """ Register a capture in the testclass """
517 # add to the list of captures with current timestamp
518 cls._captures.append((time.time(), cap_name))
519 # filter out from zombies
520 cls._zombie_captures = [(stamp, name)
521 for (stamp, name) in cls._zombie_captures
522 if name != cap_name]
523
524 @classmethod
525 def pg_start(cls):
526 """ Remove any zombie captures and enable the packet generator """
527 # how long before capture is allowed to be deleted - otherwise vpp
528 # crashes - 100ms seems enough (this shouldn't be needed at all)
529 capture_ttl = 0.1
530 now = time.time()
531 for stamp, cap_name in cls._zombie_captures:
532 wait = stamp + capture_ttl - now
533 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100534 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100535 now = time.time()
536 cls.logger.debug("Removing zombie capture %s" % cap_name)
537 cls.vapi.cli('packet-generator delete %s' % cap_name)
538
Klement Sekeraf62ae122016-10-11 11:47:09 +0200539 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
540 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100541 cls._zombie_captures = cls._captures
542 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200543
Damjan Marionf56b77a2016-10-03 19:44:57 +0200544 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200545 def create_pg_interfaces(cls, interfaces):
546 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100547 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200548
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100549 :param interfaces: iterable indexes of the interfaces.
550 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200551
Klement Sekeraf62ae122016-10-11 11:47:09 +0200552 """
553 result = []
554 for i in interfaces:
555 intf = VppPGInterface(cls, i)
556 setattr(cls, intf.name, intf)
557 result.append(intf)
558 cls.pg_interfaces = result
559 return result
560
Matej Klotton0178d522016-11-04 11:11:44 +0100561 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200562 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100563 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100564 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100565
Klement Sekerab9ef2732018-06-24 22:49:33 +0200566 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100567 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100568 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200569 result = [VppLoInterface(cls) for i in range(count)]
570 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100571 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100572 cls.lo_interfaces = result
573 return result
574
Damjan Marionf56b77a2016-10-03 19:44:57 +0200575 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200576 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200577 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200578 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200579 NOTE: Currently works only when Raw layer is present.
580
581 :param packet: packet
582 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200583 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200584
585 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200586 packet_len = len(packet) + 4
587 extend = size - packet_len
588 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200589 num = (extend / len(padding)) + 1
590 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200591
Klement Sekeradab231a2016-12-21 08:50:14 +0100592 @classmethod
593 def reset_packet_infos(cls):
594 """ Reset the list of packet info objects and packet counts to zero """
595 cls._packet_infos = {}
596 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200597
Klement Sekeradab231a2016-12-21 08:50:14 +0100598 @classmethod
599 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200600 """
601 Create packet info object containing the source and destination indexes
602 and add it to the testcase's packet info list
603
Klement Sekeradab231a2016-12-21 08:50:14 +0100604 :param VppInterface src_if: source interface
605 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200606
607 :returns: _PacketInfo object
608
609 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200610 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100611 info.index = len(cls._packet_infos)
612 info.src = src_if.sw_if_index
613 info.dst = dst_if.sw_if_index
614 if isinstance(dst_if, VppSubInterface):
615 dst_idx = dst_if.parent.sw_if_index
616 else:
617 dst_idx = dst_if.sw_if_index
618 if dst_idx in cls._packet_count_for_dst_if_idx:
619 cls._packet_count_for_dst_if_idx[dst_idx] += 1
620 else:
621 cls._packet_count_for_dst_if_idx[dst_idx] = 1
622 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200623 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200624
Damjan Marionf56b77a2016-10-03 19:44:57 +0200625 @staticmethod
626 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200627 """
628 Convert _PacketInfo object to packet payload
629
630 :param info: _PacketInfo object
631
632 :returns: string containing serialized data from packet info
633 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100634 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
635 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200636
Damjan Marionf56b77a2016-10-03 19:44:57 +0200637 @staticmethod
638 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200639 """
640 Convert packet payload to _PacketInfo object
641
642 :param payload: packet payload
643
644 :returns: _PacketInfo object containing de-serialized data from payload
645
646 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200647 numbers = payload.split()
648 info = _PacketInfo()
649 info.index = int(numbers[0])
650 info.src = int(numbers[1])
651 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100652 info.ip = int(numbers[3])
653 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200654 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200655
Damjan Marionf56b77a2016-10-03 19:44:57 +0200656 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200657 """
658 Iterate over the packet info list stored in the testcase
659 Start iteration with first element if info is None
660 Continue based on index in info if info is specified
661
662 :param info: info or None
663 :returns: next info in list or None if no more infos
664 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200665 if info is None:
666 next_index = 0
667 else:
668 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100669 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200670 return None
671 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100672 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200673
Klement Sekeraf62ae122016-10-11 11:47:09 +0200674 def get_next_packet_info_for_interface(self, src_index, info):
675 """
676 Search the packet info list for the next packet info with same source
677 interface index
678
679 :param src_index: source interface index to search for
680 :param info: packet info - where to start the search
681 :returns: packet info or None
682
683 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200684 while True:
685 info = self.get_next_packet_info(info)
686 if info is None:
687 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200688 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200689 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200690
Klement Sekeraf62ae122016-10-11 11:47:09 +0200691 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
692 """
693 Search the packet info list for the next packet info with same source
694 and destination interface indexes
695
696 :param src_index: source interface index to search for
697 :param dst_index: destination interface index to search for
698 :param info: packet info - where to start the search
699 :returns: packet info or None
700
701 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200702 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200703 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200704 if info is None:
705 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200706 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200707 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200708
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200709 def assert_equal(self, real_value, expected_value, name_or_class=None):
710 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100711 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200712 return
713 try:
714 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
715 msg = msg % (getdoc(name_or_class).strip(),
716 real_value, str(name_or_class(real_value)),
717 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100718 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200719 msg = "Invalid %s: %s does not match expected value %s" % (
720 name_or_class, real_value, expected_value)
721
722 self.assertEqual(real_value, expected_value, msg)
723
Klement Sekerab17dd962017-01-09 07:43:48 +0100724 def assert_in_range(self,
725 real_value,
726 expected_min,
727 expected_max,
728 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200729 if name is None:
730 msg = None
731 else:
732 msg = "Invalid %s: %s out of range <%s,%s>" % (
733 name, real_value, expected_min, expected_max)
734 self.assertTrue(expected_min <= real_value <= expected_max, msg)
735
Klement Sekerad81ae412018-05-16 10:52:54 +0200736 def assert_packet_checksums_valid(self, packet,
737 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200738 received = packet.__class__(str(packet))
739 self.logger.debug(
740 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200741 udp_layers = ['UDP', 'UDPerror']
742 checksum_fields = ['cksum', 'chksum']
743 checksums = []
744 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200745 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200746 while True:
747 layer = temp.getlayer(counter)
748 if layer:
749 for cf in checksum_fields:
750 if hasattr(layer, cf):
751 if ignore_zero_udp_checksums and \
752 0 == getattr(layer, cf) and \
753 layer.name in udp_layers:
754 continue
755 delattr(layer, cf)
756 checksums.append((counter, cf))
757 else:
758 break
759 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200760 if 0 == len(checksums):
761 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200762 temp = temp.__class__(str(temp))
763 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200764 calc_sum = getattr(temp[layer], cf)
765 self.assert_equal(
766 getattr(received[layer], cf), calc_sum,
767 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
768 self.logger.debug(
769 "Checksum field `%s` on `%s` layer has correct value `%s`" %
770 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200771
772 def assert_checksum_valid(self, received_packet, layer,
773 field_name='chksum',
774 ignore_zero_checksum=False):
775 """ Check checksum of received packet on given layer """
776 received_packet_checksum = getattr(received_packet[layer], field_name)
777 if ignore_zero_checksum and 0 == received_packet_checksum:
778 return
779 recalculated = received_packet.__class__(str(received_packet))
780 delattr(recalculated[layer], field_name)
781 recalculated = recalculated.__class__(str(recalculated))
782 self.assert_equal(received_packet_checksum,
783 getattr(recalculated[layer], field_name),
784 "packet checksum on layer: %s" % layer)
785
786 def assert_ip_checksum_valid(self, received_packet,
787 ignore_zero_checksum=False):
788 self.assert_checksum_valid(received_packet, 'IP',
789 ignore_zero_checksum=ignore_zero_checksum)
790
791 def assert_tcp_checksum_valid(self, received_packet,
792 ignore_zero_checksum=False):
793 self.assert_checksum_valid(received_packet, 'TCP',
794 ignore_zero_checksum=ignore_zero_checksum)
795
796 def assert_udp_checksum_valid(self, received_packet,
797 ignore_zero_checksum=True):
798 self.assert_checksum_valid(received_packet, 'UDP',
799 ignore_zero_checksum=ignore_zero_checksum)
800
801 def assert_embedded_icmp_checksum_valid(self, received_packet):
802 if received_packet.haslayer(IPerror):
803 self.assert_checksum_valid(received_packet, 'IPerror')
804 if received_packet.haslayer(TCPerror):
805 self.assert_checksum_valid(received_packet, 'TCPerror')
806 if received_packet.haslayer(UDPerror):
807 self.assert_checksum_valid(received_packet, 'UDPerror',
808 ignore_zero_checksum=True)
809 if received_packet.haslayer(ICMPerror):
810 self.assert_checksum_valid(received_packet, 'ICMPerror')
811
812 def assert_icmp_checksum_valid(self, received_packet):
813 self.assert_checksum_valid(received_packet, 'ICMP')
814 self.assert_embedded_icmp_checksum_valid(received_packet)
815
816 def assert_icmpv6_checksum_valid(self, pkt):
817 if pkt.haslayer(ICMPv6DestUnreach):
818 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
819 self.assert_embedded_icmp_checksum_valid(pkt)
820 if pkt.haslayer(ICMPv6EchoRequest):
821 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
822 if pkt.haslayer(ICMPv6EchoReply):
823 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
824
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100825 @classmethod
826 def sleep(cls, timeout, remark=None):
827 if hasattr(cls, 'logger'):
Klement Sekera3cfa5582017-04-19 07:10:58 +0000828 cls.logger.debug("Starting sleep for %ss (%s)" % (timeout, remark))
829 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100830 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000831 after = time.time()
832 if after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200833 cls.logger.error("unexpected time.sleep() result - "
834 "slept for %ss instead of ~%ss!" % (
835 after - before, timeout))
Klement Sekera3cfa5582017-04-19 07:10:58 +0000836 if hasattr(cls, 'logger'):
837 cls.logger.debug(
838 "Finished sleep (%s) - slept %ss (wanted %ss)" % (
839 remark, after - before, timeout))
Klement Sekeraa57a9702017-02-02 06:58:07 +0100840
Neale Ranns947ea622018-06-07 23:48:20 -0700841 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800842 self.vapi.cli("clear trace")
843 intf.add_stream(pkts)
844 self.pg_enable_capture(self.pg_interfaces)
845 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700846 if not timeout:
847 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800848 for i in self.pg_interfaces:
849 i.get_capture(0, timeout=timeout)
850 i.assert_nothing_captured(remark=remark)
851 timeout = 0.1
852
853 def send_and_expect(self, input, pkts, output):
854 self.vapi.cli("clear trace")
855 input.add_stream(pkts)
856 self.pg_enable_capture(self.pg_interfaces)
857 self.pg_start()
858 rx = output.get_capture(len(pkts))
859 return rx
860
Damjan Marionf56b77a2016-10-03 19:44:57 +0200861
Klement Sekera87134932017-03-07 11:39:27 +0100862class TestCasePrinter(object):
863 _shared_state = {}
864
865 def __init__(self):
866 self.__dict__ = self._shared_state
867 if not hasattr(self, "_test_case_set"):
868 self._test_case_set = set()
869
870 def print_test_case_heading_if_first_time(self, case):
871 if case.__class__ not in self._test_case_set:
872 print(double_line_delim)
873 print(colorize(getdoc(case.__class__).splitlines()[0], YELLOW))
874 print(double_line_delim)
875 self._test_case_set.add(case.__class__)
876
877
Damjan Marionf56b77a2016-10-03 19:44:57 +0200878class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200879 """
880 @property result_string
881 String variable to store the test case result string.
882 @property errors
883 List variable containing 2-tuples of TestCase instances and strings
884 holding formatted tracebacks. Each tuple represents a test which
885 raised an unexpected exception.
886 @property failures
887 List variable containing 2-tuples of TestCase instances and strings
888 holding formatted tracebacks. Each tuple represents a test where
889 a failure was explicitly signalled using the TestCase.assert*()
890 methods.
891 """
892
Damjan Marionf56b77a2016-10-03 19:44:57 +0200893 def __init__(self, stream, descriptions, verbosity):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200894 """
Klement Sekerada505f62017-01-04 12:58:53 +0100895 :param stream File descriptor to store where to report test results.
896 Set to the standard error stream by default.
897 :param descriptions Boolean variable to store information if to use
898 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200899 :param verbosity Integer variable to store required verbosity level.
900 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200901 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
902 self.stream = stream
903 self.descriptions = descriptions
904 self.verbosity = verbosity
905 self.result_string = None
Klement Sekera87134932017-03-07 11:39:27 +0100906 self.printer = TestCasePrinter()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200907
Damjan Marionf56b77a2016-10-03 19:44:57 +0200908 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200909 """
910 Record a test succeeded result
911
912 :param test:
913
914 """
Klement Sekerab91017a2017-02-09 06:04:36 +0100915 if hasattr(test, 'logger'):
916 test.logger.debug("--- addSuccess() %s.%s(%s) called"
917 % (test.__class__.__name__,
918 test._testMethodName,
919 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200920 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +0200921 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200922
Klement Sekeraf62ae122016-10-11 11:47:09 +0200923 def addSkip(self, test, reason):
924 """
925 Record a test skipped.
926
927 :param test:
928 :param reason:
929
930 """
Klement Sekerab91017a2017-02-09 06:04:36 +0100931 if hasattr(test, 'logger'):
932 test.logger.debug("--- addSkip() %s.%s(%s) called, reason is %s"
933 % (test.__class__.__name__,
934 test._testMethodName,
935 test._testMethodDoc,
936 reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200937 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +0200938 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200939
Klement Sekeraf413bef2017-08-15 07:09:02 +0200940 def symlink_failed(self, test):
941 logger = None
942 if hasattr(test, 'logger'):
943 logger = test.logger
944 if hasattr(test, 'tempdir'):
945 try:
946 failed_dir = os.getenv('VPP_TEST_FAILED_DIR')
947 link_path = '%s/%s-FAILED' % (failed_dir,
948 test.tempdir.split("/")[-1])
949 if logger:
950 logger.debug("creating a link to the failed test")
951 logger.debug("os.symlink(%s, %s)" %
952 (test.tempdir, link_path))
953 os.symlink(test.tempdir, link_path)
954 except Exception as e:
955 if logger:
956 logger.error(e)
957
Klement Sekeradf2b9802017-10-05 10:26:03 +0200958 def send_failure_through_pipe(self, test):
959 if hasattr(self, 'test_framework_failed_pipe'):
960 pipe = self.test_framework_failed_pipe
961 if pipe:
Klement Sekera4c5422e2018-06-22 13:19:45 +0200962 if test.__class__.__name__ == "_ErrorHolder":
963 x = str(test)
964 if x.startswith("setUpClass"):
965 # x looks like setUpClass (test_function.test_class)
966 cls = x.split(".")[1].split(")")[0]
967 for t in self.test_suite:
968 if t.__class__.__name__ == cls:
969 pipe.send(t.__class__)
970 break
971 else:
972 raise Exception("Can't find class name `%s' "
973 "(from ErrorHolder) in test suite "
974 "`%s'" % (cls, self.test_suite))
975 else:
976 raise Exception("FIXME: unexpected special case - "
977 "ErrorHolder description is `%s'" %
978 str(test))
979 else:
980 pipe.send(test.__class__)
Klement Sekeradf2b9802017-10-05 10:26:03 +0200981
Damjan Marionf56b77a2016-10-03 19:44:57 +0200982 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200983 """
984 Record a test failed result
985
986 :param test:
987 :param err: error message
988
989 """
Klement Sekerab91017a2017-02-09 06:04:36 +0100990 if hasattr(test, 'logger'):
991 test.logger.debug("--- addFailure() %s.%s(%s) called, err is %s"
992 % (test.__class__.__name__,
993 test._testMethodName,
994 test._testMethodDoc, err))
995 test.logger.debug("formatted exception is:\n%s" %
996 "".join(format_exception(*err)))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200997 unittest.TestResult.addFailure(self, test, err)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200998 if hasattr(test, 'tempdir'):
Klement Sekera277b89c2016-10-28 13:20:27 +0200999 self.result_string = colorize("FAIL", RED) + \
Klement Sekeraf62ae122016-10-11 11:47:09 +02001000 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
Klement Sekeraf413bef2017-08-15 07:09:02 +02001001 self.symlink_failed(test)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001002 else:
Klement Sekera277b89c2016-10-28 13:20:27 +02001003 self.result_string = colorize("FAIL", RED) + ' [no temp dir]'
Damjan Marionf56b77a2016-10-03 19:44:57 +02001004
Klement Sekeradf2b9802017-10-05 10:26:03 +02001005 self.send_failure_through_pipe(test)
1006
Damjan Marionf56b77a2016-10-03 19:44:57 +02001007 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001008 """
1009 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001010
Klement Sekeraf62ae122016-10-11 11:47:09 +02001011 :param test:
1012 :param err: error message
1013
1014 """
Klement Sekerab91017a2017-02-09 06:04:36 +01001015 if hasattr(test, 'logger'):
1016 test.logger.debug("--- addError() %s.%s(%s) called, err is %s"
1017 % (test.__class__.__name__,
1018 test._testMethodName,
1019 test._testMethodDoc, err))
1020 test.logger.debug("formatted exception is:\n%s" %
1021 "".join(format_exception(*err)))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001022 unittest.TestResult.addError(self, test, err)
1023 if hasattr(test, 'tempdir'):
Klement Sekera277b89c2016-10-28 13:20:27 +02001024 self.result_string = colorize("ERROR", RED) + \
Klement Sekeraf62ae122016-10-11 11:47:09 +02001025 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
Klement Sekeraf413bef2017-08-15 07:09:02 +02001026 self.symlink_failed(test)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001027 else:
Klement Sekera277b89c2016-10-28 13:20:27 +02001028 self.result_string = colorize("ERROR", RED) + ' [no temp dir]'
Klement Sekeraf62ae122016-10-11 11:47:09 +02001029
Klement Sekeradf2b9802017-10-05 10:26:03 +02001030 self.send_failure_through_pipe(test)
1031
Damjan Marionf56b77a2016-10-03 19:44:57 +02001032 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001033 """
1034 Get test description
1035
1036 :param test:
1037 :returns: test description
1038
1039 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001040 # TODO: if none print warning not raise exception
1041 short_description = test.shortDescription()
1042 if self.descriptions and short_description:
1043 return short_description
1044 else:
1045 return str(test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001046
Damjan Marionf56b77a2016-10-03 19:44:57 +02001047 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001048 """
1049 Start a test
1050
1051 :param test:
1052
1053 """
Klement Sekera87134932017-03-07 11:39:27 +01001054 self.printer.print_test_case_heading_if_first_time(test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001055 unittest.TestResult.startTest(self, test)
1056 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001057 self.stream.writeln(
1058 "Starting " + self.getDescription(test) + " ...")
1059 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001060
Damjan Marionf56b77a2016-10-03 19:44:57 +02001061 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001062 """
1063 Stop a test
1064
1065 :param test:
1066
1067 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001068 unittest.TestResult.stopTest(self, test)
1069 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001070 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001071 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001072 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001073 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001074 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001075 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001076 self.result_string))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001077
Damjan Marionf56b77a2016-10-03 19:44:57 +02001078 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001079 """
1080 Print errors from running the test case
1081 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001082 self.stream.writeln()
1083 self.printErrorList('ERROR', self.errors)
1084 self.printErrorList('FAIL', self.failures)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001085
Damjan Marionf56b77a2016-10-03 19:44:57 +02001086 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001087 """
1088 Print error list to the output stream together with error type
1089 and test case description.
1090
1091 :param flavour: error type
1092 :param errors: iterable errors
1093
1094 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001095 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001096 self.stream.writeln(double_line_delim)
1097 self.stream.writeln("%s: %s" %
1098 (flavour, self.getDescription(test)))
1099 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001100 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001101
1102
Klement Sekeradf2b9802017-10-05 10:26:03 +02001103class Filter_by_test_option:
1104 def __init__(self, filter_file_name, filter_class_name, filter_func_name):
1105 self.filter_file_name = filter_file_name
1106 self.filter_class_name = filter_class_name
1107 self.filter_func_name = filter_func_name
1108
1109 def __call__(self, file_name, class_name, func_name):
1110 if self.filter_file_name and file_name != self.filter_file_name:
1111 return False
1112 if self.filter_class_name and class_name != self.filter_class_name:
1113 return False
1114 if self.filter_func_name and func_name != self.filter_func_name:
1115 return False
1116 return True
1117
1118
Damjan Marionf56b77a2016-10-03 19:44:57 +02001119class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001120 """
Klement Sekera104543f2017-02-03 07:29:43 +01001121 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001122 """
1123 @property
1124 def resultclass(self):
1125 """Class maintaining the results of the tests"""
1126 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001127
Klement Sekeradf2b9802017-10-05 10:26:03 +02001128 def __init__(self, keep_alive_pipe=None, failed_pipe=None,
1129 stream=sys.stderr, descriptions=True,
Klement Sekera3f6ff192017-08-11 06:56:05 +02001130 verbosity=1, failfast=False, buffer=False, resultclass=None):
Klement Sekera7a161da2017-01-17 13:42:48 +01001131 # ignore stream setting here, use hard-coded stdout to be in sync
1132 # with prints from VppTestCase methods ...
1133 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1134 verbosity, failfast, buffer,
1135 resultclass)
Klement Sekera909a6a12017-08-08 04:33:53 +02001136 reporter = KeepAliveReporter()
Klement Sekeradf2b9802017-10-05 10:26:03 +02001137 reporter.pipe = keep_alive_pipe
1138 # this is super-ugly, but very simple to implement and works as long
1139 # as we run only one test at the same time
1140 VppTestResult.test_framework_failed_pipe = failed_pipe
Klement Sekera7a161da2017-01-17 13:42:48 +01001141
Klement Sekera104543f2017-02-03 07:29:43 +01001142 test_option = "TEST"
1143
1144 def parse_test_option(self):
Klement Sekera13a83ef2018-03-21 12:35:51 +01001145 f = os.getenv(self.test_option, None)
Klement Sekera104543f2017-02-03 07:29:43 +01001146 filter_file_name = None
1147 filter_class_name = None
1148 filter_func_name = None
1149 if f:
1150 if '.' in f:
1151 parts = f.split('.')
1152 if len(parts) > 3:
1153 raise Exception("Unrecognized %s option: %s" %
1154 (self.test_option, f))
1155 if len(parts) > 2:
1156 if parts[2] not in ('*', ''):
1157 filter_func_name = parts[2]
1158 if parts[1] not in ('*', ''):
1159 filter_class_name = parts[1]
1160 if parts[0] not in ('*', ''):
1161 if parts[0].startswith('test_'):
1162 filter_file_name = parts[0]
1163 else:
1164 filter_file_name = 'test_%s' % parts[0]
1165 else:
1166 if f.startswith('test_'):
1167 filter_file_name = f
1168 else:
1169 filter_file_name = 'test_%s' % f
1170 return filter_file_name, filter_class_name, filter_func_name
1171
Klement Sekeradf2b9802017-10-05 10:26:03 +02001172 @staticmethod
1173 def filter_tests(tests, filter_cb):
Klement Sekera104543f2017-02-03 07:29:43 +01001174 result = unittest.suite.TestSuite()
1175 for t in tests:
1176 if isinstance(t, unittest.suite.TestSuite):
1177 # this is a bunch of tests, recursively filter...
Klement Sekera05742262018-03-14 18:14:49 +01001178 x = VppTestRunner.filter_tests(t, filter_cb)
Klement Sekera104543f2017-02-03 07:29:43 +01001179 if x.countTestCases() > 0:
1180 result.addTest(x)
1181 elif isinstance(t, unittest.TestCase):
1182 # this is a single test
1183 parts = t.id().split('.')
1184 # t.id() for common cases like this:
1185 # test_classifier.TestClassifier.test_acl_ip
1186 # apply filtering only if it is so
1187 if len(parts) == 3:
Klement Sekeradf2b9802017-10-05 10:26:03 +02001188 if not filter_cb(parts[0], parts[1], parts[2]):
Klement Sekera104543f2017-02-03 07:29:43 +01001189 continue
1190 result.addTest(t)
1191 else:
1192 # unexpected object, don't touch it
1193 result.addTest(t)
1194 return result
1195
Damjan Marionf56b77a2016-10-03 19:44:57 +02001196 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001197 """
1198 Run the tests
1199
1200 :param test:
1201
1202 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001203 faulthandler.enable() # emit stack trace to stderr if killed by signal
Klement Sekeraf62ae122016-10-11 11:47:09 +02001204 print("Running tests using custom test runner") # debug message
Klement Sekera104543f2017-02-03 07:29:43 +01001205 filter_file, filter_class, filter_func = self.parse_test_option()
1206 print("Active filters: file=%s, class=%s, function=%s" % (
1207 filter_file, filter_class, filter_func))
Klement Sekeradf2b9802017-10-05 10:26:03 +02001208 filter_cb = Filter_by_test_option(
1209 filter_file, filter_class, filter_func)
1210 filtered = self.filter_tests(test, filter_cb)
Klement Sekera104543f2017-02-03 07:29:43 +01001211 print("%s out of %s tests match specified filters" % (
1212 filtered.countTestCases(), test.countTestCases()))
Klement Sekera3747c752017-04-10 06:30:17 +02001213 if not running_extended_tests():
1214 print("Not running extended tests (some tests will be skipped)")
Klement Sekera4c5422e2018-06-22 13:19:45 +02001215 # super-ugly hack #2
1216 VppTestResult.test_suite = filtered
Klement Sekera104543f2017-02-03 07:29:43 +01001217 return super(VppTestRunner, self).run(filtered)
Neale Ranns812ed392017-10-16 04:20:13 -07001218
1219
1220class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001221 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001222 self.logger = logger
1223 self.args = args
1224 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001225 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001226 super(Worker, self).__init__()
1227
1228 def run(self):
1229 executable = self.args[0]
1230 self.logger.debug("Running executable w/args `%s'" % self.args)
1231 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001232 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001233 env["CK_LOG_FILE_NAME"] = "-"
1234 self.process = subprocess.Popen(
1235 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1236 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1237 out, err = self.process.communicate()
1238 self.logger.debug("Finished running `%s'" % executable)
1239 self.logger.info("Return code is `%s'" % self.process.returncode)
1240 self.logger.info(single_line_delim)
1241 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1242 self.logger.info(single_line_delim)
1243 self.logger.info(out)
1244 self.logger.info(single_line_delim)
1245 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1246 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001247 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001248 self.logger.info(single_line_delim)
1249 self.result = self.process.returncode