blob: 247704ec3af08e7b37995327d9be41b6cb240bfd [file] [log] [blame]
Klement Sekeraf62ae122016-10-11 11:47:09 +02001import signal
2import os
3import pexpect
Klement Sekera277b89c2016-10-28 13:20:27 +02004import traceback
5from log import *
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
63 def spawn_gdb(self, gdb_path, core_path):
64 gdb_cmdline = gdb_path + ' ' + self.testcase.vpp_bin + ' ' + core_path
65 gdb = pexpect.spawn(gdb_cmdline)
66 gdb.interact()
67 try:
68 gdb.terminate(True)
69 except:
70 pass
71 if gdb.isalive():
72 raise Exception("GDB refused to die...")
73
74 def on_crash(self, core_path):
Klement Sekera01bbbe92016-11-02 09:25:05 +010075 if self.testcase.debug_core:
Klement Sekeraf62ae122016-10-11 11:47:09 +020076 gdb_path = '/usr/bin/gdb'
77 if os.path.isfile(gdb_path) and os.access(gdb_path, os.X_OK):
78 # automatically attach gdb
79 self.spawn_gdb(gdb_path, core_path)
80 return
81 else:
Klement Sekera277b89c2016-10-28 13:20:27 +020082 self.logger.error(
83 "Debugger '%s' does not exist or is not an executable.." %
84 gdb_path)
Klement Sekeraf62ae122016-10-11 11:47:09 +020085
Klement Sekera277b89c2016-10-28 13:20:27 +020086 self.logger.critical('core file present, debug with: gdb ' +
87 self.testcase.vpp_bin + ' ' + core_path)
Klement Sekeraf62ae122016-10-11 11:47:09 +020088
89 def poll_vpp(self):
90 """
91 Poll the vpp status and throw an exception if it's not running
92 :raises VppDiedError: exception if VPP is not running anymore
93 """
Klement Sekera085f5c02016-11-24 01:59:16 +010094 if self.testcase.vpp_dead:
Klement Sekeraf62ae122016-10-11 11:47:09 +020095 # already dead, nothing to do
96 return
97
98 self.testcase.vpp.poll()
99 if self.testcase.vpp.returncode is not None:
100 signaldict = dict(
101 (k, v) for v, k in reversed(sorted(signal.__dict__.items()))
102 if v.startswith('SIG') and not v.startswith('SIG_'))
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200103
104 if self.testcase.vpp.returncode in signaldict:
105 s = signaldict[abs(self.testcase.vpp.returncode)]
106 else:
107 s = "unknown"
Klement Sekerada505f62017-01-04 12:58:53 +0100108 msg = "VPP subprocess died unexpectedly with returncode %d [%s]" %\
109 (self.testcase.vpp.returncode, s)
Klement Sekera277b89c2016-10-28 13:20:27 +0200110 self.logger.critical(msg)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200111 core_path = self.testcase.tempdir + '/core'
112 if os.path.isfile(core_path):
113 self.on_crash(core_path)
114 self.testcase.vpp_dead = True
115 raise VppDiedError(msg)
116
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200117 def before_api(self, api_name, api_args):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200118 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200119 Check if VPP died before executing an API
Klement Sekeraf62ae122016-10-11 11:47:09 +0200120
121 :param api_name: name of the API
122 :param api_args: tuple containing the API arguments
123 :raises VppDiedError: exception if VPP is not running anymore
124
125 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200126 super(PollHook, self).before_api(api_name, api_args)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200127 self.poll_vpp()
128
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200129 def before_cli(self, cli):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200130 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200131 Check if VPP died before executing a CLI
Klement Sekeraf62ae122016-10-11 11:47:09 +0200132
133 :param cli: CLI string
134 :raises Exception: exception if VPP is not running anymore
135
136 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200137 super(PollHook, self).before_cli(cli)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200138 self.poll_vpp()
Klement Sekera277b89c2016-10-28 13:20:27 +0200139
140
141class StepHook(PollHook):
142 """ Hook which requires user to press ENTER before doing any API/CLI """
143
144 def __init__(self, testcase):
145 self.skip_stack = None
146 self.skip_num = None
147 self.skip_count = 0
148 super(StepHook, self).__init__(testcase)
149
150 def skip(self):
151 if self.skip_stack is None:
152 return False
153 stack = traceback.extract_stack()
154 counter = 0
155 skip = True
156 for e in stack:
157 if counter > self.skip_num:
158 break
159 if e[0] != self.skip_stack[counter][0]:
160 skip = False
161 if e[1] != self.skip_stack[counter][1]:
162 skip = False
163 counter += 1
164 if skip:
165 self.skip_count += 1
166 return True
167 else:
168 print("%d API/CLI calls skipped in specified stack "
169 "frame" % self.skip_count)
170 self.skip_count = 0
171 self.skip_stack = None
172 self.skip_num = None
173 return False
174
175 def user_input(self):
176 print('number\tfunction\tfile\tcode')
177 counter = 0
178 stack = traceback.extract_stack()
179 for e in stack:
180 print('%02d.\t%s\t%s:%d\t[%s]' % (counter, e[2], e[0], e[1], e[3]))
181 counter += 1
182 print(single_line_delim)
183 print("You can enter a number of stack frame chosen from above")
184 print("Calls in/below that stack frame will be not be stepped anymore")
185 print(single_line_delim)
186 while True:
187 choice = raw_input("Enter your choice, if any, and press ENTER to "
188 "continue running the testcase...")
189 if choice == "":
190 choice = None
191 try:
192 if choice is not None:
193 num = int(choice)
194 except:
195 print("Invalid input")
196 continue
197 if choice is not None and (num < 0 or num >= len(stack)):
198 print("Invalid choice")
199 continue
200 break
201 if choice is not None:
202 self.skip_stack = stack
203 self.skip_num = num
204
205 def before_cli(self, cli):
206 """ Wait for ENTER before executing CLI """
207 if self.skip():
208 print("Skip pause before executing CLI: %s" % cli)
209 else:
210 print(double_line_delim)
211 print("Test paused before executing CLI: %s" % cli)
212 print(single_line_delim)
213 self.user_input()
214 super(StepHook, self).before_cli(cli)
215
216 def before_api(self, api_name, api_args):
217 """ Wait for ENTER before executing API """
218 if self.skip():
219 print("Skip pause before executing API: %s (%s)"
220 % (api_name, api_args))
221 else:
222 print(double_line_delim)
223 print("Test paused before executing API: %s (%s)"
224 % (api_name, api_args))
225 print(single_line_delim)
226 self.user_input()
227 super(StepHook, self).before_api(api_name, api_args)