IGMP: proxy device
[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 subprocess import check_output, CalledProcessError
7 from util import check_core_path, get_core_path
8
9
10 class Hook(object):
11     """
12     Generic hooks before/after API/CLI calls
13     """
14
15     def __init__(self, logger):
16         self.logger = logger
17
18     def before_api(self, api_name, api_args):
19         """
20         Function called before API call
21         Emit a debug message describing the API name and arguments
22
23         @param api_name: name of the API
24         @param api_args: tuple containing the API arguments
25         """
26         self.logger.debug("API: %s (%s)" %
27                           (api_name, api_args), extra={'color': RED})
28
29     def after_api(self, api_name, api_args):
30         """
31         Function called after API call
32
33         @param api_name: name of the API
34         @param api_args: tuple containing the API arguments
35         """
36         pass
37
38     def before_cli(self, cli):
39         """
40         Function called before CLI call
41         Emit a debug message describing the CLI
42
43         @param cli: CLI string
44         """
45         self.logger.debug("CLI: %s" % (cli), extra={'color': RED})
46
47     def after_cli(self, cli):
48         """
49         Function called after CLI call
50         """
51         pass
52
53
54 class VppDiedError(Exception):
55     pass
56
57
58 class PollHook(Hook):
59     """ Hook which checks if the vpp subprocess is alive """
60
61     def __init__(self, testcase):
62         super(PollHook, self).__init__(testcase.logger)
63         self.testcase = testcase
64
65     def on_crash(self, core_path):
66         self.logger.error("Core file present, debug with: gdb %s %s" %
67                           (self.testcase.vpp_bin, core_path))
68         check_core_path(self.logger, core_path)
69         self.logger.error("Running `file %s':" % core_path)
70         try:
71             info = check_output(["file", core_path])
72             self.logger.error(info)
73         except CalledProcessError as e:
74             self.logger.error(
75                 "Could not run `file' utility on core-file, "
76                 "rc=%s" % e.returncode)
77
78     def poll_vpp(self):
79         """
80         Poll the vpp status and throw an exception if it's not running
81         :raises VppDiedError: exception if VPP is not running anymore
82         """
83         if self.testcase.vpp_dead:
84             # already dead, nothing to do
85             return
86
87         self.testcase.vpp.poll()
88         if self.testcase.vpp.returncode is not None:
89             signaldict = dict(
90                 (k, v) for v, k in reversed(sorted(signal.__dict__.items()))
91                 if v.startswith('SIG') and not v.startswith('SIG_'))
92
93             if self.testcase.vpp.returncode in signaldict:
94                 s = signaldict[abs(self.testcase.vpp.returncode)]
95             else:
96                 s = "unknown"
97             msg = "VPP subprocess died unexpectedly with returncode %d [%s]" %\
98                 (self.testcase.vpp.returncode, s)
99             self.logger.critical(msg)
100             core_path = get_core_path(self.testcase.tempdir)
101             if os.path.isfile(core_path):
102                 self.on_crash(core_path)
103             self.testcase.vpp_dead = True
104             raise VppDiedError(msg)
105
106     def before_api(self, api_name, api_args):
107         """
108         Check if VPP died before executing an API
109
110         :param api_name: name of the API
111         :param api_args: tuple containing the API arguments
112         :raises VppDiedError: exception if VPP is not running anymore
113
114         """
115         super(PollHook, self).before_api(api_name, api_args)
116         self.poll_vpp()
117
118     def before_cli(self, cli):
119         """
120         Check if VPP died before executing a CLI
121
122         :param cli: CLI string
123         :raises Exception: exception if VPP is not running anymore
124
125         """
126         super(PollHook, self).before_cli(cli)
127         self.poll_vpp()
128
129
130 class StepHook(PollHook):
131     """ Hook which requires user to press ENTER before doing any API/CLI """
132
133     def __init__(self, testcase):
134         self.skip_stack = None
135         self.skip_num = None
136         self.skip_count = 0
137         super(StepHook, self).__init__(testcase)
138
139     def skip(self):
140         if self.skip_stack is None:
141             return False
142         stack = traceback.extract_stack()
143         counter = 0
144         skip = True
145         for e in stack:
146             if counter > self.skip_num:
147                 break
148             if e[0] != self.skip_stack[counter][0]:
149                 skip = False
150             if e[1] != self.skip_stack[counter][1]:
151                 skip = False
152             counter += 1
153         if skip:
154             self.skip_count += 1
155             return True
156         else:
157             print("%d API/CLI calls skipped in specified stack "
158                   "frame" % self.skip_count)
159             self.skip_count = 0
160             self.skip_stack = None
161             self.skip_num = None
162             return False
163
164     def user_input(self):
165         print('number\tfunction\tfile\tcode')
166         counter = 0
167         stack = traceback.extract_stack()
168         for e in stack:
169             print('%02d.\t%s\t%s:%d\t[%s]' % (counter, e[2], e[0], e[1], e[3]))
170             counter += 1
171         print(single_line_delim)
172         print("You may enter a number of stack frame chosen from above")
173         print("Calls in/below that stack frame will be not be stepped anymore")
174         print(single_line_delim)
175         while True:
176             print("Enter your choice, if any, and press ENTER to continue "
177                   "running the testcase...")
178             choice = sys.stdin.readline().rstrip('\r\n')
179             if choice == "":
180                 choice = None
181             try:
182                 if choice is not None:
183                     num = int(choice)
184             except ValueError:
185                 print("Invalid input")
186                 continue
187             if choice is not None and (num < 0 or num >= len(stack)):
188                 print("Invalid choice")
189                 continue
190             break
191         if choice is not None:
192             self.skip_stack = stack
193             self.skip_num = num
194
195     def before_cli(self, cli):
196         """ Wait for ENTER before executing CLI """
197         if self.skip():
198             print("Skip pause before executing CLI: %s" % cli)
199         else:
200             print(double_line_delim)
201             print("Test paused before executing CLI: %s" % cli)
202             print(single_line_delim)
203             self.user_input()
204         super(StepHook, self).before_cli(cli)
205
206     def before_api(self, api_name, api_args):
207         """ Wait for ENTER before executing API """
208         if self.skip():
209             print("Skip pause before executing API: %s (%s)"
210                   % (api_name, api_args))
211         else:
212             print(double_line_delim)
213             print("Test paused before executing API: %s (%s)"
214                   % (api_name, api_args))
215             print(single_line_delim)
216             self.user_input()
217         super(StepHook, self).before_api(api_name, api_args)