blob: dc08ad08d5a52a1cefc087f751f03bb7b64689bf [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01003from __future__ import print_function
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05004import logging
Paul Vinciguerra72f00042018-11-25 11:05:13 -08005import sys
Ole Trøan162989e2018-11-26 10:27:50 +00006import os
7import select
Paul Vinciguerra496b0de2019-06-20 12:24:12 -04008import signal
Paul Vinciguerrad6f22172020-12-05 22:39:14 +00009import subprocess
Ole Trøan162989e2018-11-26 10:27:50 +000010import unittest
Klement Sekerab23ffd72021-05-31 16:08:53 +020011import re
Klement Sekera277b89c2016-10-28 13:20:27 +020012import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080013import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000014import random
15import copy
juraj.linkes68ebc832018-11-29 09:37:08 +010016import platform
Klement Sekerab23ffd72021-05-31 16:08:53 +020017import shutil
Ole Trøan162989e2018-11-26 10:27:50 +000018from collections import deque
19from threading import Thread, Event
20from inspect import getdoc, isclass
21from traceback import format_exception
22from logging import FileHandler, DEBUG, Formatter
Andrew Yourtchenko06f32812021-01-14 10:19:08 +000023from enum import Enum
Klement Sekera558ceab2021-04-08 19:37:41 +020024from abc import ABC, abstractmethod
Ray Kinsellab8165b92021-09-22 11:24:06 +010025from struct import pack, unpack
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070026
27import scapy.compat
Klement Sekera56c492a2022-01-10 21:57:27 +000028from scapy.packet import Raw, Packet
Klement Sekerab23ffd72021-05-31 16:08:53 +020029from config import config, available_cpus, num_cpus, max_vpp_cpus
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040030import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080031from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010032from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000033from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070034from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000035from vpp_papi_provider import VppPapiProvider
Neale Ranns6197cb72021-06-03 14:43:21 +000036from vpp_papi import VppEnum
Paul Vinciguerra1043fd32019-12-02 21:42:28 -050037import vpp_papi
Ole Trøan162989e2018-11-26 10:27:50 +000038from vpp_papi.vpp_stats import VPPStats
Ole Troan4376ab22021-03-03 10:40:05 +010039from vpp_papi.vpp_transport_socket import VppTransportSocketIOError
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020040from log import (
41 RED,
42 GREEN,
43 YELLOW,
44 double_line_delim,
45 single_line_delim,
46 get_logger,
47 colorize,
48)
Ole Trøan162989e2018-11-26 10:27:50 +000049from vpp_object import VppObjectRegistry
50from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020051from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
52from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
53from scapy.layers.inet6 import ICMPv6EchoReply
Naveen Joyc872cec2022-08-30 13:59:03 -070054from vpp_running import use_running
Klement Sekera47f35272023-03-29 16:04:58 +020055from test_result_code import TestResultCode
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080056
Klement Sekera558ceab2021-04-08 19:37:41 +020057
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050058logger = logging.getLogger(__name__)
59
60# Set up an empty logger for the testcase that can be overridden as necessary
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020061null_logger = logging.getLogger("VppTestCase")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050062null_logger.addHandler(logging.NullHandler())
63
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040064
Klement Sekerab23ffd72021-05-31 16:08:53 +020065if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010066 import debug_internal
67
Klement Sekeraf62ae122016-10-11 11:47:09 +020068"""
69 Test framework module.
70
71 The module provides a set of tools for constructing and running tests and
72 representing the results.
73"""
74
Klement Sekeraf62ae122016-10-11 11:47:09 +020075
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040076class VppDiedError(Exception):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020077 """exception for reporting that the subprocess has died."""
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040078
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020079 signals_by_value = {
80 v: k
81 for k, v in signal.__dict__.items()
82 if k.startswith("SIG") and not k.startswith("SIG_")
83 }
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040084
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040085 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040086 self.rv = rv
87 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040088 self.testcase = testcase
89 self.method_name = method_name
90
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040091 try:
92 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -040093 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040094 pass
95
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040096 if testcase is None and method_name is None:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020097 in_msg = ""
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040098 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020099 in_msg = " while running %s.%s" % (testcase, method_name)
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400100
Klement Sekera79a31db2021-03-12 18:16:10 +0100101 if self.rv:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200102 msg = "VPP subprocess died unexpectedly%s with return code: %d%s." % (
103 in_msg,
104 self.rv,
105 " [%s]" % (self.signal_name if self.signal_name is not None else ""),
106 )
Klement Sekera79a31db2021-03-12 18:16:10 +0100107 else:
108 msg = "VPP subprocess died unexpectedly%s." % in_msg
109
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400110 super(VppDiedError, self).__init__(msg)
111
112
Damjan Marionf56b77a2016-10-03 19:44:57 +0200113class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200114 """Private class to create packet info object.
115
116 Help process information about the next packet.
117 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200118 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200119
Matej Klotton86d87c42016-11-11 11:38:55 +0100120 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200121 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100122 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200123 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100124 #: Store the index of the destination packet generator interface
125 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200126 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100127 #: Store expected ip version
128 ip = -1
129 #: Store expected upper protocol
130 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100131 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200132 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200133
Matej Klotton16a14cd2016-12-07 15:09:13 +0100134 def __eq__(self, other):
135 index = self.index == other.index
136 src = self.src == other.src
137 dst = self.dst == other.dst
138 data = self.data == other.data
139 return index and src and dst and data
140
Klement Sekeraf62ae122016-10-11 11:47:09 +0200141
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100142def pump_output(testclass):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200143 """pump output from vpp stdout/stderr to proper queues"""
Dave Wallace670724c2022-09-20 21:52:18 -0400144 if not hasattr(testclass, "vpp"):
145 return
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100146 stdout_fragment = ""
147 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400148 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200149 readable = select.select(
150 [
151 testclass.vpp.stdout.fileno(),
152 testclass.vpp.stderr.fileno(),
153 testclass.pump_thread_wakeup_pipe[0],
154 ],
155 [],
156 [],
157 )[0]
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100158 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100159 read = os.read(testclass.vpp.stdout.fileno(), 102400)
160 if len(read) > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200161 split = read.decode("ascii", errors="backslashreplace").splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100162 if len(stdout_fragment) > 0:
163 split[0] = "%s%s" % (stdout_fragment, split[0])
164 if len(split) > 0 and split[-1].endswith("\n"):
165 limit = None
166 else:
167 limit = -1
168 stdout_fragment = split[-1]
169 testclass.vpp_stdout_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200170 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100171 for line in split[:limit]:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200172 testclass.logger.info("VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100173 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100174 read = os.read(testclass.vpp.stderr.fileno(), 102400)
175 if len(read) > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200176 split = read.decode("ascii", errors="backslashreplace").splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100177 if len(stderr_fragment) > 0:
178 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200179 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100180 limit = None
181 else:
182 limit = -1
183 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200184
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100185 testclass.vpp_stderr_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200186 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100187 for line in split[:limit]:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200188 testclass.logger.error("VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800189 # ignoring the dummy pipe here intentionally - the
190 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200191
192
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800193def _is_platform_aarch64():
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200194 return platform.machine() == "aarch64"
juraj.linkes68ebc832018-11-29 09:37:08 +0100195
Klement Sekera6aa58b72019-05-16 14:34:55 +0200196
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800197is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100198
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800199
Dave Wallacee95b2462022-09-18 22:28:44 -0400200def _is_distro_ubuntu2204():
201 with open("/etc/os-release") as f:
202 for line in f.readlines():
203 if "jammy" in line:
204 return True
205 return False
206
207
208is_distro_ubuntu2204 = _is_distro_ubuntu2204()
209
210
Dave Wallace670724c2022-09-20 21:52:18 -0400211def _is_distro_debian11():
212 with open("/etc/os-release") as f:
213 for line in f.readlines():
214 if "bullseye" in line:
215 return True
216 return False
217
218
219is_distro_debian11 = _is_distro_debian11()
220
221
Klement Sekera909a6a12017-08-08 04:33:53 +0200222class KeepAliveReporter(object):
223 """
224 Singleton object which reports test start to parent process
225 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200226
Klement Sekera909a6a12017-08-08 04:33:53 +0200227 _shared_state = {}
228
229 def __init__(self):
230 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800231 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200232
233 @property
234 def pipe(self):
235 return self._pipe
236
237 @pipe.setter
238 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800239 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200240 raise Exception("Internal error - pipe should only be set once.")
241 self._pipe = pipe
242
juraj.linkes40dd73b2018-09-21 13:55:16 +0200243 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200244 """
245 Write current test tmpdir & desc to keep-alive pipe to signal liveness
246 """
Dave Wallace670724c2022-09-20 21:52:18 -0400247 if not hasattr(test, "vpp") or self.pipe is None:
Klement Sekera3f6ff192017-08-11 06:56:05 +0200248 # if not running forked..
249 return
250
Klement Sekera909a6a12017-08-08 04:33:53 +0200251 if isclass(test):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200252 desc = "%s (%s)" % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200253 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200254 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200255
Klement Sekerab23ffd72021-05-31 16:08:53 +0200256 self.pipe.send((desc, config.vpp, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200257
258
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000259class TestCaseTag(Enum):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000260 # marks the suites that must run at the end
261 # using only a single test runner
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000262 RUN_SOLO = 1
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000263 # marks the suites broken on VPP multi-worker
264 FIXME_VPP_WORKERS = 2
Naveen Joy6eaeea92021-09-09 17:57:02 -0700265 # marks the suites broken when ASan is enabled
266 FIXME_ASAN = 3
Dave Wallacee95b2462022-09-18 22:28:44 -0400267 # marks suites broken on Ubuntu-22.04
268 FIXME_UBUNTU2204 = 4
Dave Wallace670724c2022-09-20 21:52:18 -0400269 # marks suites broken on Debian-11
270 FIXME_DEBIAN11 = 5
Dave Wallace8a0a9d22022-10-04 22:02:49 -0400271 # marks suites broken on debug vpp image
272 FIXME_VPP_DEBUG = 6
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000273
274
275def create_tag_decorator(e):
276 def decorator(cls):
277 try:
278 cls.test_tags.append(e)
279 except AttributeError:
280 cls.test_tags = [e]
281 return cls
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200282
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000283 return decorator
284
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000285
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000286tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000287tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Naveen Joy6eaeea92021-09-09 17:57:02 -0700288tag_fixme_asan = create_tag_decorator(TestCaseTag.FIXME_ASAN)
Dave Wallacee95b2462022-09-18 22:28:44 -0400289tag_fixme_ubuntu2204 = create_tag_decorator(TestCaseTag.FIXME_UBUNTU2204)
Dave Wallace670724c2022-09-20 21:52:18 -0400290tag_fixme_debian11 = create_tag_decorator(TestCaseTag.FIXME_DEBIAN11)
Dave Wallace8a0a9d22022-10-04 22:02:49 -0400291tag_fixme_vpp_debug = create_tag_decorator(TestCaseTag.FIXME_VPP_DEBUG)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000292
293
Klement Sekerae2636852021-03-16 12:52:12 +0100294class DummyVpp:
295 returncode = None
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200296 pid = 0xCAFEBAFE
Klement Sekerae2636852021-03-16 12:52:12 +0100297
298 def poll(self):
299 pass
300
301 def terminate(self):
302 pass
303
304
Klement Sekera558ceab2021-04-08 19:37:41 +0200305class CPUInterface(ABC):
306 cpus = []
307 skipped_due_to_cpu_lack = False
308
309 @classmethod
310 @abstractmethod
311 def get_cpus_required(cls):
312 pass
313
314 @classmethod
315 def assign_cpus(cls, cpus):
316 cls.cpus = cpus
317
318
Naveen Joyc872cec2022-08-30 13:59:03 -0700319@use_running
Klement Sekera558ceab2021-04-08 19:37:41 +0200320class VppTestCase(CPUInterface, unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100321 """This subclass is a base class for VPP test cases that are implemented as
322 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200323 """
324
Arthur de Kerhordb023802021-03-11 10:26:54 -0800325 extra_vpp_statseg_config = ""
Klement Sekerad3e0d102023-01-26 12:35:35 +0100326 extra_vpp_config = []
Ole Troana45dc072018-12-21 16:04:22 +0100327 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500328 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400329 vapi_response_timeout = 5
Klement Sekera140af152022-02-18 10:30:51 +0000330 remove_configured_vpp_objects_on_tear_down = True
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100331
Klement Sekeraf62ae122016-10-11 11:47:09 +0200332 @property
333 def packet_infos(self):
334 """List of packet infos"""
335 return self._packet_infos
336
Klement Sekeradab231a2016-12-21 08:50:14 +0100337 @classmethod
338 def get_packet_count_for_if_idx(cls, dst_if_index):
339 """Get the number of packet info for specified destination if index"""
340 if dst_if_index in cls._packet_count_for_dst_if_idx:
341 return cls._packet_count_for_dst_if_idx[dst_if_index]
342 else:
343 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200344
345 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000346 def has_tag(cls, tag):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200347 """if the test case has a given tag - return true"""
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000348 try:
349 return tag in cls.test_tags
350 except AttributeError:
351 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000352 return False
353
354 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000355 def is_tagged_run_solo(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200356 """if the test case class is timing-sensitive - return true"""
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000357 return cls.has_tag(TestCaseTag.RUN_SOLO)
358
359 @classmethod
Naveen Joy6eaeea92021-09-09 17:57:02 -0700360 def skip_fixme_asan(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200361 """if @tag_fixme_asan & ASan is enabled - mark for skip"""
Naveen Joy6eaeea92021-09-09 17:57:02 -0700362 if cls.has_tag(TestCaseTag.FIXME_ASAN):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200363 vpp_extra_cmake_args = os.environ.get("VPP_EXTRA_CMAKE_ARGS", "")
364 if "DVPP_ENABLE_SANITIZE_ADDR=ON" in vpp_extra_cmake_args:
Naveen Joy6eaeea92021-09-09 17:57:02 -0700365 cls = unittest.skip("Skipping @tag_fixme_asan tests")(cls)
366
367 @classmethod
Dave Wallacee95b2462022-09-18 22:28:44 -0400368 def skip_fixme_ubuntu2204(cls):
369 """if distro is ubuntu 22.04 and @tag_fixme_ubuntu2204 mark for skip"""
370 if cls.has_tag(TestCaseTag.FIXME_UBUNTU2204):
371 cls = unittest.skip("Skipping @tag_fixme_ubuntu2204 tests")(cls)
372
373 @classmethod
Dave Wallace670724c2022-09-20 21:52:18 -0400374 def skip_fixme_debian11(cls):
375 """if distro is Debian-11 and @tag_fixme_debian11 mark for skip"""
376 if cls.has_tag(TestCaseTag.FIXME_DEBIAN11):
377 cls = unittest.skip("Skipping @tag_fixme_debian11 tests")(cls)
378
379 @classmethod
Dave Wallace8a0a9d22022-10-04 22:02:49 -0400380 def skip_fixme_vpp_debug(cls):
381 cls = unittest.skip("Skipping @tag_fixme_vpp_debug tests")(cls)
382
383 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200384 def instance(cls):
385 """Return the instance of this testcase"""
386 return cls.test_instance
387
Damjan Marionf56b77a2016-10-03 19:44:57 +0200388 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200389 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000390 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200391 cls.debug_core = False
392 cls.debug_gdb = False
393 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000394 cls.debug_all = False
Klement Sekerae2636852021-03-16 12:52:12 +0100395 cls.debug_attach = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200396 if d is None:
397 return
398 dl = d.lower()
399 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200400 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000401 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200402 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000403 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200404 cls.debug_gdbserver = True
Klement Sekerae2636852021-03-16 12:52:12 +0100405 elif dl == "attach":
406 cls.debug_attach = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200407 else:
408 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000409 if dl == "gdb-all" or dl == "gdbserver-all":
410 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200411
Klement Sekera558ceab2021-04-08 19:37:41 +0200412 @classmethod
413 def get_vpp_worker_count(cls):
414 if not hasattr(cls, "vpp_worker_count"):
415 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
416 cls.vpp_worker_count = 0
417 else:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200418 cls.vpp_worker_count = config.vpp_worker_count
Klement Sekera558ceab2021-04-08 19:37:41 +0200419 return cls.vpp_worker_count
juraj.linkes184870a2018-07-16 14:22:01 +0200420
Klement Sekera558ceab2021-04-08 19:37:41 +0200421 @classmethod
422 def get_cpus_required(cls):
423 return 1 + cls.get_vpp_worker_count()
juraj.linkes184870a2018-07-16 14:22:01 +0200424
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800425 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200426 def setUpConstants(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200427 """Set-up the test case class based on environment variables"""
Klement Sekerab23ffd72021-05-31 16:08:53 +0200428 cls.step = config.step
429 cls.plugin_path = ":".join(config.vpp_plugin_dir)
430 cls.test_plugin_path = ":".join(config.vpp_test_plugin_dir)
431 cls.extern_plugin_path = ":".join(config.extern_plugin_dir)
Ole Troana45dc072018-12-21 16:04:22 +0100432 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100433 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100434 debug_cli = "cli-listen localhost:5002"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200435 size = re.search(r"\d+[gG]", config.coredump_size)
436 if size:
437 coredump_size = f"coredump-size {config.coredump_size}".lower()
438 else:
Ole Troana45dc072018-12-21 16:04:22 +0100439 coredump_size = "coredump-size unlimited"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200440 default_variant = config.variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000441 if default_variant is not None:
Benoît Ganne09ef5922022-07-29 10:52:34 +0200442 default_variant = "default { variant %s 100 }" % default_variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000443 else:
444 default_variant = ""
445
Klement Sekerab23ffd72021-05-31 16:08:53 +0200446 api_fuzzing = config.api_fuzz
Dave Barach77841402020-04-29 17:04:10 -0400447 if api_fuzzing is None:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200448 api_fuzzing = "off"
Dave Barach77841402020-04-29 17:04:10 -0400449
Klement Sekera8d815022021-03-15 16:58:10 +0100450 cls.vpp_cmdline = [
Klement Sekerab23ffd72021-05-31 16:08:53 +0200451 config.vpp,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200452 "unix",
453 "{",
454 "nodaemon",
455 debug_cli,
456 "full-coredump",
457 coredump_size,
458 "runtime-dir",
459 cls.tempdir,
Klement Sekera8d815022021-03-15 16:58:10 +0100460 "}",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200461 "api-trace",
462 "{",
463 "on",
464 "}",
465 "api-segment",
466 "{",
467 "prefix",
468 cls.get_api_segment_prefix(),
469 "}",
470 "cpu",
471 "{",
472 "main-core",
473 str(cls.cpus[0]),
474 ]
475 if cls.extern_plugin_path not in (None, ""):
476 cls.extra_vpp_plugin_config.append("add-path %s" % cls.extern_plugin_path)
477 if cls.get_vpp_worker_count():
478 cls.vpp_cmdline.extend(
479 ["corelist-workers", ",".join([str(x) for x in cls.cpus[1:]])]
480 )
481 cls.vpp_cmdline.extend(
482 [
483 "}",
484 "physmem",
485 "{",
486 "max-size",
487 "32m",
488 "}",
489 "statseg",
490 "{",
491 "socket-name",
492 cls.get_stats_sock_path(),
493 cls.extra_vpp_statseg_config,
494 "}",
495 "socksvr",
496 "{",
497 "socket-name",
498 cls.get_api_sock_path(),
499 "}",
500 "node { ",
501 default_variant,
502 "}",
503 "api-fuzz {",
504 api_fuzzing,
505 "}",
506 "plugins",
507 "{",
508 "plugin",
509 "dpdk_plugin.so",
510 "{",
511 "disable",
512 "}",
513 "plugin",
514 "rdma_plugin.so",
515 "{",
516 "disable",
517 "}",
518 "plugin",
519 "lisp_unittest_plugin.so",
520 "{",
521 "enable",
522 "}",
523 "plugin",
524 "unittest_plugin.so",
525 "{",
526 "enable",
527 "}",
528 ]
529 + cls.extra_vpp_plugin_config
530 + [
531 "}",
532 ]
533 )
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000534
Klement Sekerad3e0d102023-01-26 12:35:35 +0100535 if cls.extra_vpp_config is not None:
536 cls.vpp_cmdline.extend(cls.extra_vpp_config)
Dave Barach7d31ab22019-05-08 19:18:18 -0400537
Klement Sekerae2636852021-03-16 12:52:12 +0100538 if not cls.debug_attach:
539 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
540 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200541
542 @classmethod
543 def wait_for_enter(cls):
544 if cls.debug_gdbserver:
545 print(double_line_delim)
546 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
547 elif cls.debug_gdb:
548 print(double_line_delim)
549 print("Spawned VPP with PID: %d" % cls.vpp.pid)
550 else:
551 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
552 return
553 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000554 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200555 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200556 print(
557 f"sudo gdb {config.vpp} "
558 f"-ex 'target remote localhost:{cls.gdbserver_port}'"
559 )
560 print(
561 "Now is the time to attach gdb by running the above "
562 "command, set up breakpoints etc., then resume VPP from "
563 "within gdb by issuing the 'continue' command"
564 )
Dave Wallace24564332019-10-21 02:53:14 +0000565 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200566 elif cls.debug_gdb:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200567 print(f"sudo gdb {config.vpp} -ex 'attach {cls.vpp.pid}'")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200568 print(
569 "Now is the time to attach gdb by running the above "
570 "command and set up breakpoints etc., then resume VPP from"
571 " within gdb by issuing the 'continue' command"
572 )
Klement Sekera277b89c2016-10-28 13:20:27 +0200573 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800574 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200575
576 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100577 def attach_vpp(cls):
578 cls.vpp = DummyVpp()
579
580 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200581 def run_vpp(cls):
Dave Wallace670724c2022-09-20 21:52:18 -0400582 if (
583 is_distro_ubuntu2204 == True and cls.has_tag(TestCaseTag.FIXME_UBUNTU2204)
584 ) or (is_distro_debian11 == True and cls.has_tag(TestCaseTag.FIXME_DEBIAN11)):
585 return
Klement Sekera558ceab2021-04-08 19:37:41 +0200586 cls.logger.debug(f"Assigned cpus: {cls.cpus}")
Klement Sekera277b89c2016-10-28 13:20:27 +0200587 cmdline = cls.vpp_cmdline
588
589 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200590 gdbserver = "/usr/bin/gdbserver"
591 if not os.path.isfile(gdbserver) or not os.access(gdbserver, os.X_OK):
592 raise Exception(
593 "gdbserver binary '%s' does not exist or is "
594 "not executable" % gdbserver
595 )
Klement Sekera931be3a2016-11-03 05:36:01 +0100596
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200597 cmdline = [
598 gdbserver,
599 "localhost:{port}".format(port=cls.gdbserver_port),
600 ] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200601 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
602
Klement Sekera931be3a2016-11-03 05:36:01 +0100603 try:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200604 cls.vpp = subprocess.Popen(
605 cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE
606 )
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800607 except subprocess.CalledProcessError as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200608 cls.logger.critical(
609 "Subprocess returned with non-0 return code: (%s)", e.returncode
610 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800611 raise
612 except OSError as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200613 cls.logger.critical(
614 "Subprocess returned with OS error: (%s) %s", e.errno, e.strerror
615 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800616 raise
617 except Exception as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200618 cls.logger.exception("Subprocess returned unexpected from %s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100619 raise
620
Klement Sekera277b89c2016-10-28 13:20:27 +0200621 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100622
Damjan Marionf56b77a2016-10-03 19:44:57 +0200623 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000624 def wait_for_coredump(cls):
625 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400626 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000627 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400628 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000629 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400630 ok = False
631 while time.time() < deadline:
632 cls.sleep(1)
633 size = curr_size
634 curr_size = os.path.getsize(corefile)
635 if size == curr_size:
636 ok = True
637 break
638 if not ok:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200639 cls.logger.error(
640 "Timed out waiting for coredump to complete: %s", corefile
641 )
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400642 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200643 cls.logger.error("Coredump complete: %s, size %d", corefile, curr_size)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400644
645 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100646 def get_stats_sock_path(cls):
647 return "%s/stats.sock" % cls.tempdir
648
649 @classmethod
650 def get_api_sock_path(cls):
651 return "%s/api.sock" % cls.tempdir
652
653 @classmethod
654 def get_api_segment_prefix(cls):
655 return os.path.basename(cls.tempdir) # Only used for VAPI
656
657 @classmethod
658 def get_tempdir(cls):
Klement Sekerab3fc6582022-03-10 11:47:45 +0100659 if cls.debug_attach:
660 tmpdir = f"{config.tmp_dir}/unittest-attach-gdb"
661 else:
662 tmpdir = f"{config.tmp_dir}/vpp-unittest-{cls.__name__}"
663 if config.wipe_tmp_dir:
664 shutil.rmtree(tmpdir, ignore_errors=True)
665 os.mkdir(tmpdir)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200666 return tmpdir
667
668 @classmethod
669 def create_file_handler(cls):
670 if config.log_dir is None:
671 cls.file_handler = FileHandler(f"{cls.tempdir}/log.txt")
672 return
673
674 logdir = f"{config.log_dir}/vpp-unittest-{cls.__name__}"
675 if config.wipe_tmp_dir:
676 shutil.rmtree(logdir, ignore_errors=True)
677 os.mkdir(logdir)
678 cls.file_handler = FileHandler(f"{logdir}/log.txt")
Klement Sekerae2636852021-03-16 12:52:12 +0100679
680 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200681 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200682 """
683 Perform class setup before running the testcase
684 Remove shared memory files, start vpp and connect the vpp-api
685 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800686 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100687 cls.logger = get_logger(cls.__name__)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200688 random.seed(config.rnd_seed)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200689 if hasattr(cls, "parallel_handler"):
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100690 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100691 cls.logger.propagate = False
Klement Sekerab23ffd72021-05-31 16:08:53 +0200692 cls.set_debug_flags(config.debug)
Klement Sekerae2636852021-03-16 12:52:12 +0100693 cls.tempdir = cls.get_tempdir()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200694 cls.create_file_handler()
Klement Sekera027dbd52017-04-11 06:01:53 +0200695 cls.file_handler.setFormatter(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200696 Formatter(fmt="%(asctime)s,%(msecs)03d %(message)s", datefmt="%H:%M:%S")
697 )
Klement Sekera027dbd52017-04-11 06:01:53 +0200698 cls.file_handler.setLevel(DEBUG)
699 cls.logger.addHandler(cls.file_handler)
Klement Sekerae2636852021-03-16 12:52:12 +0100700 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200701 os.chdir(cls.tempdir)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200702 cls.logger.info(
703 "Temporary dir is %s, api socket is %s",
704 cls.tempdir,
705 cls.get_api_sock_path(),
706 )
Klement Sekerab23ffd72021-05-31 16:08:53 +0200707 cls.logger.debug("Random seed is %s", config.rnd_seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200708 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100709 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200710 cls._pcaps = []
711 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200712 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100713 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100714 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200715 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200716 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200717 # need to catch exceptions here because if we raise, then the cleanup
718 # doesn't get called and we might end with a zombie vpp
719 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100720 if cls.debug_attach:
721 cls.attach_vpp()
722 else:
723 cls.run_vpp()
Dave Wallace670724c2022-09-20 21:52:18 -0400724 if not hasattr(cls, "vpp"):
725 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200726 cls.reporter.send_keep_alive(cls, "setUpClass")
juraj.linkes40dd73b2018-09-21 13:55:16 +0200727 VppTestResult.current_test_case_info = TestCaseInfo(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200728 cls.logger, cls.tempdir, cls.vpp.pid, config.vpp
729 )
Klement Sekerae4504c62016-12-08 10:16:41 +0100730 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100731 cls.vpp_stderr_deque = deque()
Naveen Joyc872cec2022-08-30 13:59:03 -0700732 # Pump thread in a non-debug-attached & not running-vpp
733 if not cls.debug_attach and not hasattr(cls, "running_vpp"):
Klement Sekerae2636852021-03-16 12:52:12 +0100734 cls.pump_thread_stop_flag = Event()
735 cls.pump_thread_wakeup_pipe = os.pipe()
736 cls.pump_thread = Thread(target=pump_output, args=(cls,))
737 cls.pump_thread.daemon = True
738 cls.pump_thread.start()
739 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400740 cls.vapi_response_timeout = 0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200741 cls.vapi = VppPapiProvider(cls.__name__, cls, cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100742 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400743 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100744 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400745 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100746 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100747 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200748 try:
749 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100750 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200751 cls.vpp_startup_failed = True
752 cls.logger.critical(
753 "VPP died shortly after startup, check the"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200754 " output to standard error for possible cause"
755 )
Klement Sekera3747c752017-04-10 06:30:17 +0200756 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100757 try:
758 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100759 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500760 cls.logger.debug("Exception connecting to vapi: %s" % e)
761 cls.vapi.disconnect()
762
Klement Sekera085f5c02016-11-24 01:59:16 +0100763 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200764 print(
765 colorize(
766 "You're running VPP inside gdbserver but "
767 "VPP-API connection failed, did you forget "
768 "to 'continue' VPP from within gdb?",
769 RED,
770 )
771 )
Ole Troan4376ab22021-03-03 10:40:05 +0100772 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100773 if cls.debug_attach:
774 last_line = cls.vapi.cli("show thread").split("\n")[-2]
775 cls.vpp_worker_count = int(last_line.split(" ")[0])
776 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400777 except vpp_papi.VPPRuntimeError as e:
778 cls.logger.debug("%s" % e)
779 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100780 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000781 except Exception as e:
782 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400783 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100784 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200785
Damjan Marionf56b77a2016-10-03 19:44:57 +0200786 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500787 def _debug_quit(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200788 if cls.debug_gdbserver or cls.debug_gdb:
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500789 try:
790 cls.vpp.poll()
791
792 if cls.vpp.returncode is None:
793 print()
794 print(double_line_delim)
795 print("VPP or GDB server is still running")
796 print(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200797 input(
798 "When done debugging, press ENTER to kill the "
799 "process and finish running the testcase..."
800 )
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500801 except AttributeError:
802 pass
803
804 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200805 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200806 """
807 Disconnect vpp-api, kill vpp and cleanup shared memory files
808 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500809 cls._debug_quit()
Naveen Joyc872cec2022-08-30 13:59:03 -0700810 if hasattr(cls, "running_vpp"):
811 cls.vpp.quit_vpp()
Klement Sekera277b89c2016-10-28 13:20:27 +0200812
juraj.linkes184870a2018-07-16 14:22:01 +0200813 # first signal that we want to stop the pump thread, then wake it up
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200814 if hasattr(cls, "pump_thread_stop_flag"):
juraj.linkes184870a2018-07-16 14:22:01 +0200815 cls.pump_thread_stop_flag.set()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200816 if hasattr(cls, "pump_thread_wakeup_pipe"):
817 os.write(cls.pump_thread_wakeup_pipe[1], b"ding dong wake up")
818 if hasattr(cls, "pump_thread"):
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100819 cls.logger.debug("Waiting for pump thread to stop")
820 cls.pump_thread.join()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200821 if hasattr(cls, "vpp_stderr_reader_thread"):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500822 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100823 cls.vpp_stderr_reader_thread.join()
824
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200825 if hasattr(cls, "vpp"):
826 if hasattr(cls, "vapi"):
Ole Troanfd574082019-11-27 23:12:48 +0100827 cls.logger.debug(cls.vapi.vpp.get_stats())
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200828 cls.logger.debug("Disconnecting class vapi client on %s", cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100829 cls.vapi.disconnect()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200830 cls.logger.debug("Deleting class vapi attribute on %s", cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100831 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200832 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100833 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000834 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100835 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400836 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100837 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100838 try:
839 outs, errs = cls.vpp.communicate(timeout=5)
840 except subprocess.TimeoutExpired:
841 cls.vpp.kill()
842 outs, errs = cls.vpp.communicate()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200843 cls.logger.debug("Deleting class vpp attribute on %s", cls.__name__)
Naveen Joyc872cec2022-08-30 13:59:03 -0700844 if not cls.debug_attach and not hasattr(cls, "running_vpp"):
Klement Sekerae2636852021-03-16 12:52:12 +0100845 cls.vpp.stdout.close()
846 cls.vpp.stderr.close()
Naveen Joyc872cec2022-08-30 13:59:03 -0700847 # If vpp is a dynamic attribute set by the func use_running,
848 # deletion will result in an AttributeError that we can
849 # safetly pass.
850 try:
851 del cls.vpp
852 except AttributeError:
853 pass
Damjan Marionf56b77a2016-10-03 19:44:57 +0200854
Klement Sekera3747c752017-04-10 06:30:17 +0200855 if cls.vpp_startup_failed:
856 stdout_log = cls.logger.info
857 stderr_log = cls.logger.critical
858 else:
859 stdout_log = cls.logger.info
860 stderr_log = cls.logger.info
861
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200862 if hasattr(cls, "vpp_stdout_deque"):
Klement Sekera3747c752017-04-10 06:30:17 +0200863 stdout_log(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200864 stdout_log("VPP output to stdout while running %s:", cls.__name__)
Klement Sekera3747c752017-04-10 06:30:17 +0200865 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100866 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200867 with open(cls.tempdir + "/vpp_stdout.txt", "w") as f:
Klement Sekera027dbd52017-04-11 06:01:53 +0200868 f.write(vpp_output)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200869 stdout_log("\n%s", vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200870 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200871
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200872 if hasattr(cls, "vpp_stderr_deque"):
Klement Sekera3747c752017-04-10 06:30:17 +0200873 stderr_log(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200874 stderr_log("VPP output to stderr while running %s:", cls.__name__)
Klement Sekera3747c752017-04-10 06:30:17 +0200875 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100876 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200877 with open(cls.tempdir + "/vpp_stderr.txt", "w") as f:
Klement Sekera027dbd52017-04-11 06:01:53 +0200878 f.write(vpp_output)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200879 stderr_log("\n%s", vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200880 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200881
Damjan Marionf56b77a2016-10-03 19:44:57 +0200882 @classmethod
883 def tearDownClass(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200884 """Perform final cleanup after running all tests in this test-case"""
885 cls.logger.debug("--- tearDownClass() for %s called ---" % cls.__name__)
Dave Wallace670724c2022-09-20 21:52:18 -0400886 if not hasattr(cls, "vpp"):
887 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200888 cls.reporter.send_keep_alive(cls, "tearDownClass")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200889 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200890 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100891 cls.reset_packet_infos()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200892 if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +0100893 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200894
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700895 def show_commands_at_teardown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200896 """Allow subclass specific teardown logging additions."""
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700897 self.logger.info("--- No test specific show commands provided. ---")
898
Damjan Marionf56b77a2016-10-03 19:44:57 +0200899 def tearDown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200900 """Show various debug prints after each test"""
901 self.logger.debug(
902 "--- tearDown() for %s.%s(%s) called ---"
903 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
904 )
Dave Wallace670724c2022-09-20 21:52:18 -0400905 if not hasattr(self, "vpp"):
906 return
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700907
908 try:
909 if not self.vpp_dead:
910 self.logger.debug(self.vapi.cli("show trace max 1000"))
911 self.logger.info(self.vapi.ppcli("show interface"))
912 self.logger.info(self.vapi.ppcli("show hardware"))
913 self.logger.info(self.statistics.set_errors_str())
914 self.logger.info(self.vapi.ppcli("show run"))
915 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400916 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700917 self.logger.info("Logging testcase specific show commands.")
918 self.show_commands_at_teardown()
Klement Sekera140af152022-02-18 10:30:51 +0000919 if self.remove_configured_vpp_objects_on_tear_down:
920 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500921 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000922 m = self._testMethodName
923 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500924 tmp_api_trace = "/tmp/%s" % api_trace
925 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
926 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200927 self.logger.info("Moving %s to %s\n" % (tmp_api_trace, vpp_api_trace_log))
Dmitry Valter71d02aa2023-01-27 12:49:55 +0000928 shutil.move(tmp_api_trace, vpp_api_trace_log)
Ole Troan4376ab22021-03-03 10:40:05 +0100929 except VppTransportSocketIOError:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200930 self.logger.debug(
931 "VppTransportSocketIOError: Vpp dead. Cannot log show commands."
932 )
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700933 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100934 else:
935 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200936
Damjan Marionf56b77a2016-10-03 19:44:57 +0200937 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200938 """Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800939 super(VppTestCase, self).setUp()
Dave Wallace670724c2022-09-20 21:52:18 -0400940 if not hasattr(self, "vpp"):
941 return
Klement Sekera909a6a12017-08-08 04:33:53 +0200942 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100943 if self.vpp_dead:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200944 raise VppDiedError(
945 rv=None,
946 testcase=self.__class__.__name__,
947 method_name=self._testMethodName,
948 )
949 self.sleep(0.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100950 self.vpp_stdout_deque.append(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200951 "--- test setUp() for %s.%s(%s) starts here ---\n"
952 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
953 )
Klement Sekerae4504c62016-12-08 10:16:41 +0100954 self.vpp_stderr_deque.append(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200955 "--- test setUp() for %s.%s(%s) starts here ---\n"
956 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
957 )
Klement Sekeraf62ae122016-10-11 11:47:09 +0200958 self.vapi.cli("clear trace")
959 # store the test instance inside the test class - so that objects
960 # holding the class can access instance methods (like assertEqual)
961 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200962
Damjan Marionf56b77a2016-10-03 19:44:57 +0200963 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200964 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200965 """
966 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200967
Klement Sekera75e7d132017-09-20 08:26:30 +0200968 :param interfaces: iterable interface indexes (if None,
969 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200970
Klement Sekeraf62ae122016-10-11 11:47:09 +0200971 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200972 if interfaces is None:
973 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200974 for i in interfaces:
975 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200976
Damjan Marionf56b77a2016-10-03 19:44:57 +0200977 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200978 def register_pcap(cls, intf, worker):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200979 """Register a pcap in the testclass"""
Klement Sekera9225dee2016-12-12 08:36:58 +0100980 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200981 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100982
983 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000984 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400985 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
986 # returns float("2.190522")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200987 timestr = cls.vapi.cli("show clock")
988 head, sep, tail = timestr.partition(",")
989 head, sep, tail = head.partition("Time now")
Dave Barach19718002020-03-11 10:31:36 -0400990 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000991
992 @classmethod
993 def sleep_on_vpp_time(cls, sec):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200994 """Sleep according to time in VPP world"""
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000995 # On a busy system with many processes
996 # we might end up with VPP time being slower than real world
997 # So take that into account when waiting for VPP to do something
998 start_time = cls.get_vpp_time()
999 while cls.get_vpp_time() - start_time < sec:
1000 cls.sleep(0.1)
1001
1002 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +01001003 def pg_start(cls, trace=True):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001004 """Enable the PG, wait till it is done, then clean up"""
Dave Wallace7b8b4652023-08-15 19:05:26 -04001005 for intf, worker in cls._old_pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001006 intf.handle_old_pcap_file(intf.get_in_path(worker), intf.in_history_counter)
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001007 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +01001008 if trace:
1009 cls.vapi.cli("clear trace")
1010 cls.vapi.cli("trace add pg-input 1000")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001011 cls.vapi.cli("packet-generator enable")
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +00001012 # PG, when starts, runs to completion -
1013 # so let's avoid a race condition,
1014 # and wait a little till it's done.
1015 # Then clean it up - and then be gone.
1016 deadline = time.time() + 300
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001017 while cls.vapi.cli("show packet-generator").find("Yes") != -1:
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +00001018 cls.sleep(0.01) # yield
1019 if time.time() > deadline:
1020 cls.logger.error("Timeout waiting for pg to stop")
1021 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001022 for intf, worker in cls._pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001023 cls.vapi.cli("packet-generator delete %s" % intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001024 cls._old_pcaps = cls._pcaps
1025 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001026
Damjan Marionf56b77a2016-10-03 19:44:57 +02001027 @classmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001028 def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0, mode=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001029 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001030 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +02001031
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001032 :param interfaces: iterable indexes of the interfaces.
1033 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +02001034
Klement Sekeraf62ae122016-10-11 11:47:09 +02001035 """
1036 result = []
1037 for i in interfaces:
Neale Ranns6197cb72021-06-03 14:43:21 +00001038 intf = VppPGInterface(cls, i, gso, gso_size, mode)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001039 setattr(cls, intf.name, intf)
1040 result.append(intf)
1041 cls.pg_interfaces = result
1042 return result
1043
Matej Klotton0178d522016-11-04 11:11:44 +01001044 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +00001045 def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001046 if not hasattr(cls, "vpp"):
1047 cls.pg_interfaces = []
1048 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001049 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001050 return cls.create_pg_interfaces_internal(
1051 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP4
1052 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001053
1054 @classmethod
1055 def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001056 if not hasattr(cls, "vpp"):
1057 cls.pg_interfaces = []
1058 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001059 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001060 return cls.create_pg_interfaces_internal(
1061 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP6
1062 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001063
1064 @classmethod
1065 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001066 if not hasattr(cls, "vpp"):
1067 cls.pg_interfaces = []
1068 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001069 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001070 return cls.create_pg_interfaces_internal(
1071 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
1072 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001073
1074 @classmethod
1075 def create_pg_ethernet_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001076 if not hasattr(cls, "vpp"):
1077 cls.pg_interfaces = []
1078 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001079 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001080 return cls.create_pg_interfaces_internal(
1081 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
1082 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001083
1084 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +02001085 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +01001086 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001087 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +01001088
Klement Sekerab9ef2732018-06-24 22:49:33 +02001089 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001090 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +01001091 """
Dave Wallace670724c2022-09-20 21:52:18 -04001092 if not hasattr(cls, "vpp"):
1093 cls.lo_interfaces = []
1094 return cls.lo_interfaces
Klement Sekerab9ef2732018-06-24 22:49:33 +02001095 result = [VppLoInterface(cls) for i in range(count)]
1096 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +01001097 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +01001098 cls.lo_interfaces = result
1099 return result
1100
Neale Ranns192b13f2019-03-15 02:16:20 -07001101 @classmethod
1102 def create_bvi_interfaces(cls, count):
1103 """
1104 Create BVI interfaces.
1105
1106 :param count: number of interfaces created.
1107 :returns: List of created interfaces.
1108 """
Dave Wallace670724c2022-09-20 21:52:18 -04001109 if not hasattr(cls, "vpp"):
1110 cls.bvi_interfaces = []
1111 return cls.bvi_interfaces
Neale Ranns192b13f2019-03-15 02:16:20 -07001112 result = [VppBviInterface(cls) for i in range(count)]
1113 for intf in result:
1114 setattr(cls, intf.name, intf)
1115 cls.bvi_interfaces = result
1116 return result
1117
Damjan Marionf56b77a2016-10-03 19:44:57 +02001118 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001119 def extend_packet(packet, size, padding=" "):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001120 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001121 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +02001122 NOTE: Currently works only when Raw layer is present.
1123
1124 :param packet: packet
1125 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +02001126 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +02001127
1128 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001129 packet_len = len(packet) + 4
1130 extend = size - packet_len
1131 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +02001132 num = (extend // len(padding)) + 1
1133 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +02001134
Klement Sekeradab231a2016-12-21 08:50:14 +01001135 @classmethod
1136 def reset_packet_infos(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001137 """Reset the list of packet info objects and packet counts to zero"""
Klement Sekeradab231a2016-12-21 08:50:14 +01001138 cls._packet_infos = {}
1139 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +02001140
Klement Sekeradab231a2016-12-21 08:50:14 +01001141 @classmethod
1142 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001143 """
1144 Create packet info object containing the source and destination indexes
1145 and add it to the testcase's packet info list
1146
Klement Sekeradab231a2016-12-21 08:50:14 +01001147 :param VppInterface src_if: source interface
1148 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +02001149
1150 :returns: _PacketInfo object
1151
1152 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001153 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +01001154 info.index = len(cls._packet_infos)
1155 info.src = src_if.sw_if_index
1156 info.dst = dst_if.sw_if_index
1157 if isinstance(dst_if, VppSubInterface):
1158 dst_idx = dst_if.parent.sw_if_index
1159 else:
1160 dst_idx = dst_if.sw_if_index
1161 if dst_idx in cls._packet_count_for_dst_if_idx:
1162 cls._packet_count_for_dst_if_idx[dst_idx] += 1
1163 else:
1164 cls._packet_count_for_dst_if_idx[dst_idx] = 1
1165 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001166 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001167
Damjan Marionf56b77a2016-10-03 19:44:57 +02001168 @staticmethod
1169 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001170 """
1171 Convert _PacketInfo object to packet payload
1172
1173 :param info: _PacketInfo object
1174
1175 :returns: string containing serialized data from packet info
1176 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001177
1178 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001179 return pack("iiiih", info.index, info.src, info.dst, info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001180
Damjan Marionf56b77a2016-10-03 19:44:57 +02001181 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001182 def payload_to_info(payload, payload_field="load"):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001183 """
1184 Convert packet payload to _PacketInfo object
1185
1186 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001187 :type payload: <class 'scapy.packet.Raw'>
1188 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001189 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001190 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001191 :returns: _PacketInfo object containing de-serialized data from payload
1192
1193 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001194
1195 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
1196 payload_b = getattr(payload, payload_field)[:18]
1197
Damjan Marionf56b77a2016-10-03 19:44:57 +02001198 info = _PacketInfo()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001199 info.index, info.src, info.dst, info.ip, info.proto = unpack("iiiih", payload_b)
Ray Kinsellab8165b92021-09-22 11:24:06 +01001200
1201 # some SRv6 TCs depend on get an exception if bad values are detected
1202 if info.index > 0x4000:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001203 raise ValueError("Index value is invalid")
Ray Kinsellab8165b92021-09-22 11:24:06 +01001204
Damjan Marionf56b77a2016-10-03 19:44:57 +02001205 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001206
Damjan Marionf56b77a2016-10-03 19:44:57 +02001207 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001208 """
1209 Iterate over the packet info list stored in the testcase
1210 Start iteration with first element if info is None
1211 Continue based on index in info if info is specified
1212
1213 :param info: info or None
1214 :returns: next info in list or None if no more infos
1215 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001216 if info is None:
1217 next_index = 0
1218 else:
1219 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001220 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001221 return None
1222 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001223 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001224
Klement Sekeraf62ae122016-10-11 11:47:09 +02001225 def get_next_packet_info_for_interface(self, src_index, info):
1226 """
1227 Search the packet info list for the next packet info with same source
1228 interface index
1229
1230 :param src_index: source interface index to search for
1231 :param info: packet info - where to start the search
1232 :returns: packet info or None
1233
1234 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001235 while True:
1236 info = self.get_next_packet_info(info)
1237 if info is None:
1238 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001239 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001240 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001241
Klement Sekeraf62ae122016-10-11 11:47:09 +02001242 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1243 """
1244 Search the packet info list for the next packet info with same source
1245 and destination interface indexes
1246
1247 :param src_index: source interface index to search for
1248 :param dst_index: destination interface index to search for
1249 :param info: packet info - where to start the search
1250 :returns: packet info or None
1251
1252 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001253 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001254 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001255 if info is None:
1256 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001257 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001258 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001259
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001260 def assert_equal(self, real_value, expected_value, name_or_class=None):
1261 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001262 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001263 return
1264 try:
1265 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001266 msg = msg % (
1267 getdoc(name_or_class).strip(),
1268 real_value,
1269 str(name_or_class(real_value)),
1270 expected_value,
1271 str(name_or_class(expected_value)),
1272 )
Klement Sekera13a83ef2018-03-21 12:35:51 +01001273 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001274 msg = "Invalid %s: %s does not match expected value %s" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001275 name_or_class,
1276 real_value,
1277 expected_value,
1278 )
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001279
1280 self.assertEqual(real_value, expected_value, msg)
1281
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001282 def assert_in_range(self, real_value, expected_min, expected_max, name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001283 if name is None:
1284 msg = None
1285 else:
1286 msg = "Invalid %s: %s out of range <%s,%s>" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001287 name,
1288 real_value,
1289 expected_min,
1290 expected_max,
1291 )
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001292 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1293
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001294 def assert_packet_checksums_valid(self, packet, ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001295 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001296 udp_layers = ["UDP", "UDPerror"]
1297 checksum_fields = ["cksum", "chksum"]
Klement Sekerad81ae412018-05-16 10:52:54 +02001298 checksums = []
1299 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001300 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001301 while True:
1302 layer = temp.getlayer(counter)
1303 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001304 layer = layer.copy()
1305 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001306 for cf in checksum_fields:
1307 if hasattr(layer, cf):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001308 if (
1309 ignore_zero_udp_checksums
1310 and 0 == getattr(layer, cf)
1311 and layer.name in udp_layers
1312 ):
Klement Sekerad81ae412018-05-16 10:52:54 +02001313 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001314 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001315 checksums.append((counter, cf))
1316 else:
1317 break
1318 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001319 if 0 == len(checksums):
1320 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001321 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekera738cf732022-11-14 11:26:18 +01001322 for layer, cf in reversed(checksums):
Klement Sekera31da2e32018-06-24 22:49:55 +02001323 calc_sum = getattr(temp[layer], cf)
1324 self.assert_equal(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001325 getattr(received[layer], cf),
1326 calc_sum,
1327 "packet checksum on layer #%d: %s" % (layer, temp[layer].name),
1328 )
Klement Sekera31da2e32018-06-24 22:49:55 +02001329 self.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001330 "Checksum field `%s` on `%s` layer has correct value `%s`"
1331 % (cf, temp[layer].name, calc_sum)
1332 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001333
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001334 def assert_checksum_valid(
Klement Sekera738cf732022-11-14 11:26:18 +01001335 self,
1336 received_packet,
1337 layer,
1338 checksum_field_names=["chksum", "cksum"],
1339 ignore_zero_checksum=False,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001340 ):
1341 """Check checksum of received packet on given layer"""
Klement Sekera738cf732022-11-14 11:26:18 +01001342 layer_copy = received_packet[layer].copy()
1343 layer_copy.remove_payload()
1344 field_name = None
1345 for f in checksum_field_names:
1346 if hasattr(layer_copy, f):
1347 field_name = f
1348 break
1349 if field_name is None:
1350 raise Exception(
1351 f"Layer `{layer}` has none of checksum fields: `{checksum_field_names}`."
1352 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001353 received_packet_checksum = getattr(received_packet[layer], field_name)
1354 if ignore_zero_checksum and 0 == received_packet_checksum:
1355 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001356 recalculated = received_packet.__class__(scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001357 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001358 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001359 self.assert_equal(
1360 received_packet_checksum,
1361 getattr(recalculated[layer], field_name),
Klement Sekera738cf732022-11-14 11:26:18 +01001362 f"packet checksum (field: {field_name}) on layer: %s" % layer,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001363 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001364
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001365 def assert_ip_checksum_valid(self, received_packet, ignore_zero_checksum=False):
1366 self.assert_checksum_valid(
1367 received_packet, "IP", ignore_zero_checksum=ignore_zero_checksum
1368 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001369
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001370 def assert_tcp_checksum_valid(self, received_packet, ignore_zero_checksum=False):
1371 self.assert_checksum_valid(
1372 received_packet, "TCP", ignore_zero_checksum=ignore_zero_checksum
1373 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001374
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001375 def assert_udp_checksum_valid(self, received_packet, ignore_zero_checksum=True):
1376 self.assert_checksum_valid(
1377 received_packet, "UDP", ignore_zero_checksum=ignore_zero_checksum
1378 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001379
1380 def assert_embedded_icmp_checksum_valid(self, received_packet):
1381 if received_packet.haslayer(IPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001382 self.assert_checksum_valid(received_packet, "IPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001383 if received_packet.haslayer(TCPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001384 self.assert_checksum_valid(received_packet, "TCPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001385 if received_packet.haslayer(UDPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001386 self.assert_checksum_valid(
1387 received_packet, "UDPerror", ignore_zero_checksum=True
1388 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001389 if received_packet.haslayer(ICMPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001390 self.assert_checksum_valid(received_packet, "ICMPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001391
1392 def assert_icmp_checksum_valid(self, received_packet):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001393 self.assert_checksum_valid(received_packet, "ICMP")
Klement Sekerad81ae412018-05-16 10:52:54 +02001394 self.assert_embedded_icmp_checksum_valid(received_packet)
1395
1396 def assert_icmpv6_checksum_valid(self, pkt):
1397 if pkt.haslayer(ICMPv6DestUnreach):
Klement Sekera738cf732022-11-14 11:26:18 +01001398 self.assert_checksum_valid(pkt, "ICMPv6DestUnreach")
Klement Sekerad81ae412018-05-16 10:52:54 +02001399 self.assert_embedded_icmp_checksum_valid(pkt)
1400 if pkt.haslayer(ICMPv6EchoRequest):
Klement Sekera738cf732022-11-14 11:26:18 +01001401 self.assert_checksum_valid(pkt, "ICMPv6EchoRequest")
Klement Sekerad81ae412018-05-16 10:52:54 +02001402 if pkt.haslayer(ICMPv6EchoReply):
Klement Sekera738cf732022-11-14 11:26:18 +01001403 self.assert_checksum_valid(pkt, "ICMPv6EchoReply")
Klement Sekerad81ae412018-05-16 10:52:54 +02001404
Klement Sekera107ad732022-02-18 10:32:08 +00001405 def get_counter(self, counter):
Klement Sekera3a343d42019-05-16 14:35:46 +02001406 if counter.startswith("/"):
1407 counter_value = self.statistics.get_counter(counter)
1408 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001409 counters = self.vapi.cli("sh errors").split("\n")
Klement Sekera6aa58b72019-05-16 14:34:55 +02001410 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001411 for i in range(1, len(counters) - 1):
1412 results = counters[i].split()
1413 if results[1] == counter:
1414 counter_value = int(results[0])
1415 break
1416 return counter_value
1417
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001418 def assert_counter_equal(self, counter, expected_value, thread=None, index=0):
Klement Sekera107ad732022-02-18 10:32:08 +00001419 c = self.get_counter(counter)
1420 if thread is not None:
1421 c = c[thread][index]
1422 else:
1423 c = sum(x[index] for x in c)
Klement Sekerac5fa5392023-08-17 19:38:34 +02001424 self.logger.debug(
1425 "validate counter `%s[%s]', expected: %s, real value: %s"
1426 % (counter, index, expected_value, c)
1427 )
1428 self.assert_equal(c, expected_value, "counter `%s[%s]'" % (counter, index))
Klement Sekera107ad732022-02-18 10:32:08 +00001429
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001430 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera107ad732022-02-18 10:32:08 +00001431 counter_value = self.get_counter(counter)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001432 self.assert_equal(
1433 counter_value, expected_value, "packet counter `%s'" % counter
1434 )
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001435
Ole Troan233e4682019-05-16 15:01:34 +02001436 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001437 counter_value = self.statistics[counter].sum()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001438 self.assert_equal(counter_value, expected_value, "error counter `%s'" % counter)
Ole Troan233e4682019-05-16 15:01:34 +02001439
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001440 @classmethod
1441 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001442 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1443 # * by Guido, only the main thread can be interrupted.
1444 # */
1445 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1446 if timeout == 0:
1447 # yield quantum
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001448 if hasattr(os, "sched_yield"):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001449 os.sched_yield()
1450 else:
1451 time.sleep(0)
1452 return
1453
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001454 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001455 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001456 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001457 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001458 if after - before > 2 * timeout:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001459 cls.logger.error(
1460 "unexpected self.sleep() result - slept for %es instead of ~%es!",
1461 after - before,
1462 timeout,
1463 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001464
1465 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001466 "Finished sleep (%s) - slept %es (wanted %es)",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001467 remark,
1468 after - before,
1469 timeout,
1470 )
Klement Sekeraa57a9702017-02-02 06:58:07 +01001471
Benoît Ganne56eccdb2021-08-20 09:18:31 +02001472 def virtual_sleep(self, timeout, remark=None):
1473 self.logger.debug("Moving VPP time by %s (%s)", timeout, remark)
1474 self.vapi.cli("set clock adjust %s" % timeout)
1475
Benoît Ganne8c45e512021-02-19 16:39:13 +01001476 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001477 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001478 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001479 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001480
Klement Sekeraad3187f2022-02-18 10:34:35 +00001481 def snapshot_stats(self, stats_diff):
1482 """Return snapshot of interesting stats based on diff dictionary."""
1483 stats_snapshot = {}
1484 for sw_if_index in stats_diff:
1485 for counter in stats_diff[sw_if_index]:
1486 stats_snapshot[counter] = self.statistics[counter]
1487 self.logger.debug(f"Took statistics stats_snapshot: {stats_snapshot}")
1488 return stats_snapshot
1489
1490 def compare_stats_with_snapshot(self, stats_diff, stats_snapshot):
1491 """Assert appropriate difference between current stats and snapshot."""
1492 for sw_if_index in stats_diff:
1493 for cntr, diff in stats_diff[sw_if_index].items():
1494 if sw_if_index == "err":
1495 self.assert_equal(
1496 self.statistics[cntr].sum(),
1497 stats_snapshot[cntr].sum() + diff,
1498 f"'{cntr}' counter value (previous value: "
1499 f"{stats_snapshot[cntr].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001500 f"expected diff: {diff})",
1501 )
Klement Sekeraad3187f2022-02-18 10:34:35 +00001502 else:
1503 try:
1504 self.assert_equal(
1505 self.statistics[cntr][:, sw_if_index].sum(),
1506 stats_snapshot[cntr][:, sw_if_index].sum() + diff,
1507 f"'{cntr}' counter value (previous value: "
1508 f"{stats_snapshot[cntr][:, sw_if_index].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001509 f"expected diff: {diff})",
1510 )
Klement Sekera01578852023-01-26 13:14:01 +01001511 except IndexError as e:
Klement Sekeraad3187f2022-02-18 10:34:35 +00001512 # if diff is 0, then this most probably a case where
1513 # test declares multiple interfaces but traffic hasn't
1514 # passed through this one yet - which means the counter
1515 # value is 0 and can be ignored
1516 if 0 != diff:
Klement Sekera01578852023-01-26 13:14:01 +01001517 raise Exception(
1518 f"Couldn't sum counter: {cntr} on sw_if_index: {sw_if_index}"
1519 ) from e
Klement Sekeraad3187f2022-02-18 10:34:35 +00001520
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001521 def send_and_assert_no_replies(
1522 self, intf, pkts, remark="", timeout=None, stats_diff=None, trace=True, msg=None
1523 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001524 if stats_diff:
1525 stats_snapshot = self.snapshot_stats(stats_diff)
1526
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001527 self.pg_send(intf, pkts)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001528
1529 try:
1530 if not timeout:
1531 timeout = 1
1532 for i in self.pg_interfaces:
Klement Sekera26cd0242022-02-18 10:35:08 +00001533 i.assert_nothing_captured(timeout=timeout, remark=remark)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001534 timeout = 0.1
1535 finally:
1536 if trace:
1537 if msg:
1538 self.logger.debug(f"send_and_assert_no_replies: {msg}")
1539 self.logger.debug(self.vapi.cli("show trace"))
1540
1541 if stats_diff:
1542 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
Neale Ranns52fae862018-01-08 04:41:42 -08001543
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001544 def send_and_expect(
1545 self,
1546 intf,
1547 pkts,
1548 output,
1549 n_rx=None,
1550 worker=None,
1551 trace=True,
1552 msg=None,
1553 stats_diff=None,
1554 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001555 if stats_diff:
1556 stats_snapshot = self.snapshot_stats(stats_diff)
1557
Neale Rannsd7603d92019-03-28 08:56:10 +00001558 if not n_rx:
Klement Sekera56c492a2022-01-10 21:57:27 +00001559 n_rx = 1 if isinstance(pkts, Packet) else len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001560 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001561 rx = output.get_capture(n_rx)
Klement Sekeraff334db2021-05-26 13:02:35 +02001562 if trace:
Klement Sekeraad3187f2022-02-18 10:34:35 +00001563 if msg:
1564 self.logger.debug(f"send_and_expect: {msg}")
Klement Sekeraff334db2021-05-26 13:02:35 +02001565 self.logger.debug(self.vapi.cli("show trace"))
Klement Sekeraad3187f2022-02-18 10:34:35 +00001566
1567 if stats_diff:
1568 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1569
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001570 return rx
1571
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001572 def send_and_expect_load_balancing(
1573 self, input, pkts, outputs, worker=None, trace=True
1574 ):
Neale Ranns699bea22022-02-17 09:22:16 +00001575 self.pg_send(input, pkts, worker=worker, trace=trace)
1576 rxs = []
1577 for oo in outputs:
1578 rx = oo._get_capture(1)
1579 self.assertNotEqual(0, len(rx))
1580 rxs.append(rx)
1581 if trace:
1582 self.logger.debug(self.vapi.cli("show trace"))
1583 return rxs
1584
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001585 def send_and_expect_some(self, intf, pkts, output, worker=None, trace=True):
Neale Ranns5c6dd172022-02-17 09:08:47 +00001586 self.pg_send(intf, pkts, worker=worker, trace=trace)
1587 rx = output._get_capture(1)
1588 if trace:
1589 self.logger.debug(self.vapi.cli("show trace"))
1590 self.assertTrue(len(rx) > 0)
1591 self.assertTrue(len(rx) < len(pkts))
1592 return rx
1593
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001594 def send_and_expect_only(self, intf, pkts, output, timeout=None, stats_diff=None):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001595 if stats_diff:
1596 stats_snapshot = self.snapshot_stats(stats_diff)
1597
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001598 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001599 rx = output.get_capture(len(pkts))
1600 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001601 if not timeout:
1602 timeout = 1
1603 for i in self.pg_interfaces:
1604 if i not in outputs:
Klement Sekera26cd0242022-02-18 10:35:08 +00001605 i.assert_nothing_captured(timeout=timeout)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001606 timeout = 0.1
1607
Klement Sekeraad3187f2022-02-18 10:34:35 +00001608 if stats_diff:
1609 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1610
Neale Ranns52fae862018-01-08 04:41:42 -08001611 return rx
1612
Damjan Marionf56b77a2016-10-03 19:44:57 +02001613
juraj.linkes184870a2018-07-16 14:22:01 +02001614def get_testcase_doc_name(test):
1615 return getdoc(test.__class__).splitlines()[0]
1616
1617
Ole Trøan5ba91592018-11-22 10:01:09 +00001618def get_test_description(descriptions, test):
1619 short_description = test.shortDescription()
1620 if descriptions and short_description:
1621 return short_description
1622 else:
1623 return str(test)
1624
1625
juraj.linkes40dd73b2018-09-21 13:55:16 +02001626class TestCaseInfo(object):
1627 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1628 self.logger = logger
1629 self.tempdir = tempdir
1630 self.vpp_pid = vpp_pid
1631 self.vpp_bin_path = vpp_bin_path
1632 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001633
1634
Damjan Marionf56b77a2016-10-03 19:44:57 +02001635class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001636 """
1637 @property result_string
1638 String variable to store the test case result string.
1639 @property errors
1640 List variable containing 2-tuples of TestCase instances and strings
1641 holding formatted tracebacks. Each tuple represents a test which
1642 raised an unexpected exception.
1643 @property failures
1644 List variable containing 2-tuples of TestCase instances and strings
1645 holding formatted tracebacks. Each tuple represents a test where
1646 a failure was explicitly signalled using the TestCase.assert*()
1647 methods.
1648 """
1649
juraj.linkes40dd73b2018-09-21 13:55:16 +02001650 failed_test_cases_info = set()
1651 core_crash_test_cases_info = set()
1652 current_test_case_info = None
1653
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001654 def __init__(self, stream=None, descriptions=None, verbosity=None, runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001655 """
Klement Sekerada505f62017-01-04 12:58:53 +01001656 :param stream File descriptor to store where to report test results.
1657 Set to the standard error stream by default.
1658 :param descriptions Boolean variable to store information if to use
1659 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001660 :param verbosity Integer variable to store required verbosity level.
1661 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001662 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001663 self.stream = stream
1664 self.descriptions = descriptions
1665 self.verbosity = verbosity
Klement Sekera47f35272023-03-29 16:04:58 +02001666 self.result_code = TestResultCode.TEST_RUN
Damjan Marionf56b77a2016-10-03 19:44:57 +02001667 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001668 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001669 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001670
Damjan Marionf56b77a2016-10-03 19:44:57 +02001671 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001672 """
1673 Record a test succeeded result
1674
1675 :param test:
1676
1677 """
Klement Sekera47f35272023-03-29 16:04:58 +02001678 self.log_result("addSuccess", test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001679 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001680 self.result_string = colorize("OK", GREEN)
Klement Sekera47f35272023-03-29 16:04:58 +02001681 self.result_code = TestResultCode.PASS
1682 self.send_result_through_pipe(test, self.result_code)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001683
Klement Sekera47f35272023-03-29 16:04:58 +02001684 def addExpectedFailure(self, test, err):
1685 self.log_result("addExpectedFailure", test, err)
1686 super().addExpectedFailure(test, err)
1687 self.result_string = colorize("FAIL", GREEN)
1688 self.result_code = TestResultCode.EXPECTED_FAIL
1689 self.send_result_through_pipe(test, self.result_code)
1690
1691 def addUnexpectedSuccess(self, test):
1692 self.log_result("addUnexpectedSuccess", test)
1693 super().addUnexpectedSuccess(test)
1694 self.result_string = colorize("OK", RED)
1695 self.result_code = TestResultCode.UNEXPECTED_PASS
1696 self.send_result_through_pipe(test, self.result_code)
juraj.linkescae64f82018-09-19 15:01:47 +02001697
Klement Sekeraf62ae122016-10-11 11:47:09 +02001698 def addSkip(self, test, reason):
1699 """
1700 Record a test skipped.
1701
1702 :param test:
1703 :param reason:
1704
1705 """
Klement Sekera47f35272023-03-29 16:04:58 +02001706 self.log_result("addSkip", test, reason=reason)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001707 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001708 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001709
Klement Sekera558ceab2021-04-08 19:37:41 +02001710 if reason == "not enough cpus":
Klement Sekera47f35272023-03-29 16:04:58 +02001711 self.result_code = TestResultCode.SKIP_CPU_SHORTAGE
Klement Sekera558ceab2021-04-08 19:37:41 +02001712 else:
Klement Sekera47f35272023-03-29 16:04:58 +02001713 self.result_code = TestResultCode.SKIP
1714 self.send_result_through_pipe(test, self.result_code)
juraj.linkescae64f82018-09-19 15:01:47 +02001715
juraj.linkes40dd73b2018-09-21 13:55:16 +02001716 def symlink_failed(self):
1717 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001718 try:
Klement Sekerab23ffd72021-05-31 16:08:53 +02001719 failed_dir = config.failed_dir
juraj.linkes40dd73b2018-09-21 13:55:16 +02001720 link_path = os.path.join(
1721 failed_dir,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001722 "%s-FAILED" % os.path.basename(self.current_test_case_info.tempdir),
1723 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001724
1725 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001726 "creating a link to the failed test"
1727 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001728 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001729 "os.symlink(%s, %s)"
1730 % (self.current_test_case_info.tempdir, link_path)
1731 )
juraj.linkes184870a2018-07-16 14:22:01 +02001732 if os.path.exists(link_path):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001733 self.current_test_case_info.logger.debug("symlink already exists")
juraj.linkes184870a2018-07-16 14:22:01 +02001734 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001735 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001736
Klement Sekeraf413bef2017-08-15 07:09:02 +02001737 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001738 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001739
juraj.linkescae64f82018-09-19 15:01:47 +02001740 def send_result_through_pipe(self, test, result):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001741 if hasattr(self, "test_framework_result_pipe"):
juraj.linkescae64f82018-09-19 15:01:47 +02001742 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001743 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001744 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001745
Klement Sekera47f35272023-03-29 16:04:58 +02001746 def log_result(self, fn, test, err=None, reason=None):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001747 if self.current_test_case_info:
1748 if isinstance(test, unittest.suite._ErrorHolder):
1749 test_name = test.description
1750 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001751 test_name = "%s.%s(%s)" % (
1752 test.__class__.__name__,
1753 test._testMethodName,
1754 test._testMethodDoc,
1755 )
Klement Sekera47f35272023-03-29 16:04:58 +02001756 extra_msg = ""
1757 if err:
1758 extra_msg += f", error is {err}"
1759 if reason:
1760 extra_msg += f", reason is {reason}"
juraj.linkes40dd73b2018-09-21 13:55:16 +02001761 self.current_test_case_info.logger.debug(
Klement Sekera47f35272023-03-29 16:04:58 +02001762 f"--- {fn}() {test_name} called{extra_msg}"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001763 )
Klement Sekera47f35272023-03-29 16:04:58 +02001764 if err:
1765 self.current_test_case_info.logger.debug(
1766 "formatted exception is:\n%s" % "".join(format_exception(*err))
1767 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001768
Klement Sekera47f35272023-03-29 16:04:58 +02001769 def add_error(self, test, err, unittest_fn, result_code):
1770 self.result_code = result_code
1771 if result_code == TestResultCode.FAIL:
1772 self.log_result("addFailure", test, err=err)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001773 error_type_str = colorize("FAIL", RED)
Klement Sekera47f35272023-03-29 16:04:58 +02001774 elif result_code == TestResultCode.ERROR:
1775 self.log_result("addError", test, err=err)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001776 error_type_str = colorize("ERROR", RED)
1777 else:
Klement Sekera47f35272023-03-29 16:04:58 +02001778 raise Exception(f"Unexpected result code {result_code}")
juraj.linkes40dd73b2018-09-21 13:55:16 +02001779
1780 unittest_fn(self, test, err)
1781 if self.current_test_case_info:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001782 self.result_string = "%s [ temp dir used by test case: %s ]" % (
1783 error_type_str,
1784 self.current_test_case_info.tempdir,
1785 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001786 self.symlink_failed()
1787 self.failed_test_cases_info.add(self.current_test_case_info)
1788 if is_core_present(self.current_test_case_info.tempdir):
1789 if not self.current_test_case_info.core_crash_test:
1790 if isinstance(test, unittest.suite._ErrorHolder):
1791 test_name = str(test)
1792 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001793 test_name = "'{!s}' ({!s})".format(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001794 get_testcase_doc_name(test), test.id()
1795 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001796 self.current_test_case_info.core_crash_test = test_name
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001797 self.core_crash_test_cases_info.add(self.current_test_case_info)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001798 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001799 self.result_string = "%s [no temp dir]" % error_type_str
juraj.linkes40dd73b2018-09-21 13:55:16 +02001800
Klement Sekera47f35272023-03-29 16:04:58 +02001801 self.send_result_through_pipe(test, result_code)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001802
Damjan Marionf56b77a2016-10-03 19:44:57 +02001803 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001804 """
1805 Record a test failed result
1806
1807 :param test:
1808 :param err: error message
1809
1810 """
Klement Sekera47f35272023-03-29 16:04:58 +02001811 self.add_error(test, err, unittest.TestResult.addFailure, TestResultCode.FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001812
Damjan Marionf56b77a2016-10-03 19:44:57 +02001813 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001814 """
1815 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001816
Klement Sekeraf62ae122016-10-11 11:47:09 +02001817 :param test:
1818 :param err: error message
1819
1820 """
Klement Sekera47f35272023-03-29 16:04:58 +02001821 self.add_error(test, err, unittest.TestResult.addError, TestResultCode.ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001822
Damjan Marionf56b77a2016-10-03 19:44:57 +02001823 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001824 """
1825 Get test description
1826
1827 :param test:
1828 :returns: test description
1829
1830 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001831 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001832
Damjan Marionf56b77a2016-10-03 19:44:57 +02001833 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001834 """
1835 Start a test
1836
1837 :param test:
1838
1839 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001840
1841 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001842 if test.__class__ in self.printed:
1843 return
1844
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001845 test_doc = getdoc(test)
1846 if not test_doc:
1847 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001848
Klement Sekeraea6236b2021-03-25 14:03:44 +01001849 test_title = test_doc.splitlines()[0].rstrip()
Klement Sekera558ceab2021-04-08 19:37:41 +02001850 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001851 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001852 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001853
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001854 # This block may overwrite the colorized title above,
1855 # but we want this to stand out and be fixed
1856 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001857 test_title = colorize(f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001858
Naveen Joy6eaeea92021-09-09 17:57:02 -07001859 if test.has_tag(TestCaseTag.FIXME_ASAN):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001860 test_title = colorize(f"FIXME with ASAN: {test_title}", RED)
Naveen Joy6eaeea92021-09-09 17:57:02 -07001861 test.skip_fixme_asan()
1862
Dave Wallacee95b2462022-09-18 22:28:44 -04001863 if is_distro_ubuntu2204 == True and test.has_tag(
1864 TestCaseTag.FIXME_UBUNTU2204
1865 ):
1866 test_title = colorize(f"FIXME on Ubuntu-22.04: {test_title}", RED)
1867 test.skip_fixme_ubuntu2204()
1868
Dave Wallace670724c2022-09-20 21:52:18 -04001869 if is_distro_debian11 == True and test.has_tag(TestCaseTag.FIXME_DEBIAN11):
1870 test_title = colorize(f"FIXME on Debian-11: {test_title}", RED)
1871 test.skip_fixme_debian11()
1872
Dave Wallace8a0a9d22022-10-04 22:02:49 -04001873 if "debug" in config.vpp_tag and test.has_tag(TestCaseTag.FIXME_VPP_DEBUG):
1874 test_title = colorize(f"FIXME on VPP Debug: {test_title}", RED)
1875 test.skip_fixme_vpp_debug()
1876
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001877 if hasattr(test, "vpp_worker_count"):
Klement Sekeraea6236b2021-03-25 14:03:44 +01001878 if test.vpp_worker_count == 0:
1879 test_title += " [main thread only]"
1880 elif test.vpp_worker_count == 1:
1881 test_title += " [1 worker thread]"
1882 else:
1883 test_title += f" [{test.vpp_worker_count} worker threads]"
1884
Klement Sekera558ceab2021-04-08 19:37:41 +02001885 if test.__class__.skipped_due_to_cpu_lack:
1886 test_title = colorize(
1887 f"{test_title} [skipped - not enough cpus, "
1888 f"required={test.__class__.get_cpus_required()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001889 f"available={max_vpp_cpus}]",
1890 YELLOW,
1891 )
Klement Sekera558ceab2021-04-08 19:37:41 +02001892
1893 print(double_line_delim)
1894 print(test_title)
1895 print(double_line_delim)
1896 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001897
1898 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001899 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001900 unittest.TestResult.startTest(self, test)
1901 if self.verbosity > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001902 self.stream.writeln("Starting " + self.getDescription(test) + " ...")
Klement Sekeraf62ae122016-10-11 11:47:09 +02001903 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001904
Damjan Marionf56b77a2016-10-03 19:44:57 +02001905 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001906 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001907 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001908
1909 :param test:
1910
1911 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001912 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001913
Klement Sekera47f35272023-03-29 16:04:58 +02001914 result_code_to_suffix = {
1915 TestResultCode.PASS: "",
1916 TestResultCode.FAIL: "",
1917 TestResultCode.ERROR: "",
1918 TestResultCode.SKIP: "",
1919 TestResultCode.TEST_RUN: "",
1920 TestResultCode.SKIP_CPU_SHORTAGE: "",
1921 TestResultCode.EXPECTED_FAIL: " [EXPECTED FAIL]",
1922 TestResultCode.UNEXPECTED_PASS: " [UNEXPECTED PASS]",
1923 }
1924
Damjan Marionf56b77a2016-10-03 19:44:57 +02001925 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001926 self.stream.writeln(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001927 self.stream.writeln(
Klement Sekera47f35272023-03-29 16:04:58 +02001928 "%-72s%s%s"
1929 % (
1930 self.getDescription(test),
1931 self.result_string,
1932 result_code_to_suffix[self.result_code],
1933 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001934 )
Klement Sekeraf62ae122016-10-11 11:47:09 +02001935 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001936 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001937 self.stream.writeln(
Klement Sekera47f35272023-03-29 16:04:58 +02001938 "%-67s %4.2f %s%s"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001939 % (
1940 self.getDescription(test),
1941 time.time() - self.start_test,
1942 self.result_string,
Klement Sekera47f35272023-03-29 16:04:58 +02001943 result_code_to_suffix[self.result_code],
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001944 )
1945 )
juraj.linkescae64f82018-09-19 15:01:47 +02001946
Klement Sekera47f35272023-03-29 16:04:58 +02001947 self.send_result_through_pipe(test, TestResultCode.TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001948
Damjan Marionf56b77a2016-10-03 19:44:57 +02001949 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001950 """
1951 Print errors from running the test case
1952 """
juraj.linkesabec0122018-11-16 17:28:56 +01001953 if len(self.errors) > 0 or len(self.failures) > 0:
1954 self.stream.writeln()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001955 self.printErrorList("ERROR", self.errors)
1956 self.printErrorList("FAIL", self.failures)
juraj.linkesabec0122018-11-16 17:28:56 +01001957
1958 # ^^ that is the last output from unittest before summary
1959 if not self.runner.print_summary:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001960 devnull = unittest.runner._WritelnDecorator(open(os.devnull, "w"))
juraj.linkesabec0122018-11-16 17:28:56 +01001961 self.stream = devnull
1962 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001963
Damjan Marionf56b77a2016-10-03 19:44:57 +02001964 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001965 """
1966 Print error list to the output stream together with error type
1967 and test case description.
1968
1969 :param flavour: error type
1970 :param errors: iterable errors
1971
1972 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001973 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001974 self.stream.writeln(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001975 self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001976 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001977 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001978
1979
Damjan Marionf56b77a2016-10-03 19:44:57 +02001980class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001981 """
Klement Sekera104543f2017-02-03 07:29:43 +01001982 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001983 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001984
Klement Sekeraf62ae122016-10-11 11:47:09 +02001985 @property
1986 def resultclass(self):
1987 """Class maintaining the results of the tests"""
1988 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001989
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001990 def __init__(
1991 self,
1992 keep_alive_pipe=None,
1993 descriptions=True,
1994 verbosity=1,
1995 result_pipe=None,
1996 failfast=False,
1997 buffer=False,
1998 resultclass=None,
1999 print_summary=True,
2000 **kwargs,
2001 ):
Klement Sekera7a161da2017-01-17 13:42:48 +01002002 # ignore stream setting here, use hard-coded stdout to be in sync
2003 # with prints from VppTestCase methods ...
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002004 super(VppTestRunner, self).__init__(
2005 sys.stdout, descriptions, verbosity, failfast, buffer, resultclass, **kwargs
2006 )
juraj.linkesccfead62018-11-21 13:20:43 +01002007 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01002008
juraj.linkesabec0122018-11-16 17:28:56 +01002009 self.orig_stream = self.stream
2010 self.resultclass.test_framework_result_pipe = result_pipe
2011
2012 self.print_summary = print_summary
2013
2014 def _makeResult(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002015 return self.resultclass(self.stream, self.descriptions, self.verbosity, self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02002016
Damjan Marionf56b77a2016-10-03 19:44:57 +02002017 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02002018 """
2019 Run the tests
2020
2021 :param test:
2022
2023 """
Klement Sekera3658adc2017-06-07 08:19:47 +02002024 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02002025
2026 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01002027 if not self.print_summary:
2028 self.stream = self.orig_stream
2029 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02002030 return result
Neale Ranns812ed392017-10-16 04:20:13 -07002031
2032
2033class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05002034 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
2035 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07002036 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05002037 self.args = executable_args
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002038 if hasattr(self, "testcase") and self.testcase.debug_all:
Dave Wallace24564332019-10-21 02:53:14 +00002039 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002040 self.args = [
2041 "/usr/bin/gdbserver",
2042 "localhost:{port}".format(port=self.testcase.gdbserver_port),
2043 ] + args
2044 elif self.testcase.debug_gdb and hasattr(self, "wait_for_gdb"):
Dave Wallace24564332019-10-21 02:53:14 +00002045 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05002046 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00002047 self.app_name = os.path.basename(self.app_bin)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002048 if hasattr(self, "role"):
2049 self.app_name += " {role}".format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002050 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07002051 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002052 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05002053 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07002054
Dave Wallace24564332019-10-21 02:53:14 +00002055 def wait_for_enter(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002056 if not hasattr(self, "testcase"):
Dave Wallace24564332019-10-21 02:53:14 +00002057 return
2058 if self.testcase.debug_all and self.testcase.debug_gdbserver:
2059 print()
2060 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002061 print(
2062 "Spawned GDB Server for '{app}' with PID: {pid}".format(
2063 app=self.app_name, pid=self.process.pid
2064 )
2065 )
Dave Wallace24564332019-10-21 02:53:14 +00002066 elif self.testcase.debug_all and self.testcase.debug_gdb:
2067 print()
2068 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002069 print(
2070 "Spawned '{app}' with PID: {pid}".format(
2071 app=self.app_name, pid=self.process.pid
2072 )
2073 )
Dave Wallace24564332019-10-21 02:53:14 +00002074 else:
2075 return
2076 print(single_line_delim)
2077 print("You can debug '{app}' using:".format(app=self.app_name))
2078 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002079 print(
2080 "sudo gdb "
2081 + self.app_bin
2082 + " -ex 'target remote localhost:{port}'".format(
2083 port=self.testcase.gdbserver_port
2084 )
2085 )
2086 print(
2087 "Now is the time to attach gdb by running the above "
2088 "command, set up breakpoints etc., then resume from "
2089 "within gdb by issuing the 'continue' command"
2090 )
Dave Wallace24564332019-10-21 02:53:14 +00002091 self.testcase.gdbserver_port += 1
2092 elif self.testcase.debug_gdb:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002093 print(
2094 "sudo gdb "
2095 + self.app_bin
2096 + " -ex 'attach {pid}'".format(pid=self.process.pid)
2097 )
2098 print(
2099 "Now is the time to attach gdb by running the above "
2100 "command and set up breakpoints etc., then resume from"
2101 " within gdb by issuing the 'continue' command"
2102 )
Dave Wallace24564332019-10-21 02:53:14 +00002103 print(single_line_delim)
2104 input("Press ENTER to continue running the testcase...")
2105
Neale Ranns812ed392017-10-16 04:20:13 -07002106 def run(self):
2107 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002108 if not os.path.exists(executable) or not os.access(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002109 executable, os.F_OK | os.X_OK
2110 ):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002111 # Exit code that means some system file did not exist,
2112 # could not be opened, or had some other kind of error.
2113 self.result = os.EX_OSFILE
2114 raise EnvironmentError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002115 "executable '%s' is not found or executable." % executable
2116 )
2117 self.logger.debug(
2118 "Running executable '{app}': '{cmd}'".format(
2119 app=self.app_name, cmd=" ".join(self.args)
2120 )
2121 )
Neale Ranns812ed392017-10-16 04:20:13 -07002122 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05002123 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07002124 env["CK_LOG_FILE_NAME"] = "-"
2125 self.process = subprocess.Popen(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002126 ["stdbuf", "-o0", "-e0"] + self.args,
2127 shell=False,
2128 env=env,
2129 preexec_fn=os.setpgrp,
2130 stdout=subprocess.PIPE,
2131 stderr=subprocess.PIPE,
2132 )
Dave Wallace24564332019-10-21 02:53:14 +00002133 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07002134 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00002135 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07002136 self.logger.info("Return code is `%s'" % self.process.returncode)
2137 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002138 self.logger.info(
2139 "Executable `{app}' wrote to stdout:".format(app=self.app_name)
2140 )
Neale Ranns812ed392017-10-16 04:20:13 -07002141 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002142 self.logger.info(out.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002143 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002144 self.logger.info(
2145 "Executable `{app}' wrote to stderr:".format(app=self.app_name)
2146 )
Neale Ranns812ed392017-10-16 04:20:13 -07002147 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002148 self.logger.info(err.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002149 self.logger.info(single_line_delim)
2150 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002151
Klement Sekera6aa58b72019-05-16 14:34:55 +02002152
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002153if __name__ == "__main__":
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002154 pass