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