blob: 49b41c91dff1a3e7639fea0d1813791734743406 [file] [log] [blame]
Klement Sekeraf62ae122016-10-11 11:47:09 +02001import signal
2import os
Klement Sekera13a83ef2018-03-21 12:35:51 +01003import sys
Klement Sekera277b89c2016-10-28 13:20:27 +02004import traceback
Klement Sekera909a6a12017-08-08 04:33:53 +02005from log import RED, single_line_delim, double_line_delim
Klement Sekera75e7d132017-09-20 08:26:30 +02006from debug import spawn_gdb
Klement Sekeraf62ae122016-10-11 11:47:09 +02007
8
9class Hook(object):
10 """
11 Generic hooks before/after API/CLI calls
12 """
13
Klement Sekera277b89c2016-10-28 13:20:27 +020014 def __init__(self, logger):
15 self.logger = logger
16
Klement Sekeraf62ae122016-10-11 11:47:09 +020017 def before_api(self, api_name, api_args):
18 """
19 Function called before API call
20 Emit a debug message describing the API name and arguments
21
22 @param api_name: name of the API
23 @param api_args: tuple containing the API arguments
24 """
Klement Sekera277b89c2016-10-28 13:20:27 +020025 self.logger.debug("API: %s (%s)" %
26 (api_name, api_args), extra={'color': RED})
Klement Sekeraf62ae122016-10-11 11:47:09 +020027
28 def after_api(self, api_name, api_args):
29 """
30 Function called after API call
31
32 @param api_name: name of the API
33 @param api_args: tuple containing the API arguments
34 """
35 pass
36
37 def before_cli(self, cli):
38 """
39 Function called before CLI call
40 Emit a debug message describing the CLI
41
42 @param cli: CLI string
43 """
Klement Sekera277b89c2016-10-28 13:20:27 +020044 self.logger.debug("CLI: %s" % (cli), extra={'color': RED})
Klement Sekeraf62ae122016-10-11 11:47:09 +020045
46 def after_cli(self, cli):
47 """
48 Function called after CLI call
49 """
50 pass
51
52
53class VppDiedError(Exception):
54 pass
55
56
57class PollHook(Hook):
58 """ Hook which checks if the vpp subprocess is alive """
59
60 def __init__(self, testcase):
Klement Sekeraf62ae122016-10-11 11:47:09 +020061 self.testcase = testcase
Klement Sekera277b89c2016-10-28 13:20:27 +020062 self.logger = testcase.logger
Klement Sekeraf62ae122016-10-11 11:47:09 +020063
Klement Sekeraf62ae122016-10-11 11:47:09 +020064 def on_crash(self, core_path):
Klement Sekera01bbbe92016-11-02 09:25:05 +010065 if self.testcase.debug_core:
Klement Sekera05742262018-03-14 18:14:49 +010066 # notify parent process that we're handling a core file
67 open('%s/_core_handled' % self.testcase.tempdir, 'a').close()
Klement Sekera75e7d132017-09-20 08:26:30 +020068 spawn_gdb(self.testcase.vpp_bin, core_path, self.logger)
69 else:
70 self.logger.critical("Core file present, debug with: gdb %s %s" %
71 (self.testcase.vpp_bin, core_path))
Klement Sekeraf62ae122016-10-11 11:47:09 +020072
73 def poll_vpp(self):
74 """
75 Poll the vpp status and throw an exception if it's not running
76 :raises VppDiedError: exception if VPP is not running anymore
77 """
Klement Sekera085f5c02016-11-24 01:59:16 +010078 if self.testcase.vpp_dead:
Klement Sekeraf62ae122016-10-11 11:47:09 +020079 # already dead, nothing to do
80 return
81
82 self.testcase.vpp.poll()
83 if self.testcase.vpp.returncode is not None:
84 signaldict = dict(
85 (k, v) for v, k in reversed(sorted(signal.__dict__.items()))
86 if v.startswith('SIG') and not v.startswith('SIG_'))
Klement Sekera0e3c0de2016-09-29 14:43:44 +020087
88 if self.testcase.vpp.returncode in signaldict:
89 s = signaldict[abs(self.testcase.vpp.returncode)]
90 else:
91 s = "unknown"
Klement Sekerada505f62017-01-04 12:58:53 +010092 msg = "VPP subprocess died unexpectedly with returncode %d [%s]" %\
93 (self.testcase.vpp.returncode, s)
Klement Sekera277b89c2016-10-28 13:20:27 +020094 self.logger.critical(msg)
Klement Sekeraf62ae122016-10-11 11:47:09 +020095 core_path = self.testcase.tempdir + '/core'
96 if os.path.isfile(core_path):
97 self.on_crash(core_path)
98 self.testcase.vpp_dead = True
99 raise VppDiedError(msg)
100
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200101 def before_api(self, api_name, api_args):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200102 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200103 Check if VPP died before executing an API
Klement Sekeraf62ae122016-10-11 11:47:09 +0200104
105 :param api_name: name of the API
106 :param api_args: tuple containing the API arguments
107 :raises VppDiedError: exception if VPP is not running anymore
108
109 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200110 super(PollHook, self).before_api(api_name, api_args)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200111 self.poll_vpp()
112
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200113 def before_cli(self, cli):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200114 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200115 Check if VPP died before executing a CLI
Klement Sekeraf62ae122016-10-11 11:47:09 +0200116
117 :param cli: CLI string
118 :raises Exception: exception if VPP is not running anymore
119
120 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200121 super(PollHook, self).before_cli(cli)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200122 self.poll_vpp()
Klement Sekera277b89c2016-10-28 13:20:27 +0200123
124
125class StepHook(PollHook):
126 """ Hook which requires user to press ENTER before doing any API/CLI """
127
128 def __init__(self, testcase):
129 self.skip_stack = None
130 self.skip_num = None
131 self.skip_count = 0
132 super(StepHook, self).__init__(testcase)
133
134 def skip(self):
135 if self.skip_stack is None:
136 return False
137 stack = traceback.extract_stack()
138 counter = 0
139 skip = True
140 for e in stack:
141 if counter > self.skip_num:
142 break
143 if e[0] != self.skip_stack[counter][0]:
144 skip = False
145 if e[1] != self.skip_stack[counter][1]:
146 skip = False
147 counter += 1
148 if skip:
149 self.skip_count += 1
150 return True
151 else:
152 print("%d API/CLI calls skipped in specified stack "
153 "frame" % self.skip_count)
154 self.skip_count = 0
155 self.skip_stack = None
156 self.skip_num = None
157 return False
158
159 def user_input(self):
160 print('number\tfunction\tfile\tcode')
161 counter = 0
162 stack = traceback.extract_stack()
163 for e in stack:
164 print('%02d.\t%s\t%s:%d\t[%s]' % (counter, e[2], e[0], e[1], e[3]))
165 counter += 1
166 print(single_line_delim)
167 print("You can enter a number of stack frame chosen from above")
168 print("Calls in/below that stack frame will be not be stepped anymore")
169 print(single_line_delim)
170 while True:
Klement Sekera13a83ef2018-03-21 12:35:51 +0100171 choice = sys.stdin.readline(
172 "Enter your choice, if any, and press ENTER to continue "
173 "running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200174 if choice == "":
175 choice = None
176 try:
177 if choice is not None:
178 num = int(choice)
Klement Sekera13a83ef2018-03-21 12:35:51 +0100179 except TypeError:
Klement Sekera277b89c2016-10-28 13:20:27 +0200180 print("Invalid input")
181 continue
182 if choice is not None and (num < 0 or num >= len(stack)):
183 print("Invalid choice")
184 continue
185 break
186 if choice is not None:
187 self.skip_stack = stack
188 self.skip_num = num
189
190 def before_cli(self, cli):
191 """ Wait for ENTER before executing CLI """
192 if self.skip():
193 print("Skip pause before executing CLI: %s" % cli)
194 else:
195 print(double_line_delim)
196 print("Test paused before executing CLI: %s" % cli)
197 print(single_line_delim)
198 self.user_input()
199 super(StepHook, self).before_cli(cli)
200
201 def before_api(self, api_name, api_args):
202 """ Wait for ENTER before executing API """
203 if self.skip():
204 print("Skip pause before executing API: %s (%s)"
205 % (api_name, api_args))
206 else:
207 print(double_line_delim)
208 print("Test paused before executing API: %s (%s)"
209 % (api_name, api_args))
210 print(single_line_delim)
211 self.user_input()
212 super(StepHook, self).before_api(api_name, api_args)