blob: f7a155f41cb203803f82e58fbb89cc5e67c0fb89 [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
juraj.linkes184870a2018-07-16 14:22:01 +020014import psutil
Klement Sekerae4504c62016-12-08 10:16:41 +010015from collections import deque
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010016from threading import Thread, Event
Klement Sekera909a6a12017-08-08 04:33:53 +020017from inspect import getdoc, isclass
Klement Sekerab91017a2017-02-09 06:04:36 +010018from traceback import format_exception
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010019from logging import FileHandler, DEBUG, Formatter
20from scapy.packet import Raw
Klement Sekera13a83ef2018-03-21 12:35:51 +010021from hook import StepHook, PollHook, VppDiedError
Klement Sekeraf62ae122016-10-11 11:47:09 +020022from vpp_pg_interface import VppPGInterface
Klement Sekeradab231a2016-12-21 08:50:14 +010023from vpp_sub_interface import VppSubInterface
Matej Klotton0178d522016-11-04 11:11:44 +010024from vpp_lo_interface import VppLoInterface
Klement Sekeraf62ae122016-10-11 11:47:09 +020025from vpp_papi_provider import VppPapiProvider
Klement Sekera05742262018-03-14 18:14:49 +010026from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
27 getLogger, colorize
Klement Sekera10db26f2017-01-11 08:16:53 +010028from vpp_object import VppObjectRegistry
Klement Sekera31da2e32018-06-24 22:49:55 +020029from util import ppp
Klement Sekerad81ae412018-05-16 10:52:54 +020030from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
31from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
32from scapy.layers.inet6 import ICMPv6EchoReply
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010033if os.name == 'posix' and sys.version_info[0] < 3:
34 # using subprocess32 is recommended by python official documentation
35 # @ https://docs.python.org/2/library/subprocess.html
36 import subprocess32 as subprocess
37else:
38 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020039
Klement Sekerad81ae412018-05-16 10:52:54 +020040
Klement Sekeraebbaf552018-02-17 13:41:33 +010041debug_framework = False
42if os.getenv('TEST_DEBUG', "0") == "1":
43 debug_framework = True
44 import debug_internal
45
46
Klement Sekeraf62ae122016-10-11 11:47:09 +020047"""
48 Test framework module.
49
50 The module provides a set of tools for constructing and running tests and
51 representing the results.
52"""
53
Klement Sekeraf62ae122016-10-11 11:47:09 +020054
Damjan Marionf56b77a2016-10-03 19:44:57 +020055class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020056 """Private class to create packet info object.
57
58 Help process information about the next packet.
59 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020060 """
Matej Klotton86d87c42016-11-11 11:38:55 +010061 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020062 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010063 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020064 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010065 #: Store the index of the destination packet generator interface
66 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020067 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010068 #: Store expected ip version
69 ip = -1
70 #: Store expected upper protocol
71 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010072 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020073 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020074
Matej Klotton16a14cd2016-12-07 15:09:13 +010075 def __eq__(self, other):
76 index = self.index == other.index
77 src = self.src == other.src
78 dst = self.dst == other.dst
79 data = self.data == other.data
80 return index and src and dst and data
81
Klement Sekeraf62ae122016-10-11 11:47:09 +020082
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010083def pump_output(testclass):
84 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +010085 stdout_fragment = ""
86 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -040087 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010088 readable = select.select([testclass.vpp.stdout.fileno(),
89 testclass.vpp.stderr.fileno(),
90 testclass.pump_thread_wakeup_pipe[0]],
91 [], [])[0]
92 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +010093 read = os.read(testclass.vpp.stdout.fileno(), 102400)
94 if len(read) > 0:
95 split = read.splitlines(True)
96 if len(stdout_fragment) > 0:
97 split[0] = "%s%s" % (stdout_fragment, split[0])
98 if len(split) > 0 and split[-1].endswith("\n"):
99 limit = None
100 else:
101 limit = -1
102 stdout_fragment = split[-1]
103 testclass.vpp_stdout_deque.extend(split[:limit])
104 if not testclass.cache_vpp_output:
105 for line in split[:limit]:
106 testclass.logger.debug(
107 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100108 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100109 read = os.read(testclass.vpp.stderr.fileno(), 102400)
110 if len(read) > 0:
111 split = read.splitlines(True)
112 if len(stderr_fragment) > 0:
113 split[0] = "%s%s" % (stderr_fragment, split[0])
114 if len(split) > 0 and split[-1].endswith("\n"):
115 limit = None
116 else:
117 limit = -1
118 stderr_fragment = split[-1]
119 testclass.vpp_stderr_deque.extend(split[:limit])
120 if not testclass.cache_vpp_output:
121 for line in split[:limit]:
122 testclass.logger.debug(
123 "VPP STDERR: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100124 # ignoring the dummy pipe here intentionally - the flag will take care
125 # of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200126
127
Klement Sekera87134932017-03-07 11:39:27 +0100128def running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100129 s = os.getenv("EXTENDED_TESTS", "n")
130 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100131
132
Klement Sekerad3e671e2017-09-29 12:36:37 +0200133def running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100134 os_id = os.getenv("OS_ID", "")
135 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200136
137
Klement Sekera909a6a12017-08-08 04:33:53 +0200138class KeepAliveReporter(object):
139 """
140 Singleton object which reports test start to parent process
141 """
142 _shared_state = {}
143
144 def __init__(self):
145 self.__dict__ = self._shared_state
146
147 @property
148 def pipe(self):
149 return self._pipe
150
151 @pipe.setter
152 def pipe(self, pipe):
153 if hasattr(self, '_pipe'):
154 raise Exception("Internal error - pipe should only be set once.")
155 self._pipe = pipe
156
157 def send_keep_alive(self, test):
158 """
159 Write current test tmpdir & desc to keep-alive pipe to signal liveness
160 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200161 if self.pipe is None:
162 # if not running forked..
163 return
164
Klement Sekera909a6a12017-08-08 04:33:53 +0200165 if isclass(test):
166 desc = test.__name__
167 else:
168 desc = test.shortDescription()
169 if not desc:
170 desc = str(test)
171
Dave Wallacee2efd122017-09-30 22:04:21 -0400172 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200173
174
Damjan Marionf56b77a2016-10-03 19:44:57 +0200175class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100176 """This subclass is a base class for VPP test cases that are implemented as
177 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200178 """
179
180 @property
181 def packet_infos(self):
182 """List of packet infos"""
183 return self._packet_infos
184
Klement Sekeradab231a2016-12-21 08:50:14 +0100185 @classmethod
186 def get_packet_count_for_if_idx(cls, dst_if_index):
187 """Get the number of packet info for specified destination if index"""
188 if dst_if_index in cls._packet_count_for_dst_if_idx:
189 return cls._packet_count_for_dst_if_idx[dst_if_index]
190 else:
191 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200192
193 @classmethod
194 def instance(cls):
195 """Return the instance of this testcase"""
196 return cls.test_instance
197
Damjan Marionf56b77a2016-10-03 19:44:57 +0200198 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200199 def set_debug_flags(cls, d):
200 cls.debug_core = False
201 cls.debug_gdb = False
202 cls.debug_gdbserver = False
203 if d is None:
204 return
205 dl = d.lower()
206 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200207 cls.debug_core = True
208 elif dl == "gdb":
209 cls.debug_gdb = True
210 elif dl == "gdbserver":
211 cls.debug_gdbserver = True
212 else:
213 raise Exception("Unrecognized DEBUG option: '%s'" % d)
214
215 @classmethod
juraj.linkes184870a2018-07-16 14:22:01 +0200216 def get_least_used_cpu(self):
217 cpu_usage_list = [set(range(psutil.cpu_count()))]
218 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
219 if 'vpp_main' == p.info['name']]
220 for vpp_process in vpp_processes:
221 for cpu_usage_set in cpu_usage_list:
222 try:
223 cpu_num = vpp_process.cpu_num()
224 if cpu_num in cpu_usage_set:
225 cpu_usage_set_index = cpu_usage_list.index(
226 cpu_usage_set)
227 if cpu_usage_set_index == len(cpu_usage_list) - 1:
228 cpu_usage_list.append({cpu_num})
229 else:
230 cpu_usage_list[cpu_usage_set_index + 1].add(
231 cpu_num)
232 cpu_usage_set.remove(cpu_num)
233 break
234 except psutil.NoSuchProcess:
235 pass
236
237 for cpu_usage_set in cpu_usage_list:
238 if len(cpu_usage_set) > 0:
239 min_usage_set = cpu_usage_set
240 break
241
242 return random.choice(tuple(min_usage_set))
243
244 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200245 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200246 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100247 s = os.getenv("STEP", "n")
248 cls.step = True if s.lower() in ("y", "yes", "1") else False
249 d = os.getenv("DEBUG", None)
250 c = os.getenv("CACHE_OUTPUT", "1")
251 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200252 cls.set_debug_flags(d)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200253 cls.vpp_bin = os.getenv('VPP_TEST_BIN', "vpp")
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100254 cls.plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100255 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
256 plugin_path = None
257 if cls.plugin_path is not None:
258 if cls.extern_plugin_path is not None:
259 plugin_path = "%s:%s" % (
260 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100261 else:
262 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100263 elif cls.extern_plugin_path is not None:
264 plugin_path = cls.extern_plugin_path
Klement Sekera01bbbe92016-11-02 09:25:05 +0100265 debug_cli = ""
266 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
267 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100268 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100269 size = os.getenv("COREDUMP_SIZE")
270 if size is not None:
271 coredump_size = "coredump-size %s" % size
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100272 if coredump_size is None:
Dave Wallacee2efd122017-09-30 22:04:21 -0400273 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200274
275 cpu_core_number = cls.get_least_used_cpu()
276
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100277 cls.vpp_cmdline = [cls.vpp_bin, "unix",
Dave Wallacee2efd122017-09-30 22:04:21 -0400278 "{", "nodaemon", debug_cli, "full-coredump",
279 coredump_size, "}", "api-trace", "{", "on", "}",
Damjan Marion374e2c52017-03-09 20:38:15 +0100280 "api-segment", "{", "prefix", cls.shm_prefix, "}",
juraj.linkes184870a2018-07-16 14:22:01 +0200281 "cpu", "{", "main-core", str(cpu_core_number), "}",
Damjan Marion374e2c52017-03-09 20:38:15 +0100282 "plugins", "{", "plugin", "dpdk_plugin.so", "{",
Dave Barach8b5dc4f2018-07-23 18:00:54 -0400283 "disable", "}", "plugin", "unittest_plugin.so",
284 "{", "enable", "}", "}", ]
Klement Sekera47e275b2017-03-21 08:21:25 +0100285 if plugin_path is not None:
286 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekera277b89c2016-10-28 13:20:27 +0200287 cls.logger.info("vpp_cmdline: %s" % cls.vpp_cmdline)
288
289 @classmethod
290 def wait_for_enter(cls):
291 if cls.debug_gdbserver:
292 print(double_line_delim)
293 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
294 elif cls.debug_gdb:
295 print(double_line_delim)
296 print("Spawned VPP with PID: %d" % cls.vpp.pid)
297 else:
298 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
299 return
300 print(single_line_delim)
301 print("You can debug the VPP using e.g.:")
302 if cls.debug_gdbserver:
303 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
304 print("Now is the time to attach a gdb by running the above "
305 "command, set up breakpoints etc. and then resume VPP from "
306 "within gdb by issuing the 'continue' command")
307 elif cls.debug_gdb:
308 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
309 print("Now is the time to attach a gdb by running the above "
310 "command and set up breakpoints etc.")
311 print(single_line_delim)
Klement Sekerae1ace192018-03-23 21:54:12 +0100312 raw_input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200313
314 @classmethod
315 def run_vpp(cls):
316 cmdline = cls.vpp_cmdline
317
318 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100319 gdbserver = '/usr/bin/gdbserver'
320 if not os.path.isfile(gdbserver) or \
321 not os.access(gdbserver, os.X_OK):
322 raise Exception("gdbserver binary '%s' does not exist or is "
323 "not executable" % gdbserver)
324
325 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200326 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
327
Klement Sekera931be3a2016-11-03 05:36:01 +0100328 try:
329 cls.vpp = subprocess.Popen(cmdline,
330 stdout=subprocess.PIPE,
331 stderr=subprocess.PIPE,
332 bufsize=1)
333 except Exception as e:
334 cls.logger.critical("Couldn't start vpp: %s" % e)
335 raise
336
Klement Sekera277b89c2016-10-28 13:20:27 +0200337 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100338
Damjan Marionf56b77a2016-10-03 19:44:57 +0200339 @classmethod
340 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200341 """
342 Perform class setup before running the testcase
343 Remove shared memory files, start vpp and connect the vpp-api
344 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100345 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100346 random.seed()
juraj.linkes184870a2018-07-16 14:22:01 +0200347 if not hasattr(cls, 'logger'):
348 cls.logger = getLogger(cls.__name__)
349 else:
350 cls.logger.name = cls.__name__
Klement Sekeraf62ae122016-10-11 11:47:09 +0200351 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200352 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera027dbd52017-04-11 06:01:53 +0200353 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
354 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100355 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
356 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200357 cls.file_handler.setLevel(DEBUG)
358 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200359 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200360 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200361 cls.logger.info("Temporary dir is %s, shm prefix is %s",
362 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200363 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100364 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100365 cls._captures = []
366 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200367 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100368 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100369 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200370 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200371 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200372 # need to catch exceptions here because if we raise, then the cleanup
373 # doesn't get called and we might end with a zombie vpp
374 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200375 cls.run_vpp()
Dave Wallacee2efd122017-09-30 22:04:21 -0400376 cls.reporter.send_keep_alive(cls)
Klement Sekerae4504c62016-12-08 10:16:41 +0100377 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100378 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100379 cls.pump_thread_stop_flag = Event()
380 cls.pump_thread_wakeup_pipe = os.pipe()
381 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100382 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100383 cls.pump_thread.start()
Klement Sekera7bb873a2016-11-18 07:38:42 +0100384 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100385 if cls.step:
386 hook = StepHook(cls)
387 else:
388 hook = PollHook(cls)
389 cls.vapi.register_hook(hook)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100390 cls.sleep(0.1, "after vpp startup, before initial poll")
Klement Sekera3747c752017-04-10 06:30:17 +0200391 try:
392 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100393 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200394 cls.vpp_startup_failed = True
395 cls.logger.critical(
396 "VPP died shortly after startup, check the"
397 " output to standard error for possible cause")
398 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100399 try:
400 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100401 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100402 try:
403 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100404 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100405 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100406 if cls.debug_gdbserver:
407 print(colorize("You're running VPP inside gdbserver but "
408 "VPP-API connection failed, did you forget "
409 "to 'continue' VPP from within gdb?", RED))
410 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100411 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100412 try:
413 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100414 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100415 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100416 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200417
Damjan Marionf56b77a2016-10-03 19:44:57 +0200418 @classmethod
419 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200420 """
421 Disconnect vpp-api, kill vpp and cleanup shared memory files
422 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200423 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
424 cls.vpp.poll()
425 if cls.vpp.returncode is None:
426 print(double_line_delim)
427 print("VPP or GDB server is still running")
428 print(single_line_delim)
Klement Sekerae1ace192018-03-23 21:54:12 +0100429 raw_input("When done debugging, press ENTER to kill the "
430 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200431
juraj.linkes184870a2018-07-16 14:22:01 +0200432 # first signal that we want to stop the pump thread, then wake it up
433 if hasattr(cls, 'pump_thread_stop_flag'):
434 cls.pump_thread_stop_flag.set()
435 if hasattr(cls, 'pump_thread_wakeup_pipe'):
436 os.write(cls.pump_thread_wakeup_pipe[1], 'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100437 if hasattr(cls, 'pump_thread'):
438 cls.logger.debug("Waiting for pump thread to stop")
439 cls.pump_thread.join()
440 if hasattr(cls, 'vpp_stderr_reader_thread'):
441 cls.logger.debug("Waiting for stdderr pump to stop")
442 cls.vpp_stderr_reader_thread.join()
443
Klement Sekeraf62ae122016-10-11 11:47:09 +0200444 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100445 if hasattr(cls, 'vapi'):
446 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100447 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200448 cls.vpp.poll()
449 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100450 cls.logger.debug("Sending TERM to vpp")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200451 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100452 cls.logger.debug("Waiting for vpp to die")
453 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200454 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200455
Klement Sekera3747c752017-04-10 06:30:17 +0200456 if cls.vpp_startup_failed:
457 stdout_log = cls.logger.info
458 stderr_log = cls.logger.critical
459 else:
460 stdout_log = cls.logger.info
461 stderr_log = cls.logger.info
462
Klement Sekerae4504c62016-12-08 10:16:41 +0100463 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200464 stdout_log(single_line_delim)
465 stdout_log('VPP output to stdout while running %s:', cls.__name__)
466 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100467 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200468 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
469 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200470 stdout_log('\n%s', vpp_output)
471 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200472
Klement Sekerae4504c62016-12-08 10:16:41 +0100473 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200474 stderr_log(single_line_delim)
475 stderr_log('VPP output to stderr while running %s:', cls.__name__)
476 stderr_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100477 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200478 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
479 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200480 stderr_log('\n%s', vpp_output)
481 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200482
Damjan Marionf56b77a2016-10-03 19:44:57 +0200483 @classmethod
484 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200485 """ Perform final cleanup after running all tests in this test-case """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200486 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200487 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100488 cls.reset_packet_infos()
489 if debug_framework:
490 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200491
Damjan Marionf56b77a2016-10-03 19:44:57 +0200492 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200493 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100494 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
495 (self.__class__.__name__, self._testMethodName,
496 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200497 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200498 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700499 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200500 self.logger.info(self.vapi.ppcli("show hardware"))
501 self.logger.info(self.vapi.ppcli("show error"))
502 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800503 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100504 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500505 # Save/Dump VPP api trace log
506 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
507 tmp_api_trace = "/tmp/%s" % api_trace
508 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
509 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
510 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
511 vpp_api_trace_log))
512 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500513 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500514 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100515 else:
516 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200517
Damjan Marionf56b77a2016-10-03 19:44:57 +0200518 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200519 """ Clear trace before running each test"""
Klement Sekera909a6a12017-08-08 04:33:53 +0200520 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100521 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
522 (self.__class__.__name__, self._testMethodName,
523 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100524 if self.vpp_dead:
525 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100526 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100527 self.vpp_stdout_deque.append(
528 "--- test setUp() for %s.%s(%s) starts here ---\n" %
529 (self.__class__.__name__, self._testMethodName,
530 self._testMethodDoc))
531 self.vpp_stderr_deque.append(
532 "--- test setUp() for %s.%s(%s) starts here ---\n" %
533 (self.__class__.__name__, self._testMethodName,
534 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200535 self.vapi.cli("clear trace")
536 # store the test instance inside the test class - so that objects
537 # holding the class can access instance methods (like assertEqual)
538 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200539
Damjan Marionf56b77a2016-10-03 19:44:57 +0200540 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200541 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200542 """
543 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200544
Klement Sekera75e7d132017-09-20 08:26:30 +0200545 :param interfaces: iterable interface indexes (if None,
546 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200547
Klement Sekeraf62ae122016-10-11 11:47:09 +0200548 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200549 if interfaces is None:
550 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200551 for i in interfaces:
552 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200553
Damjan Marionf56b77a2016-10-03 19:44:57 +0200554 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100555 def register_capture(cls, cap_name):
556 """ Register a capture in the testclass """
557 # add to the list of captures with current timestamp
558 cls._captures.append((time.time(), cap_name))
559 # filter out from zombies
560 cls._zombie_captures = [(stamp, name)
561 for (stamp, name) in cls._zombie_captures
562 if name != cap_name]
563
564 @classmethod
565 def pg_start(cls):
566 """ Remove any zombie captures and enable the packet generator """
567 # how long before capture is allowed to be deleted - otherwise vpp
568 # crashes - 100ms seems enough (this shouldn't be needed at all)
569 capture_ttl = 0.1
570 now = time.time()
571 for stamp, cap_name in cls._zombie_captures:
572 wait = stamp + capture_ttl - now
573 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100574 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100575 now = time.time()
576 cls.logger.debug("Removing zombie capture %s" % cap_name)
577 cls.vapi.cli('packet-generator delete %s' % cap_name)
578
Klement Sekeraf62ae122016-10-11 11:47:09 +0200579 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
580 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100581 cls._zombie_captures = cls._captures
582 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200583
Damjan Marionf56b77a2016-10-03 19:44:57 +0200584 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200585 def create_pg_interfaces(cls, interfaces):
586 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100587 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200588
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100589 :param interfaces: iterable indexes of the interfaces.
590 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200591
Klement Sekeraf62ae122016-10-11 11:47:09 +0200592 """
593 result = []
594 for i in interfaces:
595 intf = VppPGInterface(cls, i)
596 setattr(cls, intf.name, intf)
597 result.append(intf)
598 cls.pg_interfaces = result
599 return result
600
Matej Klotton0178d522016-11-04 11:11:44 +0100601 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200602 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100603 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100604 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100605
Klement Sekerab9ef2732018-06-24 22:49:33 +0200606 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100607 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100608 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200609 result = [VppLoInterface(cls) for i in range(count)]
610 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100611 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100612 cls.lo_interfaces = result
613 return result
614
Damjan Marionf56b77a2016-10-03 19:44:57 +0200615 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200616 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200617 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200618 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200619 NOTE: Currently works only when Raw layer is present.
620
621 :param packet: packet
622 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200623 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200624
625 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200626 packet_len = len(packet) + 4
627 extend = size - packet_len
628 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200629 num = (extend / len(padding)) + 1
630 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200631
Klement Sekeradab231a2016-12-21 08:50:14 +0100632 @classmethod
633 def reset_packet_infos(cls):
634 """ Reset the list of packet info objects and packet counts to zero """
635 cls._packet_infos = {}
636 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200637
Klement Sekeradab231a2016-12-21 08:50:14 +0100638 @classmethod
639 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200640 """
641 Create packet info object containing the source and destination indexes
642 and add it to the testcase's packet info list
643
Klement Sekeradab231a2016-12-21 08:50:14 +0100644 :param VppInterface src_if: source interface
645 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200646
647 :returns: _PacketInfo object
648
649 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200650 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100651 info.index = len(cls._packet_infos)
652 info.src = src_if.sw_if_index
653 info.dst = dst_if.sw_if_index
654 if isinstance(dst_if, VppSubInterface):
655 dst_idx = dst_if.parent.sw_if_index
656 else:
657 dst_idx = dst_if.sw_if_index
658 if dst_idx in cls._packet_count_for_dst_if_idx:
659 cls._packet_count_for_dst_if_idx[dst_idx] += 1
660 else:
661 cls._packet_count_for_dst_if_idx[dst_idx] = 1
662 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200663 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200664
Damjan Marionf56b77a2016-10-03 19:44:57 +0200665 @staticmethod
666 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200667 """
668 Convert _PacketInfo object to packet payload
669
670 :param info: _PacketInfo object
671
672 :returns: string containing serialized data from packet info
673 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100674 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
675 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200676
Damjan Marionf56b77a2016-10-03 19:44:57 +0200677 @staticmethod
678 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200679 """
680 Convert packet payload to _PacketInfo object
681
682 :param payload: packet payload
683
684 :returns: _PacketInfo object containing de-serialized data from payload
685
686 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200687 numbers = payload.split()
688 info = _PacketInfo()
689 info.index = int(numbers[0])
690 info.src = int(numbers[1])
691 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100692 info.ip = int(numbers[3])
693 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200694 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200695
Damjan Marionf56b77a2016-10-03 19:44:57 +0200696 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200697 """
698 Iterate over the packet info list stored in the testcase
699 Start iteration with first element if info is None
700 Continue based on index in info if info is specified
701
702 :param info: info or None
703 :returns: next info in list or None if no more infos
704 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200705 if info is None:
706 next_index = 0
707 else:
708 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100709 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200710 return None
711 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100712 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200713
Klement Sekeraf62ae122016-10-11 11:47:09 +0200714 def get_next_packet_info_for_interface(self, src_index, info):
715 """
716 Search the packet info list for the next packet info with same source
717 interface index
718
719 :param src_index: source interface index to search for
720 :param info: packet info - where to start the search
721 :returns: packet info or None
722
723 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200724 while True:
725 info = self.get_next_packet_info(info)
726 if info is None:
727 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200728 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200729 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200730
Klement Sekeraf62ae122016-10-11 11:47:09 +0200731 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
732 """
733 Search the packet info list for the next packet info with same source
734 and destination interface indexes
735
736 :param src_index: source interface index to search for
737 :param dst_index: destination interface index to search for
738 :param info: packet info - where to start the search
739 :returns: packet info or None
740
741 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200742 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200743 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200744 if info is None:
745 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200746 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200747 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200748
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200749 def assert_equal(self, real_value, expected_value, name_or_class=None):
750 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100751 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200752 return
753 try:
754 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
755 msg = msg % (getdoc(name_or_class).strip(),
756 real_value, str(name_or_class(real_value)),
757 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100758 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200759 msg = "Invalid %s: %s does not match expected value %s" % (
760 name_or_class, real_value, expected_value)
761
762 self.assertEqual(real_value, expected_value, msg)
763
Klement Sekerab17dd962017-01-09 07:43:48 +0100764 def assert_in_range(self,
765 real_value,
766 expected_min,
767 expected_max,
768 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200769 if name is None:
770 msg = None
771 else:
772 msg = "Invalid %s: %s out of range <%s,%s>" % (
773 name, real_value, expected_min, expected_max)
774 self.assertTrue(expected_min <= real_value <= expected_max, msg)
775
Klement Sekerad81ae412018-05-16 10:52:54 +0200776 def assert_packet_checksums_valid(self, packet,
777 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200778 received = packet.__class__(str(packet))
779 self.logger.debug(
780 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200781 udp_layers = ['UDP', 'UDPerror']
782 checksum_fields = ['cksum', 'chksum']
783 checksums = []
784 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200785 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200786 while True:
787 layer = temp.getlayer(counter)
788 if layer:
789 for cf in checksum_fields:
790 if hasattr(layer, cf):
791 if ignore_zero_udp_checksums and \
792 0 == getattr(layer, cf) and \
793 layer.name in udp_layers:
794 continue
795 delattr(layer, cf)
796 checksums.append((counter, cf))
797 else:
798 break
799 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200800 if 0 == len(checksums):
801 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200802 temp = temp.__class__(str(temp))
803 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200804 calc_sum = getattr(temp[layer], cf)
805 self.assert_equal(
806 getattr(received[layer], cf), calc_sum,
807 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
808 self.logger.debug(
809 "Checksum field `%s` on `%s` layer has correct value `%s`" %
810 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200811
812 def assert_checksum_valid(self, received_packet, layer,
813 field_name='chksum',
814 ignore_zero_checksum=False):
815 """ Check checksum of received packet on given layer """
816 received_packet_checksum = getattr(received_packet[layer], field_name)
817 if ignore_zero_checksum and 0 == received_packet_checksum:
818 return
819 recalculated = received_packet.__class__(str(received_packet))
820 delattr(recalculated[layer], field_name)
821 recalculated = recalculated.__class__(str(recalculated))
822 self.assert_equal(received_packet_checksum,
823 getattr(recalculated[layer], field_name),
824 "packet checksum on layer: %s" % layer)
825
826 def assert_ip_checksum_valid(self, received_packet,
827 ignore_zero_checksum=False):
828 self.assert_checksum_valid(received_packet, 'IP',
829 ignore_zero_checksum=ignore_zero_checksum)
830
831 def assert_tcp_checksum_valid(self, received_packet,
832 ignore_zero_checksum=False):
833 self.assert_checksum_valid(received_packet, 'TCP',
834 ignore_zero_checksum=ignore_zero_checksum)
835
836 def assert_udp_checksum_valid(self, received_packet,
837 ignore_zero_checksum=True):
838 self.assert_checksum_valid(received_packet, 'UDP',
839 ignore_zero_checksum=ignore_zero_checksum)
840
841 def assert_embedded_icmp_checksum_valid(self, received_packet):
842 if received_packet.haslayer(IPerror):
843 self.assert_checksum_valid(received_packet, 'IPerror')
844 if received_packet.haslayer(TCPerror):
845 self.assert_checksum_valid(received_packet, 'TCPerror')
846 if received_packet.haslayer(UDPerror):
847 self.assert_checksum_valid(received_packet, 'UDPerror',
848 ignore_zero_checksum=True)
849 if received_packet.haslayer(ICMPerror):
850 self.assert_checksum_valid(received_packet, 'ICMPerror')
851
852 def assert_icmp_checksum_valid(self, received_packet):
853 self.assert_checksum_valid(received_packet, 'ICMP')
854 self.assert_embedded_icmp_checksum_valid(received_packet)
855
856 def assert_icmpv6_checksum_valid(self, pkt):
857 if pkt.haslayer(ICMPv6DestUnreach):
858 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
859 self.assert_embedded_icmp_checksum_valid(pkt)
860 if pkt.haslayer(ICMPv6EchoRequest):
861 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
862 if pkt.haslayer(ICMPv6EchoReply):
863 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
864
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100865 @classmethod
866 def sleep(cls, timeout, remark=None):
867 if hasattr(cls, 'logger'):
Klement Sekera3cfa5582017-04-19 07:10:58 +0000868 cls.logger.debug("Starting sleep for %ss (%s)" % (timeout, remark))
869 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100870 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000871 after = time.time()
872 if after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200873 cls.logger.error("unexpected time.sleep() result - "
874 "slept for %ss instead of ~%ss!" % (
875 after - before, timeout))
Klement Sekera3cfa5582017-04-19 07:10:58 +0000876 if hasattr(cls, 'logger'):
877 cls.logger.debug(
878 "Finished sleep (%s) - slept %ss (wanted %ss)" % (
879 remark, after - before, timeout))
Klement Sekeraa57a9702017-02-02 06:58:07 +0100880
Neale Ranns947ea622018-06-07 23:48:20 -0700881 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800882 self.vapi.cli("clear trace")
883 intf.add_stream(pkts)
884 self.pg_enable_capture(self.pg_interfaces)
885 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700886 if not timeout:
887 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800888 for i in self.pg_interfaces:
889 i.get_capture(0, timeout=timeout)
890 i.assert_nothing_captured(remark=remark)
891 timeout = 0.1
892
893 def send_and_expect(self, input, pkts, output):
894 self.vapi.cli("clear trace")
895 input.add_stream(pkts)
896 self.pg_enable_capture(self.pg_interfaces)
897 self.pg_start()
898 rx = output.get_capture(len(pkts))
899 return rx
900
Damjan Marionf56b77a2016-10-03 19:44:57 +0200901
juraj.linkes184870a2018-07-16 14:22:01 +0200902def get_testcase_doc_name(test):
903 return getdoc(test.__class__).splitlines()[0]
904
905
906def get_test_description(descriptions, test):
907 # TODO: if none print warning not raise exception
908 short_description = test.shortDescription()
909 if descriptions and short_description:
910 return short_description
911 else:
912 return str(test)
913
914
Klement Sekera87134932017-03-07 11:39:27 +0100915class TestCasePrinter(object):
916 _shared_state = {}
917
918 def __init__(self):
919 self.__dict__ = self._shared_state
920 if not hasattr(self, "_test_case_set"):
921 self._test_case_set = set()
922
923 def print_test_case_heading_if_first_time(self, case):
924 if case.__class__ not in self._test_case_set:
925 print(double_line_delim)
juraj.linkes184870a2018-07-16 14:22:01 +0200926 print(colorize(get_testcase_doc_name(case), GREEN))
Klement Sekera87134932017-03-07 11:39:27 +0100927 print(double_line_delim)
928 self._test_case_set.add(case.__class__)
929
930
Damjan Marionf56b77a2016-10-03 19:44:57 +0200931class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200932 """
933 @property result_string
934 String variable to store the test case result string.
935 @property errors
936 List variable containing 2-tuples of TestCase instances and strings
937 holding formatted tracebacks. Each tuple represents a test which
938 raised an unexpected exception.
939 @property failures
940 List variable containing 2-tuples of TestCase instances and strings
941 holding formatted tracebacks. Each tuple represents a test where
942 a failure was explicitly signalled using the TestCase.assert*()
943 methods.
944 """
945
Damjan Marionf56b77a2016-10-03 19:44:57 +0200946 def __init__(self, stream, descriptions, verbosity):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200947 """
Klement Sekerada505f62017-01-04 12:58:53 +0100948 :param stream File descriptor to store where to report test results.
949 Set to the standard error stream by default.
950 :param descriptions Boolean variable to store information if to use
951 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200952 :param verbosity Integer variable to store required verbosity level.
953 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200954 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
955 self.stream = stream
956 self.descriptions = descriptions
957 self.verbosity = verbosity
958 self.result_string = None
Klement Sekera87134932017-03-07 11:39:27 +0100959 self.printer = TestCasePrinter()
juraj.linkes0219b8d2018-08-24 16:16:28 +0200960 self.passed = 0
Damjan Marionf56b77a2016-10-03 19:44:57 +0200961
Damjan Marionf56b77a2016-10-03 19:44:57 +0200962 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200963 """
964 Record a test succeeded result
965
966 :param test:
967
968 """
Klement Sekerab91017a2017-02-09 06:04:36 +0100969 if hasattr(test, 'logger'):
970 test.logger.debug("--- addSuccess() %s.%s(%s) called"
971 % (test.__class__.__name__,
972 test._testMethodName,
973 test._testMethodDoc))
juraj.linkes0219b8d2018-08-24 16:16:28 +0200974 self.passed += 1
Damjan Marionf56b77a2016-10-03 19:44:57 +0200975 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +0200976 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200977
Klement Sekeraf62ae122016-10-11 11:47:09 +0200978 def addSkip(self, test, reason):
979 """
980 Record a test skipped.
981
982 :param test:
983 :param reason:
984
985 """
Klement Sekerab91017a2017-02-09 06:04:36 +0100986 if hasattr(test, 'logger'):
987 test.logger.debug("--- addSkip() %s.%s(%s) called, reason is %s"
988 % (test.__class__.__name__,
989 test._testMethodName,
990 test._testMethodDoc,
991 reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200992 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +0200993 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200994
Klement Sekeraf413bef2017-08-15 07:09:02 +0200995 def symlink_failed(self, test):
996 logger = None
997 if hasattr(test, 'logger'):
998 logger = test.logger
999 if hasattr(test, 'tempdir'):
1000 try:
1001 failed_dir = os.getenv('VPP_TEST_FAILED_DIR')
juraj.linkes184870a2018-07-16 14:22:01 +02001002 link_path = os.path.join(failed_dir, '%s-FAILED' %
1003 os.path.basename(test.tempdir))
Klement Sekeraf413bef2017-08-15 07:09:02 +02001004 if logger:
1005 logger.debug("creating a link to the failed test")
1006 logger.debug("os.symlink(%s, %s)" %
1007 (test.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001008 if os.path.exists(link_path):
1009 if logger:
1010 logger.debug('symlink already exists')
1011 else:
1012 os.symlink(test.tempdir, link_path)
1013
Klement Sekeraf413bef2017-08-15 07:09:02 +02001014 except Exception as e:
1015 if logger:
1016 logger.error(e)
1017
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001018 def send_results_through_pipe(self):
1019 if hasattr(self, 'test_framework_results_pipe'):
1020 pipe = self.test_framework_results_pipe
1021 if pipe:
1022 pipe.send(self)
1023
Damjan Marionf56b77a2016-10-03 19:44:57 +02001024 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001025 """
1026 Record a test failed result
1027
1028 :param test:
1029 :param err: error message
1030
1031 """
Klement Sekerab91017a2017-02-09 06:04:36 +01001032 if hasattr(test, 'logger'):
1033 test.logger.debug("--- addFailure() %s.%s(%s) called, err is %s"
1034 % (test.__class__.__name__,
1035 test._testMethodName,
1036 test._testMethodDoc, err))
1037 test.logger.debug("formatted exception is:\n%s" %
1038 "".join(format_exception(*err)))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001039 unittest.TestResult.addFailure(self, test, err)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001040 if hasattr(test, 'tempdir'):
Klement Sekera277b89c2016-10-28 13:20:27 +02001041 self.result_string = colorize("FAIL", RED) + \
Klement Sekeraf62ae122016-10-11 11:47:09 +02001042 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
Klement Sekeraf413bef2017-08-15 07:09:02 +02001043 self.symlink_failed(test)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001044 else:
Klement Sekera277b89c2016-10-28 13:20:27 +02001045 self.result_string = colorize("FAIL", RED) + ' [no temp dir]'
Damjan Marionf56b77a2016-10-03 19:44:57 +02001046
Damjan Marionf56b77a2016-10-03 19:44:57 +02001047 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001048 """
1049 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001050
Klement Sekeraf62ae122016-10-11 11:47:09 +02001051 :param test:
1052 :param err: error message
1053
1054 """
Klement Sekerab91017a2017-02-09 06:04:36 +01001055 if hasattr(test, 'logger'):
1056 test.logger.debug("--- addError() %s.%s(%s) called, err is %s"
1057 % (test.__class__.__name__,
1058 test._testMethodName,
1059 test._testMethodDoc, err))
1060 test.logger.debug("formatted exception is:\n%s" %
1061 "".join(format_exception(*err)))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001062 unittest.TestResult.addError(self, test, err)
1063 if hasattr(test, 'tempdir'):
Klement Sekera277b89c2016-10-28 13:20:27 +02001064 self.result_string = colorize("ERROR", RED) + \
Klement Sekeraf62ae122016-10-11 11:47:09 +02001065 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
Klement Sekeraf413bef2017-08-15 07:09:02 +02001066 self.symlink_failed(test)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001067 else:
Klement Sekera277b89c2016-10-28 13:20:27 +02001068 self.result_string = colorize("ERROR", RED) + ' [no temp dir]'
Klement Sekeraf62ae122016-10-11 11:47:09 +02001069
Damjan Marionf56b77a2016-10-03 19:44:57 +02001070 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001071 """
1072 Get test description
1073
1074 :param test:
1075 :returns: test description
1076
1077 """
juraj.linkes184870a2018-07-16 14:22:01 +02001078 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001079
Damjan Marionf56b77a2016-10-03 19:44:57 +02001080 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001081 """
1082 Start a test
1083
1084 :param test:
1085
1086 """
Klement Sekera87134932017-03-07 11:39:27 +01001087 self.printer.print_test_case_heading_if_first_time(test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001088 unittest.TestResult.startTest(self, test)
1089 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001090 self.stream.writeln(
1091 "Starting " + self.getDescription(test) + " ...")
1092 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001093
Damjan Marionf56b77a2016-10-03 19:44:57 +02001094 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001095 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001096 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001097
1098 :param test:
1099
1100 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001101 unittest.TestResult.stopTest(self, test)
1102 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001103 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001104 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001105 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001106 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001107 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001108 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001109 self.result_string))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001110 self.send_results_through_pipe()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001111
Damjan Marionf56b77a2016-10-03 19:44:57 +02001112 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001113 """
1114 Print errors from running the test case
1115 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001116 self.stream.writeln()
1117 self.printErrorList('ERROR', self.errors)
1118 self.printErrorList('FAIL', self.failures)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001119
Damjan Marionf56b77a2016-10-03 19:44:57 +02001120 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001121 """
1122 Print error list to the output stream together with error type
1123 and test case description.
1124
1125 :param flavour: error type
1126 :param errors: iterable errors
1127
1128 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001129 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001130 self.stream.writeln(double_line_delim)
1131 self.stream.writeln("%s: %s" %
1132 (flavour, self.getDescription(test)))
1133 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001134 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001135
1136
Damjan Marionf56b77a2016-10-03 19:44:57 +02001137class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001138 """
Klement Sekera104543f2017-02-03 07:29:43 +01001139 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001140 """
1141 @property
1142 def resultclass(self):
1143 """Class maintaining the results of the tests"""
1144 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001145
juraj.linkes184870a2018-07-16 14:22:01 +02001146 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001147 results_pipe=None, failfast=False, buffer=False,
1148 resultclass=None):
Klement Sekera7a161da2017-01-17 13:42:48 +01001149 # ignore stream setting here, use hard-coded stdout to be in sync
1150 # with prints from VppTestCase methods ...
1151 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1152 verbosity, failfast, buffer,
1153 resultclass)
Klement Sekera909a6a12017-08-08 04:33:53 +02001154 reporter = KeepAliveReporter()
Klement Sekeradf2b9802017-10-05 10:26:03 +02001155 reporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001156
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001157 VppTestResult.test_framework_results_pipe = results_pipe
1158
Damjan Marionf56b77a2016-10-03 19:44:57 +02001159 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001160 """
1161 Run the tests
1162
1163 :param test:
1164
1165 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001166 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001167
1168 result = super(VppTestRunner, self).run(test)
1169 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001170
1171
1172class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001173 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001174 self.logger = logger
1175 self.args = args
1176 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001177 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001178 super(Worker, self).__init__()
1179
1180 def run(self):
1181 executable = self.args[0]
1182 self.logger.debug("Running executable w/args `%s'" % self.args)
1183 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001184 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001185 env["CK_LOG_FILE_NAME"] = "-"
1186 self.process = subprocess.Popen(
1187 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1188 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1189 out, err = self.process.communicate()
1190 self.logger.debug("Finished running `%s'" % executable)
1191 self.logger.info("Return code is `%s'" % self.process.returncode)
1192 self.logger.info(single_line_delim)
1193 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1194 self.logger.info(single_line_delim)
1195 self.logger.info(out)
1196 self.logger.info(single_line_delim)
1197 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1198 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001199 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001200 self.logger.info(single_line_delim)
1201 self.result = self.process.returncode