blob: da34724befd60702037fa5e79124395e2e3cf917 [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
Ole Troan73202102018-08-31 00:29:48 +020026from vpp_papi.vpp_stats import VPPStats
Klement Sekera05742262018-03-14 18:14:49 +010027from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
28 getLogger, colorize
Klement Sekera10db26f2017-01-11 08:16:53 +010029from vpp_object import VppObjectRegistry
Klement Sekera31da2e32018-06-24 22:49:55 +020030from util import ppp
Klement Sekerad81ae412018-05-16 10:52:54 +020031from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
32from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
33from scapy.layers.inet6 import ICMPv6EchoReply
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010034if os.name == 'posix' and sys.version_info[0] < 3:
35 # using subprocess32 is recommended by python official documentation
36 # @ https://docs.python.org/2/library/subprocess.html
37 import subprocess32 as subprocess
38else:
39 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020040
Klement Sekerad81ae412018-05-16 10:52:54 +020041
juraj.linkescae64f82018-09-19 15:01:47 +020042PASS = 0
43FAIL = 1
44ERROR = 2
45SKIP = 3
46TEST_RUN = 4
47
48
Klement Sekeraebbaf552018-02-17 13:41:33 +010049debug_framework = False
50if os.getenv('TEST_DEBUG', "0") == "1":
51 debug_framework = True
52 import debug_internal
53
54
Klement Sekeraf62ae122016-10-11 11:47:09 +020055"""
56 Test framework module.
57
58 The module provides a set of tools for constructing and running tests and
59 representing the results.
60"""
61
Klement Sekeraf62ae122016-10-11 11:47:09 +020062
Damjan Marionf56b77a2016-10-03 19:44:57 +020063class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020064 """Private class to create packet info object.
65
66 Help process information about the next packet.
67 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020068 """
Matej Klotton86d87c42016-11-11 11:38:55 +010069 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020070 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010071 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020072 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010073 #: Store the index of the destination packet generator interface
74 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020075 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010076 #: Store expected ip version
77 ip = -1
78 #: Store expected upper protocol
79 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010080 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020081 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020082
Matej Klotton16a14cd2016-12-07 15:09:13 +010083 def __eq__(self, other):
84 index = self.index == other.index
85 src = self.src == other.src
86 dst = self.dst == other.dst
87 data = self.data == other.data
88 return index and src and dst and data
89
Klement Sekeraf62ae122016-10-11 11:47:09 +020090
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010091def pump_output(testclass):
92 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +010093 stdout_fragment = ""
94 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -040095 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010096 readable = select.select([testclass.vpp.stdout.fileno(),
97 testclass.vpp.stderr.fileno(),
98 testclass.pump_thread_wakeup_pipe[0]],
99 [], [])[0]
100 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100101 read = os.read(testclass.vpp.stdout.fileno(), 102400)
102 if len(read) > 0:
103 split = read.splitlines(True)
104 if len(stdout_fragment) > 0:
105 split[0] = "%s%s" % (stdout_fragment, split[0])
106 if len(split) > 0 and split[-1].endswith("\n"):
107 limit = None
108 else:
109 limit = -1
110 stdout_fragment = split[-1]
111 testclass.vpp_stdout_deque.extend(split[:limit])
112 if not testclass.cache_vpp_output:
113 for line in split[:limit]:
114 testclass.logger.debug(
115 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100116 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100117 read = os.read(testclass.vpp.stderr.fileno(), 102400)
118 if len(read) > 0:
119 split = read.splitlines(True)
120 if len(stderr_fragment) > 0:
121 split[0] = "%s%s" % (stderr_fragment, split[0])
122 if len(split) > 0 and split[-1].endswith("\n"):
123 limit = None
124 else:
125 limit = -1
126 stderr_fragment = split[-1]
127 testclass.vpp_stderr_deque.extend(split[:limit])
128 if not testclass.cache_vpp_output:
129 for line in split[:limit]:
130 testclass.logger.debug(
131 "VPP STDERR: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100132 # ignoring the dummy pipe here intentionally - the flag will take care
133 # of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200134
135
Klement Sekera87134932017-03-07 11:39:27 +0100136def running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100137 s = os.getenv("EXTENDED_TESTS", "n")
138 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100139
140
Klement Sekerad3e671e2017-09-29 12:36:37 +0200141def running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100142 os_id = os.getenv("OS_ID", "")
143 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200144
145
Klement Sekera909a6a12017-08-08 04:33:53 +0200146class KeepAliveReporter(object):
147 """
148 Singleton object which reports test start to parent process
149 """
150 _shared_state = {}
151
152 def __init__(self):
153 self.__dict__ = self._shared_state
154
155 @property
156 def pipe(self):
157 return self._pipe
158
159 @pipe.setter
160 def pipe(self, pipe):
161 if hasattr(self, '_pipe'):
162 raise Exception("Internal error - pipe should only be set once.")
163 self._pipe = pipe
164
165 def send_keep_alive(self, test):
166 """
167 Write current test tmpdir & desc to keep-alive pipe to signal liveness
168 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200169 if self.pipe is None:
170 # if not running forked..
171 return
172
Klement Sekera909a6a12017-08-08 04:33:53 +0200173 if isclass(test):
174 desc = test.__name__
175 else:
176 desc = test.shortDescription()
177 if not desc:
178 desc = str(test)
179
Dave Wallacee2efd122017-09-30 22:04:21 -0400180 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200181
182
Damjan Marionf56b77a2016-10-03 19:44:57 +0200183class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100184 """This subclass is a base class for VPP test cases that are implemented as
185 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200186 """
187
188 @property
189 def packet_infos(self):
190 """List of packet infos"""
191 return self._packet_infos
192
Klement Sekeradab231a2016-12-21 08:50:14 +0100193 @classmethod
194 def get_packet_count_for_if_idx(cls, dst_if_index):
195 """Get the number of packet info for specified destination if index"""
196 if dst_if_index in cls._packet_count_for_dst_if_idx:
197 return cls._packet_count_for_dst_if_idx[dst_if_index]
198 else:
199 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200200
201 @classmethod
202 def instance(cls):
203 """Return the instance of this testcase"""
204 return cls.test_instance
205
Damjan Marionf56b77a2016-10-03 19:44:57 +0200206 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200207 def set_debug_flags(cls, d):
208 cls.debug_core = False
209 cls.debug_gdb = False
210 cls.debug_gdbserver = False
211 if d is None:
212 return
213 dl = d.lower()
214 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200215 cls.debug_core = True
216 elif dl == "gdb":
217 cls.debug_gdb = True
218 elif dl == "gdbserver":
219 cls.debug_gdbserver = True
220 else:
221 raise Exception("Unrecognized DEBUG option: '%s'" % d)
222
223 @classmethod
juraj.linkes184870a2018-07-16 14:22:01 +0200224 def get_least_used_cpu(self):
225 cpu_usage_list = [set(range(psutil.cpu_count()))]
226 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
227 if 'vpp_main' == p.info['name']]
228 for vpp_process in vpp_processes:
229 for cpu_usage_set in cpu_usage_list:
230 try:
231 cpu_num = vpp_process.cpu_num()
232 if cpu_num in cpu_usage_set:
233 cpu_usage_set_index = cpu_usage_list.index(
234 cpu_usage_set)
235 if cpu_usage_set_index == len(cpu_usage_list) - 1:
236 cpu_usage_list.append({cpu_num})
237 else:
238 cpu_usage_list[cpu_usage_set_index + 1].add(
239 cpu_num)
240 cpu_usage_set.remove(cpu_num)
241 break
242 except psutil.NoSuchProcess:
243 pass
244
245 for cpu_usage_set in cpu_usage_list:
246 if len(cpu_usage_set) > 0:
247 min_usage_set = cpu_usage_set
248 break
249
250 return random.choice(tuple(min_usage_set))
251
252 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200253 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200254 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100255 s = os.getenv("STEP", "n")
256 cls.step = True if s.lower() in ("y", "yes", "1") else False
257 d = os.getenv("DEBUG", None)
258 c = os.getenv("CACHE_OUTPUT", "1")
259 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200260 cls.set_debug_flags(d)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200261 cls.vpp_bin = os.getenv('VPP_TEST_BIN', "vpp")
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100262 cls.plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100263 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
264 plugin_path = None
265 if cls.plugin_path is not None:
266 if cls.extern_plugin_path is not None:
267 plugin_path = "%s:%s" % (
268 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100269 else:
270 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100271 elif cls.extern_plugin_path is not None:
272 plugin_path = cls.extern_plugin_path
Klement Sekera01bbbe92016-11-02 09:25:05 +0100273 debug_cli = ""
274 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
275 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100276 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100277 size = os.getenv("COREDUMP_SIZE")
278 if size is not None:
279 coredump_size = "coredump-size %s" % size
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100280 if coredump_size is None:
Dave Wallacee2efd122017-09-30 22:04:21 -0400281 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200282
283 cpu_core_number = cls.get_least_used_cpu()
284
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100285 cls.vpp_cmdline = [cls.vpp_bin, "unix",
Dave Wallacee2efd122017-09-30 22:04:21 -0400286 "{", "nodaemon", debug_cli, "full-coredump",
287 coredump_size, "}", "api-trace", "{", "on", "}",
Damjan Marion374e2c52017-03-09 20:38:15 +0100288 "api-segment", "{", "prefix", cls.shm_prefix, "}",
juraj.linkes184870a2018-07-16 14:22:01 +0200289 "cpu", "{", "main-core", str(cpu_core_number), "}",
Klement Sekera611864f2018-09-26 11:19:00 +0200290 "statseg", "{", "socket-name", cls.stats_sock, "}",
Damjan Marion374e2c52017-03-09 20:38:15 +0100291 "plugins", "{", "plugin", "dpdk_plugin.so", "{",
Dave Barach8b5dc4f2018-07-23 18:00:54 -0400292 "disable", "}", "plugin", "unittest_plugin.so",
293 "{", "enable", "}", "}", ]
Klement Sekera47e275b2017-03-21 08:21:25 +0100294 if plugin_path is not None:
295 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekera277b89c2016-10-28 13:20:27 +0200296 cls.logger.info("vpp_cmdline: %s" % cls.vpp_cmdline)
297
298 @classmethod
299 def wait_for_enter(cls):
300 if cls.debug_gdbserver:
301 print(double_line_delim)
302 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
303 elif cls.debug_gdb:
304 print(double_line_delim)
305 print("Spawned VPP with PID: %d" % cls.vpp.pid)
306 else:
307 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
308 return
309 print(single_line_delim)
310 print("You can debug the VPP using e.g.:")
311 if cls.debug_gdbserver:
312 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
313 print("Now is the time to attach a gdb by running the above "
314 "command, set up breakpoints etc. and then resume VPP from "
315 "within gdb by issuing the 'continue' command")
316 elif cls.debug_gdb:
317 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
318 print("Now is the time to attach a gdb by running the above "
319 "command and set up breakpoints etc.")
320 print(single_line_delim)
Klement Sekerae1ace192018-03-23 21:54:12 +0100321 raw_input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200322
323 @classmethod
324 def run_vpp(cls):
325 cmdline = cls.vpp_cmdline
326
327 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100328 gdbserver = '/usr/bin/gdbserver'
329 if not os.path.isfile(gdbserver) or \
330 not os.access(gdbserver, os.X_OK):
331 raise Exception("gdbserver binary '%s' does not exist or is "
332 "not executable" % gdbserver)
333
334 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200335 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
336
Klement Sekera931be3a2016-11-03 05:36:01 +0100337 try:
338 cls.vpp = subprocess.Popen(cmdline,
339 stdout=subprocess.PIPE,
340 stderr=subprocess.PIPE,
341 bufsize=1)
342 except Exception as e:
343 cls.logger.critical("Couldn't start vpp: %s" % e)
344 raise
345
Klement Sekera277b89c2016-10-28 13:20:27 +0200346 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100347
Damjan Marionf56b77a2016-10-03 19:44:57 +0200348 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200349 def wait_for_stats_socket(cls):
350 deadline = time.time() + 3
351 while time.time() < deadline or cls.debug_gdb or cls.debug_gdbserver:
352 if os.path.exists(cls.stats_sock):
353 break
354
355 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200356 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200357 """
358 Perform class setup before running the testcase
359 Remove shared memory files, start vpp and connect the vpp-api
360 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100361 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100362 random.seed()
juraj.linkes184870a2018-07-16 14:22:01 +0200363 if not hasattr(cls, 'logger'):
364 cls.logger = getLogger(cls.__name__)
365 else:
366 cls.logger.name = cls.__name__
Klement Sekeraf62ae122016-10-11 11:47:09 +0200367 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200368 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200369 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200370 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
371 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100372 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
373 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200374 cls.file_handler.setLevel(DEBUG)
375 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200376 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200377 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200378 cls.logger.info("Temporary dir is %s, shm prefix is %s",
379 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200380 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100381 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100382 cls._captures = []
383 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200384 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100385 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100386 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200387 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200388 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200389 # need to catch exceptions here because if we raise, then the cleanup
390 # doesn't get called and we might end with a zombie vpp
391 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200392 cls.run_vpp()
Dave Wallacee2efd122017-09-30 22:04:21 -0400393 cls.reporter.send_keep_alive(cls)
Klement Sekerae4504c62016-12-08 10:16:41 +0100394 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100395 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100396 cls.pump_thread_stop_flag = Event()
397 cls.pump_thread_wakeup_pipe = os.pipe()
398 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100399 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100400 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200401 if cls.debug_gdb or cls.debug_gdbserver:
402 read_timeout = 0
403 else:
404 read_timeout = 5
405 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
406 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100407 if cls.step:
408 hook = StepHook(cls)
409 else:
410 hook = PollHook(cls)
411 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200412 cls.wait_for_stats_socket()
413 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200414 try:
415 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100416 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200417 cls.vpp_startup_failed = True
418 cls.logger.critical(
419 "VPP died shortly after startup, check the"
420 " output to standard error for possible cause")
421 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100422 try:
423 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100424 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100425 try:
426 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100427 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100428 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100429 if cls.debug_gdbserver:
430 print(colorize("You're running VPP inside gdbserver but "
431 "VPP-API connection failed, did you forget "
432 "to 'continue' VPP from within gdb?", RED))
433 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100434 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100435 try:
436 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100437 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100438 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100439 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200440
Damjan Marionf56b77a2016-10-03 19:44:57 +0200441 @classmethod
442 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200443 """
444 Disconnect vpp-api, kill vpp and cleanup shared memory files
445 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200446 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
447 cls.vpp.poll()
448 if cls.vpp.returncode is None:
449 print(double_line_delim)
450 print("VPP or GDB server is still running")
451 print(single_line_delim)
Klement Sekerae1ace192018-03-23 21:54:12 +0100452 raw_input("When done debugging, press ENTER to kill the "
453 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200454
juraj.linkes184870a2018-07-16 14:22:01 +0200455 # first signal that we want to stop the pump thread, then wake it up
456 if hasattr(cls, 'pump_thread_stop_flag'):
457 cls.pump_thread_stop_flag.set()
458 if hasattr(cls, 'pump_thread_wakeup_pipe'):
459 os.write(cls.pump_thread_wakeup_pipe[1], 'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100460 if hasattr(cls, 'pump_thread'):
461 cls.logger.debug("Waiting for pump thread to stop")
462 cls.pump_thread.join()
463 if hasattr(cls, 'vpp_stderr_reader_thread'):
464 cls.logger.debug("Waiting for stdderr pump to stop")
465 cls.vpp_stderr_reader_thread.join()
466
Klement Sekeraf62ae122016-10-11 11:47:09 +0200467 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100468 if hasattr(cls, 'vapi'):
469 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100470 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200471 cls.vpp.poll()
472 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100473 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200474 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100475 cls.logger.debug("Waiting for vpp to die")
476 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200477 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200478
Klement Sekera3747c752017-04-10 06:30:17 +0200479 if cls.vpp_startup_failed:
480 stdout_log = cls.logger.info
481 stderr_log = cls.logger.critical
482 else:
483 stdout_log = cls.logger.info
484 stderr_log = cls.logger.info
485
Klement Sekerae4504c62016-12-08 10:16:41 +0100486 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200487 stdout_log(single_line_delim)
488 stdout_log('VPP output to stdout while running %s:', cls.__name__)
489 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100490 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200491 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
492 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200493 stdout_log('\n%s', vpp_output)
494 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200495
Klement Sekerae4504c62016-12-08 10:16:41 +0100496 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200497 stderr_log(single_line_delim)
498 stderr_log('VPP output to stderr while running %s:', cls.__name__)
499 stderr_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100500 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200501 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
502 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200503 stderr_log('\n%s', vpp_output)
504 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200505
Damjan Marionf56b77a2016-10-03 19:44:57 +0200506 @classmethod
507 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200508 """ Perform final cleanup after running all tests in this test-case """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200509 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200510 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100511 cls.reset_packet_infos()
512 if debug_framework:
513 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200514
Damjan Marionf56b77a2016-10-03 19:44:57 +0200515 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200516 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100517 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
518 (self.__class__.__name__, self._testMethodName,
519 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200520 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200521 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700522 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200523 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200524 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200525 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800526 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100527 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500528 # Save/Dump VPP api trace log
529 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
530 tmp_api_trace = "/tmp/%s" % api_trace
531 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
532 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
533 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
534 vpp_api_trace_log))
535 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500536 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500537 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100538 else:
539 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200540
Damjan Marionf56b77a2016-10-03 19:44:57 +0200541 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200542 """ Clear trace before running each test"""
Klement Sekera909a6a12017-08-08 04:33:53 +0200543 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100544 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
545 (self.__class__.__name__, self._testMethodName,
546 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100547 if self.vpp_dead:
548 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100549 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100550 self.vpp_stdout_deque.append(
551 "--- test setUp() for %s.%s(%s) starts here ---\n" %
552 (self.__class__.__name__, self._testMethodName,
553 self._testMethodDoc))
554 self.vpp_stderr_deque.append(
555 "--- test setUp() for %s.%s(%s) starts here ---\n" %
556 (self.__class__.__name__, self._testMethodName,
557 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200558 self.vapi.cli("clear trace")
559 # store the test instance inside the test class - so that objects
560 # holding the class can access instance methods (like assertEqual)
561 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200562
Damjan Marionf56b77a2016-10-03 19:44:57 +0200563 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200564 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200565 """
566 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200567
Klement Sekera75e7d132017-09-20 08:26:30 +0200568 :param interfaces: iterable interface indexes (if None,
569 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200570
Klement Sekeraf62ae122016-10-11 11:47:09 +0200571 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200572 if interfaces is None:
573 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200574 for i in interfaces:
575 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200576
Damjan Marionf56b77a2016-10-03 19:44:57 +0200577 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100578 def register_capture(cls, cap_name):
579 """ Register a capture in the testclass """
580 # add to the list of captures with current timestamp
581 cls._captures.append((time.time(), cap_name))
582 # filter out from zombies
583 cls._zombie_captures = [(stamp, name)
584 for (stamp, name) in cls._zombie_captures
585 if name != cap_name]
586
587 @classmethod
588 def pg_start(cls):
589 """ Remove any zombie captures and enable the packet generator """
590 # how long before capture is allowed to be deleted - otherwise vpp
591 # crashes - 100ms seems enough (this shouldn't be needed at all)
592 capture_ttl = 0.1
593 now = time.time()
594 for stamp, cap_name in cls._zombie_captures:
595 wait = stamp + capture_ttl - now
596 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100597 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100598 now = time.time()
599 cls.logger.debug("Removing zombie capture %s" % cap_name)
600 cls.vapi.cli('packet-generator delete %s' % cap_name)
601
Klement Sekeraf62ae122016-10-11 11:47:09 +0200602 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
603 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100604 cls._zombie_captures = cls._captures
605 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200606
Damjan Marionf56b77a2016-10-03 19:44:57 +0200607 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200608 def create_pg_interfaces(cls, interfaces):
609 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100610 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200611
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100612 :param interfaces: iterable indexes of the interfaces.
613 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200614
Klement Sekeraf62ae122016-10-11 11:47:09 +0200615 """
616 result = []
617 for i in interfaces:
618 intf = VppPGInterface(cls, i)
619 setattr(cls, intf.name, intf)
620 result.append(intf)
621 cls.pg_interfaces = result
622 return result
623
Matej Klotton0178d522016-11-04 11:11:44 +0100624 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200625 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100626 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100627 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100628
Klement Sekerab9ef2732018-06-24 22:49:33 +0200629 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100630 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100631 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200632 result = [VppLoInterface(cls) for i in range(count)]
633 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100634 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100635 cls.lo_interfaces = result
636 return result
637
Damjan Marionf56b77a2016-10-03 19:44:57 +0200638 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200639 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200640 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200641 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200642 NOTE: Currently works only when Raw layer is present.
643
644 :param packet: packet
645 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200646 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200647
648 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200649 packet_len = len(packet) + 4
650 extend = size - packet_len
651 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200652 num = (extend / len(padding)) + 1
653 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200654
Klement Sekeradab231a2016-12-21 08:50:14 +0100655 @classmethod
656 def reset_packet_infos(cls):
657 """ Reset the list of packet info objects and packet counts to zero """
658 cls._packet_infos = {}
659 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200660
Klement Sekeradab231a2016-12-21 08:50:14 +0100661 @classmethod
662 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200663 """
664 Create packet info object containing the source and destination indexes
665 and add it to the testcase's packet info list
666
Klement Sekeradab231a2016-12-21 08:50:14 +0100667 :param VppInterface src_if: source interface
668 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200669
670 :returns: _PacketInfo object
671
672 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200673 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100674 info.index = len(cls._packet_infos)
675 info.src = src_if.sw_if_index
676 info.dst = dst_if.sw_if_index
677 if isinstance(dst_if, VppSubInterface):
678 dst_idx = dst_if.parent.sw_if_index
679 else:
680 dst_idx = dst_if.sw_if_index
681 if dst_idx in cls._packet_count_for_dst_if_idx:
682 cls._packet_count_for_dst_if_idx[dst_idx] += 1
683 else:
684 cls._packet_count_for_dst_if_idx[dst_idx] = 1
685 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200686 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200687
Damjan Marionf56b77a2016-10-03 19:44:57 +0200688 @staticmethod
689 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200690 """
691 Convert _PacketInfo object to packet payload
692
693 :param info: _PacketInfo object
694
695 :returns: string containing serialized data from packet info
696 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100697 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
698 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200699
Damjan Marionf56b77a2016-10-03 19:44:57 +0200700 @staticmethod
701 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200702 """
703 Convert packet payload to _PacketInfo object
704
705 :param payload: packet payload
706
707 :returns: _PacketInfo object containing de-serialized data from payload
708
709 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200710 numbers = payload.split()
711 info = _PacketInfo()
712 info.index = int(numbers[0])
713 info.src = int(numbers[1])
714 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100715 info.ip = int(numbers[3])
716 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200717 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200718
Damjan Marionf56b77a2016-10-03 19:44:57 +0200719 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200720 """
721 Iterate over the packet info list stored in the testcase
722 Start iteration with first element if info is None
723 Continue based on index in info if info is specified
724
725 :param info: info or None
726 :returns: next info in list or None if no more infos
727 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200728 if info is None:
729 next_index = 0
730 else:
731 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100732 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200733 return None
734 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100735 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200736
Klement Sekeraf62ae122016-10-11 11:47:09 +0200737 def get_next_packet_info_for_interface(self, src_index, info):
738 """
739 Search the packet info list for the next packet info with same source
740 interface index
741
742 :param src_index: source interface index to search for
743 :param info: packet info - where to start the search
744 :returns: packet info or None
745
746 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200747 while True:
748 info = self.get_next_packet_info(info)
749 if info is None:
750 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200751 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200752 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200753
Klement Sekeraf62ae122016-10-11 11:47:09 +0200754 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
755 """
756 Search the packet info list for the next packet info with same source
757 and destination interface indexes
758
759 :param src_index: source interface index to search for
760 :param dst_index: destination interface index to search for
761 :param info: packet info - where to start the search
762 :returns: packet info or None
763
764 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200765 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200766 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200767 if info is None:
768 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200769 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200770 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200771
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200772 def assert_equal(self, real_value, expected_value, name_or_class=None):
773 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100774 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200775 return
776 try:
777 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
778 msg = msg % (getdoc(name_or_class).strip(),
779 real_value, str(name_or_class(real_value)),
780 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100781 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200782 msg = "Invalid %s: %s does not match expected value %s" % (
783 name_or_class, real_value, expected_value)
784
785 self.assertEqual(real_value, expected_value, msg)
786
Klement Sekerab17dd962017-01-09 07:43:48 +0100787 def assert_in_range(self,
788 real_value,
789 expected_min,
790 expected_max,
791 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200792 if name is None:
793 msg = None
794 else:
795 msg = "Invalid %s: %s out of range <%s,%s>" % (
796 name, real_value, expected_min, expected_max)
797 self.assertTrue(expected_min <= real_value <= expected_max, msg)
798
Klement Sekerad81ae412018-05-16 10:52:54 +0200799 def assert_packet_checksums_valid(self, packet,
800 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200801 received = packet.__class__(str(packet))
802 self.logger.debug(
803 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200804 udp_layers = ['UDP', 'UDPerror']
805 checksum_fields = ['cksum', 'chksum']
806 checksums = []
807 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200808 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200809 while True:
810 layer = temp.getlayer(counter)
811 if layer:
812 for cf in checksum_fields:
813 if hasattr(layer, cf):
814 if ignore_zero_udp_checksums and \
815 0 == getattr(layer, cf) and \
816 layer.name in udp_layers:
817 continue
818 delattr(layer, cf)
819 checksums.append((counter, cf))
820 else:
821 break
822 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200823 if 0 == len(checksums):
824 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200825 temp = temp.__class__(str(temp))
826 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200827 calc_sum = getattr(temp[layer], cf)
828 self.assert_equal(
829 getattr(received[layer], cf), calc_sum,
830 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
831 self.logger.debug(
832 "Checksum field `%s` on `%s` layer has correct value `%s`" %
833 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200834
835 def assert_checksum_valid(self, received_packet, layer,
836 field_name='chksum',
837 ignore_zero_checksum=False):
838 """ Check checksum of received packet on given layer """
839 received_packet_checksum = getattr(received_packet[layer], field_name)
840 if ignore_zero_checksum and 0 == received_packet_checksum:
841 return
842 recalculated = received_packet.__class__(str(received_packet))
843 delattr(recalculated[layer], field_name)
844 recalculated = recalculated.__class__(str(recalculated))
845 self.assert_equal(received_packet_checksum,
846 getattr(recalculated[layer], field_name),
847 "packet checksum on layer: %s" % layer)
848
849 def assert_ip_checksum_valid(self, received_packet,
850 ignore_zero_checksum=False):
851 self.assert_checksum_valid(received_packet, 'IP',
852 ignore_zero_checksum=ignore_zero_checksum)
853
854 def assert_tcp_checksum_valid(self, received_packet,
855 ignore_zero_checksum=False):
856 self.assert_checksum_valid(received_packet, 'TCP',
857 ignore_zero_checksum=ignore_zero_checksum)
858
859 def assert_udp_checksum_valid(self, received_packet,
860 ignore_zero_checksum=True):
861 self.assert_checksum_valid(received_packet, 'UDP',
862 ignore_zero_checksum=ignore_zero_checksum)
863
864 def assert_embedded_icmp_checksum_valid(self, received_packet):
865 if received_packet.haslayer(IPerror):
866 self.assert_checksum_valid(received_packet, 'IPerror')
867 if received_packet.haslayer(TCPerror):
868 self.assert_checksum_valid(received_packet, 'TCPerror')
869 if received_packet.haslayer(UDPerror):
870 self.assert_checksum_valid(received_packet, 'UDPerror',
871 ignore_zero_checksum=True)
872 if received_packet.haslayer(ICMPerror):
873 self.assert_checksum_valid(received_packet, 'ICMPerror')
874
875 def assert_icmp_checksum_valid(self, received_packet):
876 self.assert_checksum_valid(received_packet, 'ICMP')
877 self.assert_embedded_icmp_checksum_valid(received_packet)
878
879 def assert_icmpv6_checksum_valid(self, pkt):
880 if pkt.haslayer(ICMPv6DestUnreach):
881 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
882 self.assert_embedded_icmp_checksum_valid(pkt)
883 if pkt.haslayer(ICMPv6EchoRequest):
884 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
885 if pkt.haslayer(ICMPv6EchoReply):
886 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
887
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100888 @classmethod
889 def sleep(cls, timeout, remark=None):
890 if hasattr(cls, 'logger'):
Klement Sekera3cfa5582017-04-19 07:10:58 +0000891 cls.logger.debug("Starting sleep for %ss (%s)" % (timeout, remark))
892 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100893 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000894 after = time.time()
895 if after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200896 cls.logger.error("unexpected time.sleep() result - "
897 "slept for %ss instead of ~%ss!" % (
898 after - before, timeout))
Klement Sekera3cfa5582017-04-19 07:10:58 +0000899 if hasattr(cls, 'logger'):
900 cls.logger.debug(
901 "Finished sleep (%s) - slept %ss (wanted %ss)" % (
902 remark, after - before, timeout))
Klement Sekeraa57a9702017-02-02 06:58:07 +0100903
Neale Ranns947ea622018-06-07 23:48:20 -0700904 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800905 self.vapi.cli("clear trace")
906 intf.add_stream(pkts)
907 self.pg_enable_capture(self.pg_interfaces)
908 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700909 if not timeout:
910 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800911 for i in self.pg_interfaces:
912 i.get_capture(0, timeout=timeout)
913 i.assert_nothing_captured(remark=remark)
914 timeout = 0.1
915
916 def send_and_expect(self, input, pkts, output):
917 self.vapi.cli("clear trace")
918 input.add_stream(pkts)
919 self.pg_enable_capture(self.pg_interfaces)
920 self.pg_start()
921 rx = output.get_capture(len(pkts))
922 return rx
923
Damjan Marionf56b77a2016-10-03 19:44:57 +0200924
juraj.linkes184870a2018-07-16 14:22:01 +0200925def get_testcase_doc_name(test):
926 return getdoc(test.__class__).splitlines()[0]
927
928
929def get_test_description(descriptions, test):
930 # TODO: if none print warning not raise exception
931 short_description = test.shortDescription()
932 if descriptions and short_description:
933 return short_description
934 else:
935 return str(test)
936
937
Klement Sekera87134932017-03-07 11:39:27 +0100938class TestCasePrinter(object):
939 _shared_state = {}
940
941 def __init__(self):
942 self.__dict__ = self._shared_state
943 if not hasattr(self, "_test_case_set"):
944 self._test_case_set = set()
945
946 def print_test_case_heading_if_first_time(self, case):
947 if case.__class__ not in self._test_case_set:
948 print(double_line_delim)
juraj.linkes184870a2018-07-16 14:22:01 +0200949 print(colorize(get_testcase_doc_name(case), GREEN))
Klement Sekera87134932017-03-07 11:39:27 +0100950 print(double_line_delim)
951 self._test_case_set.add(case.__class__)
952
953
Damjan Marionf56b77a2016-10-03 19:44:57 +0200954class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200955 """
956 @property result_string
957 String variable to store the test case result string.
958 @property errors
959 List variable containing 2-tuples of TestCase instances and strings
960 holding formatted tracebacks. Each tuple represents a test which
961 raised an unexpected exception.
962 @property failures
963 List variable containing 2-tuples of TestCase instances and strings
964 holding formatted tracebacks. Each tuple represents a test where
965 a failure was explicitly signalled using the TestCase.assert*()
966 methods.
967 """
968
Damjan Marionf56b77a2016-10-03 19:44:57 +0200969 def __init__(self, stream, descriptions, verbosity):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200970 """
Klement Sekerada505f62017-01-04 12:58:53 +0100971 :param stream File descriptor to store where to report test results.
972 Set to the standard error stream by default.
973 :param descriptions Boolean variable to store information if to use
974 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200975 :param verbosity Integer variable to store required verbosity level.
976 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200977 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
978 self.stream = stream
979 self.descriptions = descriptions
980 self.verbosity = verbosity
981 self.result_string = None
Klement Sekera87134932017-03-07 11:39:27 +0100982 self.printer = TestCasePrinter()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200983
Damjan Marionf56b77a2016-10-03 19:44:57 +0200984 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200985 """
986 Record a test succeeded result
987
988 :param test:
989
990 """
Klement Sekerab91017a2017-02-09 06:04:36 +0100991 if hasattr(test, 'logger'):
992 test.logger.debug("--- addSuccess() %s.%s(%s) called"
993 % (test.__class__.__name__,
994 test._testMethodName,
995 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200996 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +0200997 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200998
juraj.linkescae64f82018-09-19 15:01:47 +0200999 self.send_result_through_pipe(test, PASS)
1000
Klement Sekeraf62ae122016-10-11 11:47:09 +02001001 def addSkip(self, test, reason):
1002 """
1003 Record a test skipped.
1004
1005 :param test:
1006 :param reason:
1007
1008 """
Klement Sekerab91017a2017-02-09 06:04:36 +01001009 if hasattr(test, 'logger'):
1010 test.logger.debug("--- addSkip() %s.%s(%s) called, reason is %s"
1011 % (test.__class__.__name__,
1012 test._testMethodName,
1013 test._testMethodDoc,
1014 reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001015 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001016 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001017
juraj.linkescae64f82018-09-19 15:01:47 +02001018 self.send_result_through_pipe(test, SKIP)
1019
Klement Sekeraf413bef2017-08-15 07:09:02 +02001020 def symlink_failed(self, test):
1021 logger = None
1022 if hasattr(test, 'logger'):
1023 logger = test.logger
1024 if hasattr(test, 'tempdir'):
1025 try:
1026 failed_dir = os.getenv('VPP_TEST_FAILED_DIR')
juraj.linkes184870a2018-07-16 14:22:01 +02001027 link_path = os.path.join(failed_dir, '%s-FAILED' %
1028 os.path.basename(test.tempdir))
Klement Sekeraf413bef2017-08-15 07:09:02 +02001029 if logger:
1030 logger.debug("creating a link to the failed test")
1031 logger.debug("os.symlink(%s, %s)" %
1032 (test.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001033 if os.path.exists(link_path):
1034 if logger:
1035 logger.debug('symlink already exists')
1036 else:
1037 os.symlink(test.tempdir, link_path)
1038
Klement Sekeraf413bef2017-08-15 07:09:02 +02001039 except Exception as e:
1040 if logger:
1041 logger.error(e)
1042
juraj.linkescae64f82018-09-19 15:01:47 +02001043 def send_result_through_pipe(self, test, result):
1044 if hasattr(self, 'test_framework_result_pipe'):
1045 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001046 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001047 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001048
Damjan Marionf56b77a2016-10-03 19:44:57 +02001049 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001050 """
1051 Record a test failed result
1052
1053 :param test:
1054 :param err: error message
1055
1056 """
Klement Sekerab91017a2017-02-09 06:04:36 +01001057 if hasattr(test, 'logger'):
1058 test.logger.debug("--- addFailure() %s.%s(%s) called, err is %s"
1059 % (test.__class__.__name__,
1060 test._testMethodName,
1061 test._testMethodDoc, err))
1062 test.logger.debug("formatted exception is:\n%s" %
1063 "".join(format_exception(*err)))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001064 unittest.TestResult.addFailure(self, test, err)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001065 if hasattr(test, 'tempdir'):
Klement Sekera277b89c2016-10-28 13:20:27 +02001066 self.result_string = colorize("FAIL", RED) + \
Klement Sekeraf62ae122016-10-11 11:47:09 +02001067 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
Klement Sekeraf413bef2017-08-15 07:09:02 +02001068 self.symlink_failed(test)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001069 else:
Klement Sekera277b89c2016-10-28 13:20:27 +02001070 self.result_string = colorize("FAIL", RED) + ' [no temp dir]'
Damjan Marionf56b77a2016-10-03 19:44:57 +02001071
juraj.linkescae64f82018-09-19 15:01:47 +02001072 self.send_result_through_pipe(test, FAIL)
1073
Damjan Marionf56b77a2016-10-03 19:44:57 +02001074 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001075 """
1076 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001077
Klement Sekeraf62ae122016-10-11 11:47:09 +02001078 :param test:
1079 :param err: error message
1080
1081 """
Klement Sekerab91017a2017-02-09 06:04:36 +01001082 if hasattr(test, 'logger'):
1083 test.logger.debug("--- addError() %s.%s(%s) called, err is %s"
1084 % (test.__class__.__name__,
1085 test._testMethodName,
1086 test._testMethodDoc, err))
1087 test.logger.debug("formatted exception is:\n%s" %
1088 "".join(format_exception(*err)))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001089 unittest.TestResult.addError(self, test, err)
1090 if hasattr(test, 'tempdir'):
Klement Sekera277b89c2016-10-28 13:20:27 +02001091 self.result_string = colorize("ERROR", RED) + \
Klement Sekeraf62ae122016-10-11 11:47:09 +02001092 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
Klement Sekeraf413bef2017-08-15 07:09:02 +02001093 self.symlink_failed(test)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001094 else:
Klement Sekera277b89c2016-10-28 13:20:27 +02001095 self.result_string = colorize("ERROR", RED) + ' [no temp dir]'
Klement Sekeraf62ae122016-10-11 11:47:09 +02001096
juraj.linkescae64f82018-09-19 15:01:47 +02001097 self.send_result_through_pipe(test, ERROR)
1098
Damjan Marionf56b77a2016-10-03 19:44:57 +02001099 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001100 """
1101 Get test description
1102
1103 :param test:
1104 :returns: test description
1105
1106 """
juraj.linkes184870a2018-07-16 14:22:01 +02001107 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001108
Damjan Marionf56b77a2016-10-03 19:44:57 +02001109 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001110 """
1111 Start a test
1112
1113 :param test:
1114
1115 """
Klement Sekera87134932017-03-07 11:39:27 +01001116 self.printer.print_test_case_heading_if_first_time(test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001117 unittest.TestResult.startTest(self, test)
1118 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001119 self.stream.writeln(
1120 "Starting " + self.getDescription(test) + " ...")
1121 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001122
Damjan Marionf56b77a2016-10-03 19:44:57 +02001123 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001124 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001125 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001126
1127 :param test:
1128
1129 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001130 unittest.TestResult.stopTest(self, test)
1131 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001132 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001133 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001134 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001135 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001136 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001137 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001138 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001139
1140 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001141
Damjan Marionf56b77a2016-10-03 19:44:57 +02001142 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001143 """
1144 Print errors from running the test case
1145 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001146 self.stream.writeln()
1147 self.printErrorList('ERROR', self.errors)
1148 self.printErrorList('FAIL', self.failures)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001149
Damjan Marionf56b77a2016-10-03 19:44:57 +02001150 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001151 """
1152 Print error list to the output stream together with error type
1153 and test case description.
1154
1155 :param flavour: error type
1156 :param errors: iterable errors
1157
1158 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001159 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001160 self.stream.writeln(double_line_delim)
1161 self.stream.writeln("%s: %s" %
1162 (flavour, self.getDescription(test)))
1163 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001164 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001165
1166
Damjan Marionf56b77a2016-10-03 19:44:57 +02001167class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001168 """
Klement Sekera104543f2017-02-03 07:29:43 +01001169 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001170 """
1171 @property
1172 def resultclass(self):
1173 """Class maintaining the results of the tests"""
1174 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001175
juraj.linkes184870a2018-07-16 14:22:01 +02001176 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001177 result_pipe=None, failfast=False, buffer=False,
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001178 resultclass=None):
Klement Sekera7a161da2017-01-17 13:42:48 +01001179 # ignore stream setting here, use hard-coded stdout to be in sync
1180 # with prints from VppTestCase methods ...
1181 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1182 verbosity, failfast, buffer,
1183 resultclass)
Klement Sekera909a6a12017-08-08 04:33:53 +02001184 reporter = KeepAliveReporter()
Klement Sekeradf2b9802017-10-05 10:26:03 +02001185 reporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001186
juraj.linkescae64f82018-09-19 15:01:47 +02001187 VppTestResult.test_framework_result_pipe = result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001188
Damjan Marionf56b77a2016-10-03 19:44:57 +02001189 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001190 """
1191 Run the tests
1192
1193 :param test:
1194
1195 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001196 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001197
1198 result = super(VppTestRunner, self).run(test)
1199 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001200
1201
1202class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001203 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001204 self.logger = logger
1205 self.args = args
1206 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001207 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001208 super(Worker, self).__init__()
1209
1210 def run(self):
1211 executable = self.args[0]
1212 self.logger.debug("Running executable w/args `%s'" % self.args)
1213 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001214 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001215 env["CK_LOG_FILE_NAME"] = "-"
1216 self.process = subprocess.Popen(
1217 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1218 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1219 out, err = self.process.communicate()
1220 self.logger.debug("Finished running `%s'" % executable)
1221 self.logger.info("Return code is `%s'" % self.process.returncode)
1222 self.logger.info(single_line_delim)
1223 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1224 self.logger.info(single_line_delim)
1225 self.logger.info(out)
1226 self.logger.info(single_line_delim)
1227 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1228 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001229 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001230 self.logger.info(single_line_delim)
1231 self.result = self.process.returncode