blob: f9503fe79d5b2d3caa65da278e96fb28474744f9 [file] [log] [blame]
Klement Sekeraf62ae122016-10-11 11:47:09 +02001import os
Klement Sekera13a83ef2018-03-21 12:35:51 +01002import sys
Klement Sekera277b89c2016-10-28 13:20:27 +02003import traceback
Paul Vinciguerra1314ec62018-12-12 01:04:20 -08004import ipaddress
Klement Sekera9b6ece72018-03-23 10:50:11 +01005from subprocess import check_output, CalledProcessError
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07006
7import scapy.compat
Dave Wallace8800f732023-08-31 00:47:44 -04008import asfframework
Dmitry Valter3ace4d62022-03-26 15:43:14 +00009from config import config
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040010from log import RED, single_line_delim, double_line_delim
juraj.linkes40dd73b2018-09-21 13:55:16 +020011from util import check_core_path, get_core_path
Klement Sekeraf62ae122016-10-11 11:47:09 +020012
13
Paul Vinciguerrae061dad2020-12-04 14:57:51 -050014class Hook:
Klement Sekeraf62ae122016-10-11 11:47:09 +020015 """
16 Generic hooks before/after API/CLI calls
17 """
18
Paul Vinciguerra895e2f82019-01-08 20:37:40 -080019 def __init__(self, test):
20 self.test = test
21 self.logger = test.logger
Klement Sekera277b89c2016-10-28 13:20:27 +020022
Klement Sekeraf62ae122016-10-11 11:47:09 +020023 def before_api(self, api_name, api_args):
24 """
25 Function called before API call
26 Emit a debug message describing the API name and arguments
27
28 @param api_name: name of the API
29 @param api_args: tuple containing the API arguments
30 """
Paul Vinciguerra1314ec62018-12-12 01:04:20 -080031
32 def _friendly_format(val):
33 if not isinstance(val, str):
34 return val
35 if len(val) == 6:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020036 return "{!s} ({!s})".format(
37 val, ":".join(["{:02x}".format(scapy.compat.orb(x)) for x in val])
38 )
Paul Vinciguerra1314ec62018-12-12 01:04:20 -080039 try:
Paul Vinciguerra9e315952019-01-29 11:51:44 -080040 # we don't call test_type(val) because it is a packed value.
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020041 return "{!s} ({!s})".format(val, str(ipaddress.ip_address(val)))
Naveen Joy64f75302019-03-27 14:28:50 -070042 except ValueError:
Paul Vinciguerra1314ec62018-12-12 01:04:20 -080043 return val
44
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020045 _args = ", ".join(
46 "{!s}={!r}".format(key, _friendly_format(val))
47 for (key, val) in api_args.items()
48 )
49 self.logger.debug("API: %s (%s)" % (api_name, _args), extra={"color": RED})
Klement Sekeraf62ae122016-10-11 11:47:09 +020050
51 def after_api(self, api_name, api_args):
52 """
53 Function called after API call
54
55 @param api_name: name of the API
56 @param api_args: tuple containing the API arguments
57 """
58 pass
59
60 def before_cli(self, cli):
61 """
62 Function called before CLI call
63 Emit a debug message describing the CLI
64
65 @param cli: CLI string
66 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020067 self.logger.debug("CLI: %s" % (cli), extra={"color": RED})
Klement Sekeraf62ae122016-10-11 11:47:09 +020068
69 def after_cli(self, cli):
70 """
71 Function called after CLI call
72 """
73 pass
74
75
Klement Sekeraf62ae122016-10-11 11:47:09 +020076class PollHook(Hook):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020077 """Hook which checks if the vpp subprocess is alive"""
Klement Sekeraf62ae122016-10-11 11:47:09 +020078
Paul Vinciguerra895e2f82019-01-08 20:37:40 -080079 def __init__(self, test):
80 super(PollHook, self).__init__(test)
Klement Sekeraf62ae122016-10-11 11:47:09 +020081
Klement Sekeraf62ae122016-10-11 11:47:09 +020082 def on_crash(self, core_path):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020083 self.logger.error(
84 "Core file present, debug with: gdb %s %s", config.vpp, core_path
85 )
juraj.linkes40dd73b2018-09-21 13:55:16 +020086 check_core_path(self.logger, core_path)
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -080087 self.logger.error("Running `file %s':", core_path)
juraj.linkes40dd73b2018-09-21 13:55:16 +020088 try:
89 info = check_output(["file", core_path])
90 self.logger.error(info)
91 except CalledProcessError as e:
92 self.logger.error(
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -080093 "Subprocess returned with error running `file' utility on "
94 "core-file, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020095 "rc=%s",
96 e.returncode,
97 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -080098 except OSError as e:
99 self.logger.error(
100 "Subprocess returned OS error running `file' utility on "
101 "core-file, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200102 "oserror=(%s) %s",
103 e.errno,
104 e.strerror,
105 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800106 except Exception as e:
107 self.logger.error(
108 "Subprocess returned unanticipated error running `file' "
109 "utility on core-file, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200110 "%s",
111 e,
112 )
Klement Sekeraf62ae122016-10-11 11:47:09 +0200113
114 def poll_vpp(self):
115 """
116 Poll the vpp status and throw an exception if it's not running
117 :raises VppDiedError: exception if VPP is not running anymore
118 """
Dave Wallace670724c2022-09-20 21:52:18 -0400119 if not hasattr(self.test, "vpp") or self.test.vpp_dead:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200120 # already dead, nothing to do
121 return
122
Paul Vinciguerra895e2f82019-01-08 20:37:40 -0800123 self.test.vpp.poll()
124 if self.test.vpp.returncode is not None:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400125 self.test.vpp_dead = True
Paul Vinciguerra895e2f82019-01-08 20:37:40 -0800126 core_path = get_core_path(self.test.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200127 if os.path.isfile(core_path):
128 self.on_crash(core_path)
Dave Wallace8800f732023-08-31 00:47:44 -0400129 raise asfframework.VppDiedError(rv=self.test.vpp.returncode)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200130
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200131 def before_api(self, api_name, api_args):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200132 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200133 Check if VPP died before executing an API
Klement Sekeraf62ae122016-10-11 11:47:09 +0200134
135 :param api_name: name of the API
136 :param api_args: tuple containing the API arguments
137 :raises VppDiedError: exception if VPP is not running anymore
138
139 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200140 super(PollHook, self).before_api(api_name, api_args)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200141 self.poll_vpp()
142
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200143 def before_cli(self, cli):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200144 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200145 Check if VPP died before executing a CLI
Klement Sekeraf62ae122016-10-11 11:47:09 +0200146
147 :param cli: CLI string
148 :raises Exception: exception if VPP is not running anymore
149
150 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200151 super(PollHook, self).before_cli(cli)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200152 self.poll_vpp()
Klement Sekera277b89c2016-10-28 13:20:27 +0200153
154
155class StepHook(PollHook):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200156 """Hook which requires user to press ENTER before doing any API/CLI"""
Klement Sekera277b89c2016-10-28 13:20:27 +0200157
Paul Vinciguerra895e2f82019-01-08 20:37:40 -0800158 def __init__(self, test):
Klement Sekera277b89c2016-10-28 13:20:27 +0200159 self.skip_stack = None
160 self.skip_num = None
161 self.skip_count = 0
Klement Sekerac0a2f0e2022-01-28 11:31:01 +0000162 self.break_func = None
Paul Vinciguerra895e2f82019-01-08 20:37:40 -0800163 super(StepHook, self).__init__(test)
Klement Sekera277b89c2016-10-28 13:20:27 +0200164
165 def skip(self):
Klement Sekerac0a2f0e2022-01-28 11:31:01 +0000166 if self.break_func is not None:
167 return self.should_skip_func_based()
168 if self.skip_stack is not None:
169 return self.should_skip_stack_based()
170
171 def should_skip_func_based(self):
172 stack = traceback.extract_stack()
173 for e in stack:
174 if e[2] == self.break_func:
175 self.break_func = None
176 return False
177 return True
178
179 def should_skip_stack_based(self):
Klement Sekera277b89c2016-10-28 13:20:27 +0200180 stack = traceback.extract_stack()
181 counter = 0
182 skip = True
183 for e in stack:
184 if counter > self.skip_num:
185 break
186 if e[0] != self.skip_stack[counter][0]:
187 skip = False
188 if e[1] != self.skip_stack[counter][1]:
189 skip = False
190 counter += 1
191 if skip:
192 self.skip_count += 1
193 return True
194 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200195 print("%d API/CLI calls skipped in specified stack frame" % self.skip_count)
Klement Sekera277b89c2016-10-28 13:20:27 +0200196 self.skip_count = 0
197 self.skip_stack = None
198 self.skip_num = None
199 return False
200
201 def user_input(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200202 print("number\tfunction\tfile\tcode")
Klement Sekera277b89c2016-10-28 13:20:27 +0200203 counter = 0
204 stack = traceback.extract_stack()
205 for e in stack:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200206 print("%02d.\t%s\t%s:%d\t[%s]" % (counter, e[2], e[0], e[1], e[3]))
Klement Sekera277b89c2016-10-28 13:20:27 +0200207 counter += 1
208 print(single_line_delim)
juraj.linkes184870a2018-07-16 14:22:01 +0200209 print("You may enter a number of stack frame chosen from above")
Klement Sekera277b89c2016-10-28 13:20:27 +0200210 print("Calls in/below that stack frame will be not be stepped anymore")
Klement Sekerac0a2f0e2022-01-28 11:31:01 +0000211 print("Alternatively, enter a test function name to stop at")
Klement Sekera277b89c2016-10-28 13:20:27 +0200212 print(single_line_delim)
213 while True:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200214 print(
215 "Enter your choice, if any, and press ENTER to continue "
216 "running the testcase..."
217 )
218 choice = sys.stdin.readline().rstrip("\r\n")
Klement Sekera277b89c2016-10-28 13:20:27 +0200219 if choice == "":
220 choice = None
221 try:
222 if choice is not None:
223 num = int(choice)
juraj.linkes184870a2018-07-16 14:22:01 +0200224 except ValueError:
Klement Sekerac0a2f0e2022-01-28 11:31:01 +0000225 if choice.startswith("test_"):
226 break
Klement Sekera277b89c2016-10-28 13:20:27 +0200227 print("Invalid input")
228 continue
229 if choice is not None and (num < 0 or num >= len(stack)):
230 print("Invalid choice")
231 continue
232 break
233 if choice is not None:
Klement Sekerac0a2f0e2022-01-28 11:31:01 +0000234 if choice.startswith("test_"):
235 self.break_func = choice
236 else:
237 self.break_func = None
238 self.skip_stack = stack
239 self.skip_num = num
Klement Sekera277b89c2016-10-28 13:20:27 +0200240
241 def before_cli(self, cli):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200242 """Wait for ENTER before executing CLI"""
Klement Sekera277b89c2016-10-28 13:20:27 +0200243 if self.skip():
244 print("Skip pause before executing CLI: %s" % cli)
245 else:
246 print(double_line_delim)
247 print("Test paused before executing CLI: %s" % cli)
248 print(single_line_delim)
249 self.user_input()
250 super(StepHook, self).before_cli(cli)
251
252 def before_api(self, api_name, api_args):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200253 """Wait for ENTER before executing API"""
Klement Sekera277b89c2016-10-28 13:20:27 +0200254 if self.skip():
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200255 print("Skip pause before executing API: %s (%s)" % (api_name, api_args))
Klement Sekera277b89c2016-10-28 13:20:27 +0200256 else:
257 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200258 print("Test paused before executing API: %s (%s)" % (api_name, api_args))
Klement Sekera277b89c2016-10-28 13:20:27 +0200259 print(single_line_delim)
260 self.user_input()
261 super(StepHook, self).before_api(api_name, api_args)