blob: 789ec1be9b96d923bc413b7b42fe874da4eb678b [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
Klement Sekeraebbaf552018-02-17 13:41:33 +010042debug_framework = False
43if os.getenv('TEST_DEBUG', "0") == "1":
44 debug_framework = True
45 import debug_internal
46
47
Klement Sekeraf62ae122016-10-11 11:47:09 +020048"""
49 Test framework module.
50
51 The module provides a set of tools for constructing and running tests and
52 representing the results.
53"""
54
Klement Sekeraf62ae122016-10-11 11:47:09 +020055
Damjan Marionf56b77a2016-10-03 19:44:57 +020056class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020057 """Private class to create packet info object.
58
59 Help process information about the next packet.
60 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020061 """
Matej Klotton86d87c42016-11-11 11:38:55 +010062 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020063 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010064 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020065 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010066 #: Store the index of the destination packet generator interface
67 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020068 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010069 #: Store expected ip version
70 ip = -1
71 #: Store expected upper protocol
72 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010073 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020074 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020075
Matej Klotton16a14cd2016-12-07 15:09:13 +010076 def __eq__(self, other):
77 index = self.index == other.index
78 src = self.src == other.src
79 dst = self.dst == other.dst
80 data = self.data == other.data
81 return index and src and dst and data
82
Klement Sekeraf62ae122016-10-11 11:47:09 +020083
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010084def pump_output(testclass):
85 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +010086 stdout_fragment = ""
87 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -040088 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010089 readable = select.select([testclass.vpp.stdout.fileno(),
90 testclass.vpp.stderr.fileno(),
91 testclass.pump_thread_wakeup_pipe[0]],
92 [], [])[0]
93 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +010094 read = os.read(testclass.vpp.stdout.fileno(), 102400)
95 if len(read) > 0:
96 split = read.splitlines(True)
97 if len(stdout_fragment) > 0:
98 split[0] = "%s%s" % (stdout_fragment, split[0])
99 if len(split) > 0 and split[-1].endswith("\n"):
100 limit = None
101 else:
102 limit = -1
103 stdout_fragment = split[-1]
104 testclass.vpp_stdout_deque.extend(split[:limit])
105 if not testclass.cache_vpp_output:
106 for line in split[:limit]:
107 testclass.logger.debug(
108 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100109 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100110 read = os.read(testclass.vpp.stderr.fileno(), 102400)
111 if len(read) > 0:
112 split = read.splitlines(True)
113 if len(stderr_fragment) > 0:
114 split[0] = "%s%s" % (stderr_fragment, split[0])
115 if len(split) > 0 and split[-1].endswith("\n"):
116 limit = None
117 else:
118 limit = -1
119 stderr_fragment = split[-1]
120 testclass.vpp_stderr_deque.extend(split[:limit])
121 if not testclass.cache_vpp_output:
122 for line in split[:limit]:
123 testclass.logger.debug(
124 "VPP STDERR: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100125 # ignoring the dummy pipe here intentionally - the flag will take care
126 # of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200127
128
Klement Sekera87134932017-03-07 11:39:27 +0100129def running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100130 s = os.getenv("EXTENDED_TESTS", "n")
131 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100132
133
Klement Sekerad3e671e2017-09-29 12:36:37 +0200134def running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100135 os_id = os.getenv("OS_ID", "")
136 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200137
138
Klement Sekera909a6a12017-08-08 04:33:53 +0200139class KeepAliveReporter(object):
140 """
141 Singleton object which reports test start to parent process
142 """
143 _shared_state = {}
144
145 def __init__(self):
146 self.__dict__ = self._shared_state
147
148 @property
149 def pipe(self):
150 return self._pipe
151
152 @pipe.setter
153 def pipe(self, pipe):
154 if hasattr(self, '_pipe'):
155 raise Exception("Internal error - pipe should only be set once.")
156 self._pipe = pipe
157
158 def send_keep_alive(self, test):
159 """
160 Write current test tmpdir & desc to keep-alive pipe to signal liveness
161 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200162 if self.pipe is None:
163 # if not running forked..
164 return
165
Klement Sekera909a6a12017-08-08 04:33:53 +0200166 if isclass(test):
167 desc = test.__name__
168 else:
169 desc = test.shortDescription()
170 if not desc:
171 desc = str(test)
172
Dave Wallacee2efd122017-09-30 22:04:21 -0400173 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200174
175
Damjan Marionf56b77a2016-10-03 19:44:57 +0200176class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100177 """This subclass is a base class for VPP test cases that are implemented as
178 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200179 """
180
181 @property
182 def packet_infos(self):
183 """List of packet infos"""
184 return self._packet_infos
185
Klement Sekeradab231a2016-12-21 08:50:14 +0100186 @classmethod
187 def get_packet_count_for_if_idx(cls, dst_if_index):
188 """Get the number of packet info for specified destination if index"""
189 if dst_if_index in cls._packet_count_for_dst_if_idx:
190 return cls._packet_count_for_dst_if_idx[dst_if_index]
191 else:
192 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200193
194 @classmethod
195 def instance(cls):
196 """Return the instance of this testcase"""
197 return cls.test_instance
198
Damjan Marionf56b77a2016-10-03 19:44:57 +0200199 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200200 def set_debug_flags(cls, d):
201 cls.debug_core = False
202 cls.debug_gdb = False
203 cls.debug_gdbserver = False
204 if d is None:
205 return
206 dl = d.lower()
207 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200208 cls.debug_core = True
209 elif dl == "gdb":
210 cls.debug_gdb = True
211 elif dl == "gdbserver":
212 cls.debug_gdbserver = True
213 else:
214 raise Exception("Unrecognized DEBUG option: '%s'" % d)
215
216 @classmethod
juraj.linkes184870a2018-07-16 14:22:01 +0200217 def get_least_used_cpu(self):
218 cpu_usage_list = [set(range(psutil.cpu_count()))]
219 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
220 if 'vpp_main' == p.info['name']]
221 for vpp_process in vpp_processes:
222 for cpu_usage_set in cpu_usage_list:
223 try:
224 cpu_num = vpp_process.cpu_num()
225 if cpu_num in cpu_usage_set:
226 cpu_usage_set_index = cpu_usage_list.index(
227 cpu_usage_set)
228 if cpu_usage_set_index == len(cpu_usage_list) - 1:
229 cpu_usage_list.append({cpu_num})
230 else:
231 cpu_usage_list[cpu_usage_set_index + 1].add(
232 cpu_num)
233 cpu_usage_set.remove(cpu_num)
234 break
235 except psutil.NoSuchProcess:
236 pass
237
238 for cpu_usage_set in cpu_usage_list:
239 if len(cpu_usage_set) > 0:
240 min_usage_set = cpu_usage_set
241 break
242
243 return random.choice(tuple(min_usage_set))
244
245 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200246 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200247 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100248 s = os.getenv("STEP", "n")
249 cls.step = True if s.lower() in ("y", "yes", "1") else False
250 d = os.getenv("DEBUG", None)
251 c = os.getenv("CACHE_OUTPUT", "1")
252 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200253 cls.set_debug_flags(d)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200254 cls.vpp_bin = os.getenv('VPP_TEST_BIN', "vpp")
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100255 cls.plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100256 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
257 plugin_path = None
258 if cls.plugin_path is not None:
259 if cls.extern_plugin_path is not None:
260 plugin_path = "%s:%s" % (
261 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100262 else:
263 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100264 elif cls.extern_plugin_path is not None:
265 plugin_path = cls.extern_plugin_path
Klement Sekera01bbbe92016-11-02 09:25:05 +0100266 debug_cli = ""
267 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
268 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100269 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100270 size = os.getenv("COREDUMP_SIZE")
271 if size is not None:
272 coredump_size = "coredump-size %s" % size
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100273 if coredump_size is None:
Dave Wallacee2efd122017-09-30 22:04:21 -0400274 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200275
276 cpu_core_number = cls.get_least_used_cpu()
277
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100278 cls.vpp_cmdline = [cls.vpp_bin, "unix",
Dave Wallacee2efd122017-09-30 22:04:21 -0400279 "{", "nodaemon", debug_cli, "full-coredump",
280 coredump_size, "}", "api-trace", "{", "on", "}",
Damjan Marion374e2c52017-03-09 20:38:15 +0100281 "api-segment", "{", "prefix", cls.shm_prefix, "}",
juraj.linkes184870a2018-07-16 14:22:01 +0200282 "cpu", "{", "main-core", str(cpu_core_number), "}",
Ole Troan73202102018-08-31 00:29:48 +0200283 "stats", "{", "socket-name",
284 cls.tempdir + "/stats.sock", "}",
Damjan Marion374e2c52017-03-09 20:38:15 +0100285 "plugins", "{", "plugin", "dpdk_plugin.so", "{",
Dave Barach8b5dc4f2018-07-23 18:00:54 -0400286 "disable", "}", "plugin", "unittest_plugin.so",
287 "{", "enable", "}", "}", ]
Klement Sekera47e275b2017-03-21 08:21:25 +0100288 if plugin_path is not None:
289 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekera277b89c2016-10-28 13:20:27 +0200290 cls.logger.info("vpp_cmdline: %s" % cls.vpp_cmdline)
291
292 @classmethod
293 def wait_for_enter(cls):
294 if cls.debug_gdbserver:
295 print(double_line_delim)
296 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
297 elif cls.debug_gdb:
298 print(double_line_delim)
299 print("Spawned VPP with PID: %d" % cls.vpp.pid)
300 else:
301 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
302 return
303 print(single_line_delim)
304 print("You can debug the VPP using e.g.:")
305 if cls.debug_gdbserver:
306 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
307 print("Now is the time to attach a gdb by running the above "
308 "command, set up breakpoints etc. and then resume VPP from "
309 "within gdb by issuing the 'continue' command")
310 elif cls.debug_gdb:
311 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
312 print("Now is the time to attach a gdb by running the above "
313 "command and set up breakpoints etc.")
314 print(single_line_delim)
Klement Sekerae1ace192018-03-23 21:54:12 +0100315 raw_input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200316
317 @classmethod
318 def run_vpp(cls):
319 cmdline = cls.vpp_cmdline
320
321 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100322 gdbserver = '/usr/bin/gdbserver'
323 if not os.path.isfile(gdbserver) or \
324 not os.access(gdbserver, os.X_OK):
325 raise Exception("gdbserver binary '%s' does not exist or is "
326 "not executable" % gdbserver)
327
328 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200329 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
330
Klement Sekera931be3a2016-11-03 05:36:01 +0100331 try:
332 cls.vpp = subprocess.Popen(cmdline,
333 stdout=subprocess.PIPE,
334 stderr=subprocess.PIPE,
335 bufsize=1)
336 except Exception as e:
337 cls.logger.critical("Couldn't start vpp: %s" % e)
338 raise
339
Klement Sekera277b89c2016-10-28 13:20:27 +0200340 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100341
Damjan Marionf56b77a2016-10-03 19:44:57 +0200342 @classmethod
343 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200344 """
345 Perform class setup before running the testcase
346 Remove shared memory files, start vpp and connect the vpp-api
347 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100348 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100349 random.seed()
juraj.linkes184870a2018-07-16 14:22:01 +0200350 if not hasattr(cls, 'logger'):
351 cls.logger = getLogger(cls.__name__)
352 else:
353 cls.logger.name = cls.__name__
Klement Sekeraf62ae122016-10-11 11:47:09 +0200354 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200355 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera027dbd52017-04-11 06:01:53 +0200356 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
357 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100358 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
359 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200360 cls.file_handler.setLevel(DEBUG)
361 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200362 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200363 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200364 cls.logger.info("Temporary dir is %s, shm prefix is %s",
365 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200366 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100367 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100368 cls._captures = []
369 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200370 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100371 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100372 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200373 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200374 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200375 # need to catch exceptions here because if we raise, then the cleanup
376 # doesn't get called and we might end with a zombie vpp
377 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200378 cls.run_vpp()
Dave Wallacee2efd122017-09-30 22:04:21 -0400379 cls.reporter.send_keep_alive(cls)
Klement Sekerae4504c62016-12-08 10:16:41 +0100380 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100381 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100382 cls.pump_thread_stop_flag = Event()
383 cls.pump_thread_wakeup_pipe = os.pipe()
384 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100385 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100386 cls.pump_thread.start()
Klement Sekera7bb873a2016-11-18 07:38:42 +0100387 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100388 if cls.step:
389 hook = StepHook(cls)
390 else:
391 hook = PollHook(cls)
392 cls.vapi.register_hook(hook)
Ole Troan73202102018-08-31 00:29:48 +0200393 cls.statistics = VPPStats(socketname=cls.tempdir+'/stats.sock')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100394 cls.sleep(0.1, "after vpp startup, before initial poll")
Klement Sekera3747c752017-04-10 06:30:17 +0200395 try:
396 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100397 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200398 cls.vpp_startup_failed = True
399 cls.logger.critical(
400 "VPP died shortly after startup, check the"
401 " output to standard error for possible cause")
402 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100403 try:
404 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100405 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100406 try:
407 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100408 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100409 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100410 if cls.debug_gdbserver:
411 print(colorize("You're running VPP inside gdbserver but "
412 "VPP-API connection failed, did you forget "
413 "to 'continue' VPP from within gdb?", RED))
414 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100415 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100416 try:
417 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100418 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100419 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100420 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200421
Damjan Marionf56b77a2016-10-03 19:44:57 +0200422 @classmethod
423 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200424 """
425 Disconnect vpp-api, kill vpp and cleanup shared memory files
426 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200427 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
428 cls.vpp.poll()
429 if cls.vpp.returncode is None:
430 print(double_line_delim)
431 print("VPP or GDB server is still running")
432 print(single_line_delim)
Klement Sekerae1ace192018-03-23 21:54:12 +0100433 raw_input("When done debugging, press ENTER to kill the "
434 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200435
juraj.linkes184870a2018-07-16 14:22:01 +0200436 # first signal that we want to stop the pump thread, then wake it up
437 if hasattr(cls, 'pump_thread_stop_flag'):
438 cls.pump_thread_stop_flag.set()
439 if hasattr(cls, 'pump_thread_wakeup_pipe'):
440 os.write(cls.pump_thread_wakeup_pipe[1], 'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100441 if hasattr(cls, 'pump_thread'):
442 cls.logger.debug("Waiting for pump thread to stop")
443 cls.pump_thread.join()
444 if hasattr(cls, 'vpp_stderr_reader_thread'):
445 cls.logger.debug("Waiting for stdderr pump to stop")
446 cls.vpp_stderr_reader_thread.join()
447
Klement Sekeraf62ae122016-10-11 11:47:09 +0200448 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100449 if hasattr(cls, 'vapi'):
450 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100451 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200452 cls.vpp.poll()
453 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100454 cls.logger.debug("Sending TERM to vpp")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200455 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100456 cls.logger.debug("Waiting for vpp to die")
457 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200458 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200459
Klement Sekera3747c752017-04-10 06:30:17 +0200460 if cls.vpp_startup_failed:
461 stdout_log = cls.logger.info
462 stderr_log = cls.logger.critical
463 else:
464 stdout_log = cls.logger.info
465 stderr_log = cls.logger.info
466
Klement Sekerae4504c62016-12-08 10:16:41 +0100467 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200468 stdout_log(single_line_delim)
469 stdout_log('VPP output to stdout while running %s:', cls.__name__)
470 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100471 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200472 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
473 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200474 stdout_log('\n%s', vpp_output)
475 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200476
Klement Sekerae4504c62016-12-08 10:16:41 +0100477 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200478 stderr_log(single_line_delim)
479 stderr_log('VPP output to stderr while running %s:', cls.__name__)
480 stderr_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100481 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200482 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
483 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200484 stderr_log('\n%s', vpp_output)
485 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200486
Damjan Marionf56b77a2016-10-03 19:44:57 +0200487 @classmethod
488 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200489 """ Perform final cleanup after running all tests in this test-case """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200490 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200491 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100492 cls.reset_packet_infos()
493 if debug_framework:
494 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200495
Damjan Marionf56b77a2016-10-03 19:44:57 +0200496 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200497 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100498 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
499 (self.__class__.__name__, self._testMethodName,
500 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200501 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200502 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700503 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200504 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200505 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200506 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800507 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100508 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500509 # Save/Dump VPP api trace log
510 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
511 tmp_api_trace = "/tmp/%s" % api_trace
512 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
513 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
514 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
515 vpp_api_trace_log))
516 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500517 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500518 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100519 else:
520 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200521
Damjan Marionf56b77a2016-10-03 19:44:57 +0200522 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200523 """ Clear trace before running each test"""
Klement Sekera909a6a12017-08-08 04:33:53 +0200524 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100525 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
526 (self.__class__.__name__, self._testMethodName,
527 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100528 if self.vpp_dead:
529 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100530 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100531 self.vpp_stdout_deque.append(
532 "--- test setUp() for %s.%s(%s) starts here ---\n" %
533 (self.__class__.__name__, self._testMethodName,
534 self._testMethodDoc))
535 self.vpp_stderr_deque.append(
536 "--- test setUp() for %s.%s(%s) starts here ---\n" %
537 (self.__class__.__name__, self._testMethodName,
538 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200539 self.vapi.cli("clear trace")
540 # store the test instance inside the test class - so that objects
541 # holding the class can access instance methods (like assertEqual)
542 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200543
Damjan Marionf56b77a2016-10-03 19:44:57 +0200544 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200545 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200546 """
547 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200548
Klement Sekera75e7d132017-09-20 08:26:30 +0200549 :param interfaces: iterable interface indexes (if None,
550 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200551
Klement Sekeraf62ae122016-10-11 11:47:09 +0200552 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200553 if interfaces is None:
554 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200555 for i in interfaces:
556 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200557
Damjan Marionf56b77a2016-10-03 19:44:57 +0200558 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100559 def register_capture(cls, cap_name):
560 """ Register a capture in the testclass """
561 # add to the list of captures with current timestamp
562 cls._captures.append((time.time(), cap_name))
563 # filter out from zombies
564 cls._zombie_captures = [(stamp, name)
565 for (stamp, name) in cls._zombie_captures
566 if name != cap_name]
567
568 @classmethod
569 def pg_start(cls):
570 """ Remove any zombie captures and enable the packet generator """
571 # how long before capture is allowed to be deleted - otherwise vpp
572 # crashes - 100ms seems enough (this shouldn't be needed at all)
573 capture_ttl = 0.1
574 now = time.time()
575 for stamp, cap_name in cls._zombie_captures:
576 wait = stamp + capture_ttl - now
577 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100578 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100579 now = time.time()
580 cls.logger.debug("Removing zombie capture %s" % cap_name)
581 cls.vapi.cli('packet-generator delete %s' % cap_name)
582
Klement Sekeraf62ae122016-10-11 11:47:09 +0200583 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
584 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100585 cls._zombie_captures = cls._captures
586 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200587
Damjan Marionf56b77a2016-10-03 19:44:57 +0200588 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200589 def create_pg_interfaces(cls, interfaces):
590 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100591 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200592
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100593 :param interfaces: iterable indexes of the interfaces.
594 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200595
Klement Sekeraf62ae122016-10-11 11:47:09 +0200596 """
597 result = []
598 for i in interfaces:
599 intf = VppPGInterface(cls, i)
600 setattr(cls, intf.name, intf)
601 result.append(intf)
602 cls.pg_interfaces = result
603 return result
604
Matej Klotton0178d522016-11-04 11:11:44 +0100605 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200606 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100607 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100608 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100609
Klement Sekerab9ef2732018-06-24 22:49:33 +0200610 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100611 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100612 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200613 result = [VppLoInterface(cls) for i in range(count)]
614 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100615 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100616 cls.lo_interfaces = result
617 return result
618
Damjan Marionf56b77a2016-10-03 19:44:57 +0200619 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200620 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200621 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200622 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200623 NOTE: Currently works only when Raw layer is present.
624
625 :param packet: packet
626 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200627 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200628
629 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200630 packet_len = len(packet) + 4
631 extend = size - packet_len
632 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200633 num = (extend / len(padding)) + 1
634 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200635
Klement Sekeradab231a2016-12-21 08:50:14 +0100636 @classmethod
637 def reset_packet_infos(cls):
638 """ Reset the list of packet info objects and packet counts to zero """
639 cls._packet_infos = {}
640 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200641
Klement Sekeradab231a2016-12-21 08:50:14 +0100642 @classmethod
643 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200644 """
645 Create packet info object containing the source and destination indexes
646 and add it to the testcase's packet info list
647
Klement Sekeradab231a2016-12-21 08:50:14 +0100648 :param VppInterface src_if: source interface
649 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200650
651 :returns: _PacketInfo object
652
653 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200654 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100655 info.index = len(cls._packet_infos)
656 info.src = src_if.sw_if_index
657 info.dst = dst_if.sw_if_index
658 if isinstance(dst_if, VppSubInterface):
659 dst_idx = dst_if.parent.sw_if_index
660 else:
661 dst_idx = dst_if.sw_if_index
662 if dst_idx in cls._packet_count_for_dst_if_idx:
663 cls._packet_count_for_dst_if_idx[dst_idx] += 1
664 else:
665 cls._packet_count_for_dst_if_idx[dst_idx] = 1
666 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200667 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200668
Damjan Marionf56b77a2016-10-03 19:44:57 +0200669 @staticmethod
670 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200671 """
672 Convert _PacketInfo object to packet payload
673
674 :param info: _PacketInfo object
675
676 :returns: string containing serialized data from packet info
677 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100678 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
679 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200680
Damjan Marionf56b77a2016-10-03 19:44:57 +0200681 @staticmethod
682 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200683 """
684 Convert packet payload to _PacketInfo object
685
686 :param payload: packet payload
687
688 :returns: _PacketInfo object containing de-serialized data from payload
689
690 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200691 numbers = payload.split()
692 info = _PacketInfo()
693 info.index = int(numbers[0])
694 info.src = int(numbers[1])
695 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100696 info.ip = int(numbers[3])
697 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200698 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200699
Damjan Marionf56b77a2016-10-03 19:44:57 +0200700 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200701 """
702 Iterate over the packet info list stored in the testcase
703 Start iteration with first element if info is None
704 Continue based on index in info if info is specified
705
706 :param info: info or None
707 :returns: next info in list or None if no more infos
708 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200709 if info is None:
710 next_index = 0
711 else:
712 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100713 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200714 return None
715 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100716 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200717
Klement Sekeraf62ae122016-10-11 11:47:09 +0200718 def get_next_packet_info_for_interface(self, src_index, info):
719 """
720 Search the packet info list for the next packet info with same source
721 interface index
722
723 :param src_index: source interface index to search for
724 :param info: packet info - where to start the search
725 :returns: packet info or None
726
727 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200728 while True:
729 info = self.get_next_packet_info(info)
730 if info is None:
731 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200732 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200733 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200734
Klement Sekeraf62ae122016-10-11 11:47:09 +0200735 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
736 """
737 Search the packet info list for the next packet info with same source
738 and destination interface indexes
739
740 :param src_index: source interface index to search for
741 :param dst_index: destination interface index to search for
742 :param info: packet info - where to start the search
743 :returns: packet info or None
744
745 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200746 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200747 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200748 if info is None:
749 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200750 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200751 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200752
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200753 def assert_equal(self, real_value, expected_value, name_or_class=None):
754 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100755 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200756 return
757 try:
758 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
759 msg = msg % (getdoc(name_or_class).strip(),
760 real_value, str(name_or_class(real_value)),
761 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100762 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200763 msg = "Invalid %s: %s does not match expected value %s" % (
764 name_or_class, real_value, expected_value)
765
766 self.assertEqual(real_value, expected_value, msg)
767
Klement Sekerab17dd962017-01-09 07:43:48 +0100768 def assert_in_range(self,
769 real_value,
770 expected_min,
771 expected_max,
772 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200773 if name is None:
774 msg = None
775 else:
776 msg = "Invalid %s: %s out of range <%s,%s>" % (
777 name, real_value, expected_min, expected_max)
778 self.assertTrue(expected_min <= real_value <= expected_max, msg)
779
Klement Sekerad81ae412018-05-16 10:52:54 +0200780 def assert_packet_checksums_valid(self, packet,
781 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200782 received = packet.__class__(str(packet))
783 self.logger.debug(
784 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200785 udp_layers = ['UDP', 'UDPerror']
786 checksum_fields = ['cksum', 'chksum']
787 checksums = []
788 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200789 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200790 while True:
791 layer = temp.getlayer(counter)
792 if layer:
793 for cf in checksum_fields:
794 if hasattr(layer, cf):
795 if ignore_zero_udp_checksums and \
796 0 == getattr(layer, cf) and \
797 layer.name in udp_layers:
798 continue
799 delattr(layer, cf)
800 checksums.append((counter, cf))
801 else:
802 break
803 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200804 if 0 == len(checksums):
805 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200806 temp = temp.__class__(str(temp))
807 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200808 calc_sum = getattr(temp[layer], cf)
809 self.assert_equal(
810 getattr(received[layer], cf), calc_sum,
811 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
812 self.logger.debug(
813 "Checksum field `%s` on `%s` layer has correct value `%s`" %
814 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200815
816 def assert_checksum_valid(self, received_packet, layer,
817 field_name='chksum',
818 ignore_zero_checksum=False):
819 """ Check checksum of received packet on given layer """
820 received_packet_checksum = getattr(received_packet[layer], field_name)
821 if ignore_zero_checksum and 0 == received_packet_checksum:
822 return
823 recalculated = received_packet.__class__(str(received_packet))
824 delattr(recalculated[layer], field_name)
825 recalculated = recalculated.__class__(str(recalculated))
826 self.assert_equal(received_packet_checksum,
827 getattr(recalculated[layer], field_name),
828 "packet checksum on layer: %s" % layer)
829
830 def assert_ip_checksum_valid(self, received_packet,
831 ignore_zero_checksum=False):
832 self.assert_checksum_valid(received_packet, 'IP',
833 ignore_zero_checksum=ignore_zero_checksum)
834
835 def assert_tcp_checksum_valid(self, received_packet,
836 ignore_zero_checksum=False):
837 self.assert_checksum_valid(received_packet, 'TCP',
838 ignore_zero_checksum=ignore_zero_checksum)
839
840 def assert_udp_checksum_valid(self, received_packet,
841 ignore_zero_checksum=True):
842 self.assert_checksum_valid(received_packet, 'UDP',
843 ignore_zero_checksum=ignore_zero_checksum)
844
845 def assert_embedded_icmp_checksum_valid(self, received_packet):
846 if received_packet.haslayer(IPerror):
847 self.assert_checksum_valid(received_packet, 'IPerror')
848 if received_packet.haslayer(TCPerror):
849 self.assert_checksum_valid(received_packet, 'TCPerror')
850 if received_packet.haslayer(UDPerror):
851 self.assert_checksum_valid(received_packet, 'UDPerror',
852 ignore_zero_checksum=True)
853 if received_packet.haslayer(ICMPerror):
854 self.assert_checksum_valid(received_packet, 'ICMPerror')
855
856 def assert_icmp_checksum_valid(self, received_packet):
857 self.assert_checksum_valid(received_packet, 'ICMP')
858 self.assert_embedded_icmp_checksum_valid(received_packet)
859
860 def assert_icmpv6_checksum_valid(self, pkt):
861 if pkt.haslayer(ICMPv6DestUnreach):
862 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
863 self.assert_embedded_icmp_checksum_valid(pkt)
864 if pkt.haslayer(ICMPv6EchoRequest):
865 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
866 if pkt.haslayer(ICMPv6EchoReply):
867 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
868
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100869 @classmethod
870 def sleep(cls, timeout, remark=None):
871 if hasattr(cls, 'logger'):
Klement Sekera3cfa5582017-04-19 07:10:58 +0000872 cls.logger.debug("Starting sleep for %ss (%s)" % (timeout, remark))
873 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100874 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000875 after = time.time()
876 if after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200877 cls.logger.error("unexpected time.sleep() result - "
878 "slept for %ss instead of ~%ss!" % (
879 after - before, timeout))
Klement Sekera3cfa5582017-04-19 07:10:58 +0000880 if hasattr(cls, 'logger'):
881 cls.logger.debug(
882 "Finished sleep (%s) - slept %ss (wanted %ss)" % (
883 remark, after - before, timeout))
Klement Sekeraa57a9702017-02-02 06:58:07 +0100884
Neale Ranns947ea622018-06-07 23:48:20 -0700885 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800886 self.vapi.cli("clear trace")
887 intf.add_stream(pkts)
888 self.pg_enable_capture(self.pg_interfaces)
889 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700890 if not timeout:
891 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800892 for i in self.pg_interfaces:
893 i.get_capture(0, timeout=timeout)
894 i.assert_nothing_captured(remark=remark)
895 timeout = 0.1
896
897 def send_and_expect(self, input, pkts, output):
898 self.vapi.cli("clear trace")
899 input.add_stream(pkts)
900 self.pg_enable_capture(self.pg_interfaces)
901 self.pg_start()
902 rx = output.get_capture(len(pkts))
903 return rx
904
Damjan Marionf56b77a2016-10-03 19:44:57 +0200905
juraj.linkes184870a2018-07-16 14:22:01 +0200906def get_testcase_doc_name(test):
907 return getdoc(test.__class__).splitlines()[0]
908
909
910def get_test_description(descriptions, test):
911 # TODO: if none print warning not raise exception
912 short_description = test.shortDescription()
913 if descriptions and short_description:
914 return short_description
915 else:
916 return str(test)
917
918
Klement Sekera87134932017-03-07 11:39:27 +0100919class TestCasePrinter(object):
920 _shared_state = {}
921
922 def __init__(self):
923 self.__dict__ = self._shared_state
924 if not hasattr(self, "_test_case_set"):
925 self._test_case_set = set()
926
927 def print_test_case_heading_if_first_time(self, case):
928 if case.__class__ not in self._test_case_set:
929 print(double_line_delim)
juraj.linkes184870a2018-07-16 14:22:01 +0200930 print(colorize(get_testcase_doc_name(case), GREEN))
Klement Sekera87134932017-03-07 11:39:27 +0100931 print(double_line_delim)
932 self._test_case_set.add(case.__class__)
933
934
Damjan Marionf56b77a2016-10-03 19:44:57 +0200935class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200936 """
937 @property result_string
938 String variable to store the test case result string.
939 @property errors
940 List variable containing 2-tuples of TestCase instances and strings
941 holding formatted tracebacks. Each tuple represents a test which
942 raised an unexpected exception.
943 @property failures
944 List variable containing 2-tuples of TestCase instances and strings
945 holding formatted tracebacks. Each tuple represents a test where
946 a failure was explicitly signalled using the TestCase.assert*()
947 methods.
948 """
949
Damjan Marionf56b77a2016-10-03 19:44:57 +0200950 def __init__(self, stream, descriptions, verbosity):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200951 """
Klement Sekerada505f62017-01-04 12:58:53 +0100952 :param stream File descriptor to store where to report test results.
953 Set to the standard error stream by default.
954 :param descriptions Boolean variable to store information if to use
955 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200956 :param verbosity Integer variable to store required verbosity level.
957 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200958 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
959 self.stream = stream
960 self.descriptions = descriptions
961 self.verbosity = verbosity
962 self.result_string = None
Klement Sekera87134932017-03-07 11:39:27 +0100963 self.printer = TestCasePrinter()
juraj.linkes0219b8d2018-08-24 16:16:28 +0200964 self.passed = 0
Damjan Marionf56b77a2016-10-03 19:44:57 +0200965
Damjan Marionf56b77a2016-10-03 19:44:57 +0200966 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200967 """
968 Record a test succeeded result
969
970 :param test:
971
972 """
Klement Sekerab91017a2017-02-09 06:04:36 +0100973 if hasattr(test, 'logger'):
974 test.logger.debug("--- addSuccess() %s.%s(%s) called"
975 % (test.__class__.__name__,
976 test._testMethodName,
977 test._testMethodDoc))
juraj.linkes0219b8d2018-08-24 16:16:28 +0200978 self.passed += 1
Damjan Marionf56b77a2016-10-03 19:44:57 +0200979 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +0200980 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200981
Klement Sekeraf62ae122016-10-11 11:47:09 +0200982 def addSkip(self, test, reason):
983 """
984 Record a test skipped.
985
986 :param test:
987 :param reason:
988
989 """
Klement Sekerab91017a2017-02-09 06:04:36 +0100990 if hasattr(test, 'logger'):
991 test.logger.debug("--- addSkip() %s.%s(%s) called, reason is %s"
992 % (test.__class__.__name__,
993 test._testMethodName,
994 test._testMethodDoc,
995 reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200996 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +0200997 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200998
Klement Sekeraf413bef2017-08-15 07:09:02 +0200999 def symlink_failed(self, test):
1000 logger = None
1001 if hasattr(test, 'logger'):
1002 logger = test.logger
1003 if hasattr(test, 'tempdir'):
1004 try:
1005 failed_dir = os.getenv('VPP_TEST_FAILED_DIR')
juraj.linkes184870a2018-07-16 14:22:01 +02001006 link_path = os.path.join(failed_dir, '%s-FAILED' %
1007 os.path.basename(test.tempdir))
Klement Sekeraf413bef2017-08-15 07:09:02 +02001008 if logger:
1009 logger.debug("creating a link to the failed test")
1010 logger.debug("os.symlink(%s, %s)" %
1011 (test.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001012 if os.path.exists(link_path):
1013 if logger:
1014 logger.debug('symlink already exists')
1015 else:
1016 os.symlink(test.tempdir, link_path)
1017
Klement Sekeraf413bef2017-08-15 07:09:02 +02001018 except Exception as e:
1019 if logger:
1020 logger.error(e)
1021
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001022 def send_results_through_pipe(self):
1023 if hasattr(self, 'test_framework_results_pipe'):
1024 pipe = self.test_framework_results_pipe
1025 if pipe:
1026 pipe.send(self)
1027
Damjan Marionf56b77a2016-10-03 19:44:57 +02001028 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001029 """
1030 Record a test failed result
1031
1032 :param test:
1033 :param err: error message
1034
1035 """
Klement Sekerab91017a2017-02-09 06:04:36 +01001036 if hasattr(test, 'logger'):
1037 test.logger.debug("--- addFailure() %s.%s(%s) called, err is %s"
1038 % (test.__class__.__name__,
1039 test._testMethodName,
1040 test._testMethodDoc, err))
1041 test.logger.debug("formatted exception is:\n%s" %
1042 "".join(format_exception(*err)))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001043 unittest.TestResult.addFailure(self, test, err)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001044 if hasattr(test, 'tempdir'):
Klement Sekera277b89c2016-10-28 13:20:27 +02001045 self.result_string = colorize("FAIL", RED) + \
Klement Sekeraf62ae122016-10-11 11:47:09 +02001046 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
Klement Sekeraf413bef2017-08-15 07:09:02 +02001047 self.symlink_failed(test)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001048 else:
Klement Sekera277b89c2016-10-28 13:20:27 +02001049 self.result_string = colorize("FAIL", RED) + ' [no temp dir]'
Damjan Marionf56b77a2016-10-03 19:44:57 +02001050
Damjan Marionf56b77a2016-10-03 19:44:57 +02001051 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001052 """
1053 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001054
Klement Sekeraf62ae122016-10-11 11:47:09 +02001055 :param test:
1056 :param err: error message
1057
1058 """
Klement Sekerab91017a2017-02-09 06:04:36 +01001059 if hasattr(test, 'logger'):
1060 test.logger.debug("--- addError() %s.%s(%s) called, err is %s"
1061 % (test.__class__.__name__,
1062 test._testMethodName,
1063 test._testMethodDoc, err))
1064 test.logger.debug("formatted exception is:\n%s" %
1065 "".join(format_exception(*err)))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001066 unittest.TestResult.addError(self, test, err)
1067 if hasattr(test, 'tempdir'):
Klement Sekera277b89c2016-10-28 13:20:27 +02001068 self.result_string = colorize("ERROR", RED) + \
Klement Sekeraf62ae122016-10-11 11:47:09 +02001069 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
Klement Sekeraf413bef2017-08-15 07:09:02 +02001070 self.symlink_failed(test)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001071 else:
Klement Sekera277b89c2016-10-28 13:20:27 +02001072 self.result_string = colorize("ERROR", RED) + ' [no temp dir]'
Klement Sekeraf62ae122016-10-11 11:47:09 +02001073
Damjan Marionf56b77a2016-10-03 19:44:57 +02001074 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001075 """
1076 Get test description
1077
1078 :param test:
1079 :returns: test description
1080
1081 """
juraj.linkes184870a2018-07-16 14:22:01 +02001082 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001083
Damjan Marionf56b77a2016-10-03 19:44:57 +02001084 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001085 """
1086 Start a test
1087
1088 :param test:
1089
1090 """
Klement Sekera87134932017-03-07 11:39:27 +01001091 self.printer.print_test_case_heading_if_first_time(test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001092 unittest.TestResult.startTest(self, test)
1093 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001094 self.stream.writeln(
1095 "Starting " + self.getDescription(test) + " ...")
1096 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001097
Damjan Marionf56b77a2016-10-03 19:44:57 +02001098 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001099 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001100 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001101
1102 :param test:
1103
1104 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001105 unittest.TestResult.stopTest(self, test)
1106 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001107 self.stream.writeln(single_line_delim)
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))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001110 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001111 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001112 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001113 self.result_string))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001114 self.send_results_through_pipe()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001115
Damjan Marionf56b77a2016-10-03 19:44:57 +02001116 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001117 """
1118 Print errors from running the test case
1119 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001120 self.stream.writeln()
1121 self.printErrorList('ERROR', self.errors)
1122 self.printErrorList('FAIL', self.failures)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001123
Damjan Marionf56b77a2016-10-03 19:44:57 +02001124 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001125 """
1126 Print error list to the output stream together with error type
1127 and test case description.
1128
1129 :param flavour: error type
1130 :param errors: iterable errors
1131
1132 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001133 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001134 self.stream.writeln(double_line_delim)
1135 self.stream.writeln("%s: %s" %
1136 (flavour, self.getDescription(test)))
1137 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001138 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001139
1140
Damjan Marionf56b77a2016-10-03 19:44:57 +02001141class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001142 """
Klement Sekera104543f2017-02-03 07:29:43 +01001143 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001144 """
1145 @property
1146 def resultclass(self):
1147 """Class maintaining the results of the tests"""
1148 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001149
juraj.linkes184870a2018-07-16 14:22:01 +02001150 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001151 results_pipe=None, failfast=False, buffer=False,
1152 resultclass=None):
Klement Sekera7a161da2017-01-17 13:42:48 +01001153 # ignore stream setting here, use hard-coded stdout to be in sync
1154 # with prints from VppTestCase methods ...
1155 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1156 verbosity, failfast, buffer,
1157 resultclass)
Klement Sekera909a6a12017-08-08 04:33:53 +02001158 reporter = KeepAliveReporter()
Klement Sekeradf2b9802017-10-05 10:26:03 +02001159 reporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001160
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001161 VppTestResult.test_framework_results_pipe = results_pipe
1162
Damjan Marionf56b77a2016-10-03 19:44:57 +02001163 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001164 """
1165 Run the tests
1166
1167 :param test:
1168
1169 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001170 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001171
1172 result = super(VppTestRunner, self).run(test)
1173 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001174
1175
1176class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001177 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001178 self.logger = logger
1179 self.args = args
1180 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001181 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001182 super(Worker, self).__init__()
1183
1184 def run(self):
1185 executable = self.args[0]
1186 self.logger.debug("Running executable w/args `%s'" % self.args)
1187 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001188 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001189 env["CK_LOG_FILE_NAME"] = "-"
1190 self.process = subprocess.Popen(
1191 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1192 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1193 out, err = self.process.communicate()
1194 self.logger.debug("Finished running `%s'" % executable)
1195 self.logger.info("Return code is `%s'" % self.process.returncode)
1196 self.logger.info(single_line_delim)
1197 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1198 self.logger.info(single_line_delim)
1199 self.logger.info(out)
1200 self.logger.info(single_line_delim)
1201 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1202 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001203 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001204 self.logger.info(single_line_delim)
1205 self.result = self.process.returncode