blob: 77a2fc5bb23b800b511be273485b04d042c005fd [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
5from debug import spawn_gdb, gdb_path
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 Sekerab90be672017-10-10 06:44:05 +020065 if not spawn_gdb(self.testcase.vpp_bin, core_path, self.logger):
Klement Sekera277b89c2016-10-28 13:20:27 +020066 self.logger.error(
67 "Debugger '%s' does not exist or is not an executable.." %
68 gdb_path)
Klement Sekera909a6a12017-08-08 04:33:53 +020069 else:
70 return
71 self.logger.critical("Core file present, debug with: gdb %s %s" %
72 (self.testcase.vpp_bin, core_path))
Klement Sekeraf62ae122016-10-11 11:47:09 +020073
74 def poll_vpp(self):
75 """
76 Poll the vpp status and throw an exception if it's not running
77 :raises VppDiedError: exception if VPP is not running anymore
78 """
Klement Sekera085f5c02016-11-24 01:59:16 +010079 if self.testcase.vpp_dead:
Klement Sekeraf62ae122016-10-11 11:47:09 +020080 # already dead, nothing to do
81 return
82
83 self.testcase.vpp.poll()
84 if self.testcase.vpp.returncode is not None:
85 signaldict = dict(
86 (k, v) for v, k in reversed(sorted(signal.__dict__.items()))
87 if v.startswith('SIG') and not v.startswith('SIG_'))
Klement Sekera0e3c0de2016-09-29 14:43:44 +020088
89 if self.testcase.vpp.returncode in signaldict:
90 s = signaldict[abs(self.testcase.vpp.returncode)]
91 else:
92 s = "unknown"
Klement Sekerada505f62017-01-04 12:58:53 +010093 msg = "VPP subprocess died unexpectedly with returncode %d [%s]" %\
94 (self.testcase.vpp.returncode, s)
Klement Sekera277b89c2016-10-28 13:20:27 +020095 self.logger.critical(msg)
Klement Sekeraf62ae122016-10-11 11:47:09 +020096 core_path = self.testcase.tempdir + '/core'
97 if os.path.isfile(core_path):
98 self.on_crash(core_path)
99 self.testcase.vpp_dead = True
100 raise VppDiedError(msg)
101
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200102 def before_api(self, api_name, api_args):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200103 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200104 Check if VPP died before executing an API
Klement Sekeraf62ae122016-10-11 11:47:09 +0200105
106 :param api_name: name of the API
107 :param api_args: tuple containing the API arguments
108 :raises VppDiedError: exception if VPP is not running anymore
109
110 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200111 super(PollHook, self).before_api(api_name, api_args)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200112 self.poll_vpp()
113
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200114 def before_cli(self, cli):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200115 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200116 Check if VPP died before executing a CLI
Klement Sekeraf62ae122016-10-11 11:47:09 +0200117
118 :param cli: CLI string
119 :raises Exception: exception if VPP is not running anymore
120
121 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200122 super(PollHook, self).before_cli(cli)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200123 self.poll_vpp()
Klement Sekera277b89c2016-10-28 13:20:27 +0200124
125
126class StepHook(PollHook):
127 """ Hook which requires user to press ENTER before doing any API/CLI """
128
129 def __init__(self, testcase):
130 self.skip_stack = None
131 self.skip_num = None
132 self.skip_count = 0
133 super(StepHook, self).__init__(testcase)
134
135 def skip(self):
136 if self.skip_stack is None:
137 return False
138 stack = traceback.extract_stack()
139 counter = 0
140 skip = True
141 for e in stack:
142 if counter > self.skip_num:
143 break
144 if e[0] != self.skip_stack[counter][0]:
145 skip = False
146 if e[1] != self.skip_stack[counter][1]:
147 skip = False
148 counter += 1
149 if skip:
150 self.skip_count += 1
151 return True
152 else:
153 print("%d API/CLI calls skipped in specified stack "
154 "frame" % self.skip_count)
155 self.skip_count = 0
156 self.skip_stack = None
157 self.skip_num = None
158 return False
159
160 def user_input(self):
161 print('number\tfunction\tfile\tcode')
162 counter = 0
163 stack = traceback.extract_stack()
164 for e in stack:
165 print('%02d.\t%s\t%s:%d\t[%s]' % (counter, e[2], e[0], e[1], e[3]))
166 counter += 1
167 print(single_line_delim)
168 print("You can enter a number of stack frame chosen from above")
169 print("Calls in/below that stack frame will be not be stepped anymore")
170 print(single_line_delim)
171 while True:
172 choice = raw_input("Enter your choice, if any, and press ENTER to "
173 "continue running the testcase...")
174 if choice == "":
175 choice = None
176 try:
177 if choice is not None:
178 num = int(choice)
179 except:
180 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)