Add Honeycomb persistence tests
[csit.git] / resources / libraries / python / VatExecutor.py
1 # Copyright (c) 2016 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 import json
15
16 from robot.api import logger
17
18 from resources.libraries.python.ssh import SSH
19 from resources.libraries.python.constants import Constants
20
21
22 __all__ = ['VatExecutor']
23
24
25 def cleanup_vat_json_output(json_output):
26     """Return VAT JSON output cleaned from VAT clutter.
27
28     Clean up VAT JSON output from clutter like vat# prompts and such.
29
30     :param json_output: Cluttered JSON output.
31     :return: Cleaned up output JSON string.
32     """
33
34     retval = json_output
35     clutter = ['vat#', 'dump_interface_table error: Misc']
36     for garbage in clutter:
37         retval = retval.replace(garbage, '')
38     return retval
39
40
41 class VatExecutor(object):
42     def __init__(self):
43         self._stdout = None
44         self._stderr = None
45         self._ret_code = None
46
47     def execute_script(self, vat_name, node, timeout=10, json_out=True):
48         """Copy local_path script to node, execute it and return result.
49
50         :param vat_name: Name of the vat script file. Only the file name of
51         the script is required, the resources path is prepended automatically.
52         :param node: Node to execute the VAT script on.
53         :param timeout: Seconds to allow the script to run.
54         :param json_out: Require JSON output.
55         :return: (rc, stdout, stderr) tuple.
56         """
57
58         ssh = SSH()
59         ssh.connect(node)
60
61         remote_file_path = '{0}/{1}/{2}'.format(Constants.REMOTE_FW_DIR,
62                                                 Constants.RESOURCES_TPL_VAT,
63                                                 vat_name)
64         # TODO this overwrites the output if the vat script has been used twice
65         remote_file_out = remote_file_path + ".out"
66
67         cmd = "sudo -S {vat} {json} < {input}".format(
68             vat=Constants.VAT_BIN_NAME,
69             json="json" if json_out is True else "",
70             input=remote_file_path)
71         (ret_code, stdout, stderr) = ssh.exec_command(cmd, timeout)
72         self._ret_code = ret_code
73         self._stdout = stdout
74         self._stderr = stderr
75
76         logger.trace("Command '{0}' returned {1}'".format(cmd, self._ret_code))
77         logger.trace("stdout: '{0}'".format(self._stdout))
78         logger.trace("stderr: '{0}'".format(self._stderr))
79
80         # TODO: download vpp_api_test output file
81         # self._delete_files(node, remote_file_path, remote_file_out)
82
83     def execute_script_json_out(self, vat_name, node, timeout=10):
84         self.execute_script(vat_name, node, timeout, json_out=True)
85         self._stdout = cleanup_vat_json_output(self._stdout)
86
87     @staticmethod
88     def _delete_files(node, *files):
89         ssh = SSH()
90         ssh.connect(node)
91         files = " ".join([str(x) for x in files])
92         ssh.exec_command("rm {0}".format(files))
93
94     def script_should_have_failed(self):
95         if self._ret_code is None:
96             raise Exception("First execute the script!")
97         if self._ret_code == 0:
98             raise AssertionError(
99                 "Script execution passed, but failure was expected")
100
101     def script_should_have_passed(self):
102         if self._ret_code is None:
103             raise Exception("First execute the script!")
104         if self._ret_code != 0:
105             raise AssertionError(
106                 "Script execution failed, but success was expected")
107
108     def get_script_stdout(self):
109         return self._stdout
110
111     def get_script_stderr(self):
112         return self._stderr
113
114     @staticmethod
115     def cmd_from_template(node, vat_template_file, **vat_args):
116         """Execute VAT script on specified node. This method supports
117         script templates with parameters.
118
119         :param node: Node in topology on witch the script is executed.
120         :param vat_template_file: Template file of VAT script.
121         :param vat_args: Arguments to the template file.
122         :return: List of JSON objects returned by VAT.
123         """
124         with VatTerminal(node) as vat:
125             return vat.vat_terminal_exec_cmd_from_template(vat_template_file,
126                                                            **vat_args)
127
128
129 class VatTerminal(object):
130     """VAT interactive terminal.
131
132     :param node: Node to open VAT terminal on.
133     :param json_param: Defines if outputs from VAT are in JSON format.
134     Default is True.
135     :type node: dict
136     :type json_param: bool
137
138     """
139
140     __VAT_PROMPT = "vat# "
141     __LINUX_PROMPT = ":~$ "
142
143     def __init__(self, node, json_param=True):
144         json_text = ' json' if json_param else ''
145         self.json = json_param
146         self._ssh = SSH()
147         self._ssh.connect(node)
148         self._tty = self._ssh.interactive_terminal_open()
149         self._ssh.interactive_terminal_exec_command(
150             self._tty,
151             'sudo -S {}{}'.format(Constants.VAT_BIN_NAME, json_text),
152             self.__VAT_PROMPT)
153
154     def __enter__(self):
155         return self
156
157     def __exit__(self, exc_type, exc_val, exc_tb):
158         self.vat_terminal_close()
159
160     def vat_terminal_exec_cmd(self, cmd):
161         """Execute command on the opened VAT terminal.
162
163         :param cmd: Command to be executed.
164
165         :return: Command output in python representation of JSON format or
166         None if not in JSON mode.
167         """
168         logger.debug("Executing command in VAT terminal: {}".format(cmd))
169         out = self._ssh.interactive_terminal_exec_command(self._tty,
170                                                           cmd,
171                                                           self.__VAT_PROMPT)
172         logger.debug("VAT output: {}".format(out))
173         if self.json:
174             obj_start = out.find('{')
175             obj_end = out.rfind('}')
176             array_start = out.find('[')
177             array_end = out.rfind(']')
178
179             if -1 == obj_start and -1 == array_start:
180                 raise RuntimeError("No JSON data.")
181
182             if obj_start < array_start or -1 == array_start:
183                 start = obj_start
184                 end = obj_end + 1
185             else:
186                 start = array_start
187                 end = array_end + 1
188             out = out[start:end]
189             json_out = json.loads(out)
190             return json_out
191         else:
192             return None
193
194     def vat_terminal_close(self):
195         """Close VAT terminal."""
196         self._ssh.interactive_terminal_exec_command(self._tty,
197                                                     'quit',
198                                                     self.__LINUX_PROMPT)
199         self._ssh.interactive_terminal_close(self._tty)
200
201     def vat_terminal_exec_cmd_from_template(self, vat_template_file, **args):
202         """Execute VAT script from a file.
203
204         :param vat_template_file: Template file name of a VAT script.
205         :param args: Dictionary of parameters for VAT script.
206         :return: List of JSON objects returned by VAT.
207         """
208         file_path = '{}/{}'.format(Constants.RESOURCES_TPL_VAT,
209                                    vat_template_file)
210         with open(file_path, 'r') as template_file:
211             cmd_template = template_file.readlines()
212         ret = []
213         for line_tmpl in cmd_template:
214             vat_cmd = line_tmpl.format(**args)
215             ret.append(self.vat_terminal_exec_cmd(vat_cmd.replace('\n', '')))
216         return ret