acl-plugin: improvements in 'show acl-plugin macip acl' CLI
[vpp.git] / test / hook.py
1 import signal
2 import os
3 import sys
4 import traceback
5 from log import RED, single_line_delim, double_line_delim
6 from debug import spawn_gdb
7
8
9 class Hook(object):
10     """
11     Generic hooks before/after API/CLI calls
12     """
13
14     def __init__(self, logger):
15         self.logger = logger
16
17     def before_api(self, api_name, api_args):
18         """
19         Function called before API call
20         Emit a debug message describing the API name and arguments
21
22         @param api_name: name of the API
23         @param api_args: tuple containing the API arguments
24         """
25         self.logger.debug("API: %s (%s)" %
26                           (api_name, api_args), extra={'color': RED})
27
28     def after_api(self, api_name, api_args):
29         """
30         Function called after API call
31
32         @param api_name: name of the API
33         @param api_args: tuple containing the API arguments
34         """
35         pass
36
37     def before_cli(self, cli):
38         """
39         Function called before CLI call
40         Emit a debug message describing the CLI
41
42         @param cli: CLI string
43         """
44         self.logger.debug("CLI: %s" % (cli), extra={'color': RED})
45
46     def after_cli(self, cli):
47         """
48         Function called after CLI call
49         """
50         pass
51
52
53 class VppDiedError(Exception):
54     pass
55
56
57 class PollHook(Hook):
58     """ Hook which checks if the vpp subprocess is alive """
59
60     def __init__(self, testcase):
61         self.testcase = testcase
62         self.logger = testcase.logger
63
64     def on_crash(self, core_path):
65         if self.testcase.debug_core:
66             # notify parent process that we're handling a core file
67             open('%s/_core_handled' % self.testcase.tempdir, 'a').close()
68             spawn_gdb(self.testcase.vpp_bin, core_path, self.logger)
69         else:
70             self.logger.critical("Core file present, debug with: gdb %s %s" %
71                                  (self.testcase.vpp_bin, core_path))
72
73     def poll_vpp(self):
74         """
75         Poll the vpp status and throw an exception if it's not running
76         :raises VppDiedError: exception if VPP is not running anymore
77         """
78         if self.testcase.vpp_dead:
79             # already dead, nothing to do
80             return
81
82         self.testcase.vpp.poll()
83         if self.testcase.vpp.returncode is not None:
84             signaldict = dict(
85                 (k, v) for v, k in reversed(sorted(signal.__dict__.items()))
86                 if v.startswith('SIG') and not v.startswith('SIG_'))
87
88             if self.testcase.vpp.returncode in signaldict:
89                 s = signaldict[abs(self.testcase.vpp.returncode)]
90             else:
91                 s = "unknown"
92             msg = "VPP subprocess died unexpectedly with returncode %d [%s]" %\
93                 (self.testcase.vpp.returncode, s)
94             self.logger.critical(msg)
95             core_path = self.testcase.tempdir + '/core'
96             if os.path.isfile(core_path):
97                 self.on_crash(core_path)
98             self.testcase.vpp_dead = True
99             raise VppDiedError(msg)
100
101     def before_api(self, api_name, api_args):
102         """
103         Check if VPP died before executing an API
104
105         :param api_name: name of the API
106         :param api_args: tuple containing the API arguments
107         :raises VppDiedError: exception if VPP is not running anymore
108
109         """
110         super(PollHook, self).before_api(api_name, api_args)
111         self.poll_vpp()
112
113     def before_cli(self, cli):
114         """
115         Check if VPP died before executing a CLI
116
117         :param cli: CLI string
118         :raises Exception: exception if VPP is not running anymore
119
120         """
121         super(PollHook, self).before_cli(cli)
122         self.poll_vpp()
123
124
125 class StepHook(PollHook):
126     """ Hook which requires user to press ENTER before doing any API/CLI """
127
128     def __init__(self, testcase):
129         self.skip_stack = None
130         self.skip_num = None
131         self.skip_count = 0
132         super(StepHook, self).__init__(testcase)
133
134     def skip(self):
135         if self.skip_stack is None:
136             return False
137         stack = traceback.extract_stack()
138         counter = 0
139         skip = True
140         for e in stack:
141             if counter > self.skip_num:
142                 break
143             if e[0] != self.skip_stack[counter][0]:
144                 skip = False
145             if e[1] != self.skip_stack[counter][1]:
146                 skip = False
147             counter += 1
148         if skip:
149             self.skip_count += 1
150             return True
151         else:
152             print("%d API/CLI calls skipped in specified stack "
153                   "frame" % self.skip_count)
154             self.skip_count = 0
155             self.skip_stack = None
156             self.skip_num = None
157             return False
158
159     def user_input(self):
160         print('number\tfunction\tfile\tcode')
161         counter = 0
162         stack = traceback.extract_stack()
163         for e in stack:
164             print('%02d.\t%s\t%s:%d\t[%s]' % (counter, e[2], e[0], e[1], e[3]))
165             counter += 1
166         print(single_line_delim)
167         print("You can enter a number of stack frame chosen from above")
168         print("Calls in/below that stack frame will be not be stepped anymore")
169         print(single_line_delim)
170         while True:
171             choice = sys.stdin.readline(
172                 "Enter your choice, if any, and press ENTER to continue "
173                 "running the testcase...")
174             if choice == "":
175                 choice = None
176             try:
177                 if choice is not None:
178                     num = int(choice)
179             except TypeError:
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)