IPv4/6 reassembly
[vpp.git] / test / hook.py
1 import signal
2 import os
3 import traceback
4 from log import RED, single_line_delim, double_line_delim
5 from debug import spawn_gdb
6
7
8 class Hook(object):
9     """
10     Generic hooks before/after API/CLI calls
11     """
12
13     def __init__(self, logger):
14         self.logger = logger
15
16     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         """
24         self.logger.debug("API: %s (%s)" %
25                           (api_name, api_args), extra={'color': RED})
26
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         """
43         self.logger.debug("CLI: %s" % (cli), extra={'color': RED})
44
45     def after_cli(self, cli):
46         """
47         Function called after CLI call
48         """
49         pass
50
51
52 class VppDiedError(Exception):
53     pass
54
55
56 class PollHook(Hook):
57     """ Hook which checks if the vpp subprocess is alive """
58
59     def __init__(self, testcase):
60         self.testcase = testcase
61         self.logger = testcase.logger
62
63     def on_crash(self, core_path):
64         if self.testcase.debug_core:
65             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))
69
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         """
75         if self.testcase.vpp_dead:
76             # 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_'))
84
85             if self.testcase.vpp.returncode in signaldict:
86                 s = signaldict[abs(self.testcase.vpp.returncode)]
87             else:
88                 s = "unknown"
89             msg = "VPP subprocess died unexpectedly with returncode %d [%s]" %\
90                 (self.testcase.vpp.returncode, s)
91             self.logger.critical(msg)
92             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
98     def before_api(self, api_name, api_args):
99         """
100         Check if VPP died before executing an API
101
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         """
107         super(PollHook, self).before_api(api_name, api_args)
108         self.poll_vpp()
109
110     def before_cli(self, cli):
111         """
112         Check if VPP died before executing a CLI
113
114         :param cli: CLI string
115         :raises Exception: exception if VPP is not running anymore
116
117         """
118         super(PollHook, self).before_cli(cli)
119         self.poll_vpp()
120
121
122 class 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)