blob: 0ef1377ba9962bcbc5eadf6eb2aae0d5cd3462be [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 Sekera75e7d132017-09-20 08:26:30 +020065 spawn_gdb(self.testcase.vpp_bin, core_path, self.logger)
66 else:
67 self.logger.critical("Core file present, debug with: gdb %s %s" %
68 (self.testcase.vpp_bin, core_path))
Klement Sekeraf62ae122016-10-11 11:47:09 +020069
70 def poll_vpp(self):
71 """
72 Poll the vpp status and throw an exception if it's not running
73 :raises VppDiedError: exception if VPP is not running anymore
74 """
Klement Sekera085f5c02016-11-24 01:59:16 +010075 if self.testcase.vpp_dead:
Klement Sekeraf62ae122016-10-11 11:47:09 +020076 # already dead, nothing to do
77 return
78
79 self.testcase.vpp.poll()
80 if self.testcase.vpp.returncode is not None:
81 signaldict = dict(
82 (k, v) for v, k in reversed(sorted(signal.__dict__.items()))
83 if v.startswith('SIG') and not v.startswith('SIG_'))
Klement Sekera0e3c0de2016-09-29 14:43:44 +020084
85 if self.testcase.vpp.returncode in signaldict:
86 s = signaldict[abs(self.testcase.vpp.returncode)]
87 else:
88 s = "unknown"
Klement Sekerada505f62017-01-04 12:58:53 +010089 msg = "VPP subprocess died unexpectedly with returncode %d [%s]" %\
90 (self.testcase.vpp.returncode, s)
Klement Sekera277b89c2016-10-28 13:20:27 +020091 self.logger.critical(msg)
Klement Sekeraf62ae122016-10-11 11:47:09 +020092 core_path = self.testcase.tempdir + '/core'
93 if os.path.isfile(core_path):
94 self.on_crash(core_path)
95 self.testcase.vpp_dead = True
96 raise VppDiedError(msg)
97
Klement Sekera0e3c0de2016-09-29 14:43:44 +020098 def before_api(self, api_name, api_args):
Klement Sekeraf62ae122016-10-11 11:47:09 +020099 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200100 Check if VPP died before executing an API
Klement Sekeraf62ae122016-10-11 11:47:09 +0200101
102 :param api_name: name of the API
103 :param api_args: tuple containing the API arguments
104 :raises VppDiedError: exception if VPP is not running anymore
105
106 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200107 super(PollHook, self).before_api(api_name, api_args)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200108 self.poll_vpp()
109
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200110 def before_cli(self, cli):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200111 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200112 Check if VPP died before executing a CLI
Klement Sekeraf62ae122016-10-11 11:47:09 +0200113
114 :param cli: CLI string
115 :raises Exception: exception if VPP is not running anymore
116
117 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200118 super(PollHook, self).before_cli(cli)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200119 self.poll_vpp()
Klement Sekera277b89c2016-10-28 13:20:27 +0200120
121
122class StepHook(PollHook):
123 """ Hook which requires user to press ENTER before doing any API/CLI """
124
125 def __init__(self, testcase):
126 self.skip_stack = None
127 self.skip_num = None
128 self.skip_count = 0
129 super(StepHook, self).__init__(testcase)
130
131 def skip(self):
132 if self.skip_stack is None:
133 return False
134 stack = traceback.extract_stack()
135 counter = 0
136 skip = True
137 for e in stack:
138 if counter > self.skip_num:
139 break
140 if e[0] != self.skip_stack[counter][0]:
141 skip = False
142 if e[1] != self.skip_stack[counter][1]:
143 skip = False
144 counter += 1
145 if skip:
146 self.skip_count += 1
147 return True
148 else:
149 print("%d API/CLI calls skipped in specified stack "
150 "frame" % self.skip_count)
151 self.skip_count = 0
152 self.skip_stack = None
153 self.skip_num = None
154 return False
155
156 def user_input(self):
157 print('number\tfunction\tfile\tcode')
158 counter = 0
159 stack = traceback.extract_stack()
160 for e in stack:
161 print('%02d.\t%s\t%s:%d\t[%s]' % (counter, e[2], e[0], e[1], e[3]))
162 counter += 1
163 print(single_line_delim)
164 print("You can enter a number of stack frame chosen from above")
165 print("Calls in/below that stack frame will be not be stepped anymore")
166 print(single_line_delim)
167 while True:
168 choice = raw_input("Enter your choice, if any, and press ENTER to "
169 "continue running the testcase...")
170 if choice == "":
171 choice = None
172 try:
173 if choice is not None:
174 num = int(choice)
175 except:
176 print("Invalid input")
177 continue
178 if choice is not None and (num < 0 or num >= len(stack)):
179 print("Invalid choice")
180 continue
181 break
182 if choice is not None:
183 self.skip_stack = stack
184 self.skip_num = num
185
186 def before_cli(self, cli):
187 """ Wait for ENTER before executing CLI """
188 if self.skip():
189 print("Skip pause before executing CLI: %s" % cli)
190 else:
191 print(double_line_delim)
192 print("Test paused before executing CLI: %s" % cli)
193 print(single_line_delim)
194 self.user_input()
195 super(StepHook, self).before_cli(cli)
196
197 def before_api(self, api_name, api_args):
198 """ Wait for ENTER before executing API """
199 if self.skip():
200 print("Skip pause before executing API: %s (%s)"
201 % (api_name, api_args))
202 else:
203 print(double_line_delim)
204 print("Test paused before executing API: %s (%s)"
205 % (api_name, api_args))
206 print(single_line_delim)
207 self.user_input()
208 super(StepHook, self).before_api(api_name, api_args)