1 # Copyright (c) 2018 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:
6 # http://www.apache.org/licenses/LICENSE-2.0
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.
14 """Python API executor library."""
19 from paramiko.ssh_exception import SSHException
20 from robot.api import logger
22 from resources.libraries.python.constants import Constants
23 from resources.libraries.python.PapiErrors import PapiInitError, \
24 PapiJsonFileError, PapiCommandError, PapiCommandInputError
25 # TODO: from resources.libraries.python.PapiHistory import PapiHistory
26 from resources.libraries.python.ssh import SSH, SSHTimeout
28 __all__ = ['PapiExecutor']
31 class PapiExecutor(object):
32 """Contains methods for executing Python API commands on DUTs."""
34 def __init__(self, node):
39 self._json_data = None
40 self._api_reply = list()
45 self._ssh.connect(node)
47 raise SSHException('Cannot open SSH connection to host {host} to '
48 'execute PAPI command(s)'.
49 format(host=self._node['host']))
54 def __exit__(self, exc_type, exc_val, exc_tb):
58 def _process_api_data(api_d):
59 """Process API data for smooth converting to JSON string.
61 Apply binascii.hexlify() method for string values.
63 :param api_d: List of APIs with their arguments.
65 :returns: List of APIs with arguments pre-processed for JSON.
69 api_data_processed = list()
71 api_name = api['api_name']
72 api_args = api['api_args']
73 api_processed = dict(api_name=api_name)
74 api_args_processed = dict()
75 for a_k, a_v in api_args.iteritems():
76 value = binascii.hexlify(a_v) if isinstance(a_v, str) else a_v
77 api_args_processed[str(a_k)] = value
78 api_processed['api_args'] = api_args_processed
79 api_data_processed.append(api_processed)
80 return api_data_processed
83 def _revert_api_reply(api_r):
84 """Process API reply / a part of API reply.
86 Apply binascii.unhexlify() method for unicode values.
88 :param api_r: API reply.
90 :returns: Processed API reply / a part of API reply.
96 for reply_key, reply_v in api_r.iteritems():
97 for a_k, a_v in reply_v.iteritems():
98 # value = binascii.unhexlify(a_v) if isinstance(a_v, unicode) \
100 # reply_value[a_k] = value
101 reply_value[a_k] = a_v
102 reply_dict[reply_key] = reply_value
105 def _process_reply(self, api_reply):
106 """Process API reply.
108 :param api_reply: API reply.
109 :type api_reply: dict or list of dict
110 :returns: Processed API reply.
114 if isinstance(api_reply, list):
115 reverted_reply = list()
116 for a_r in api_reply:
117 reverted_reply.append(self._revert_api_reply(a_r))
119 reverted_reply = self._revert_api_reply(api_reply)
120 return reverted_reply
122 def _process_json_data(self):
123 """Process received JSON data."""
125 for data in self._json_data:
126 api_name = data['api_name']
127 api_reply = data['api_reply']
128 api_reply_processed = dict(
129 api_name=api_name, api_reply=self._process_reply(api_reply))
130 self._api_reply.append(api_reply_processed)
132 def execute_papi(self, api_data, timeout=120):
133 """Execute PAPI command(s) on remote node and store the result.
135 :param api_data: List of APIs with their arguments.
136 :param timeout: Timeout in seconds.
139 :raises SSHTimeout: If PAPI command(s) execution is timed out.
140 :raises PapiInitError: If PAPI initialization failed.
141 :raises PapiJsonFileError: If no api.json file found.
142 :raises PapiCommandError: If PAPI command(s) execution failed.
143 :raises PapiCommandInputError: If invalid attribute name or invalid
144 value is used in API call.
145 :raises RuntimeError: If PAPI executor failed due to another reason.
147 self._api_data = api_data
148 api_data_processed = self._process_api_data(api_data)
149 json_data = json.dumps(api_data_processed)
151 cmd = "python {fw_dir}/{papi_provider} --json_data '{json}'".format(
152 fw_dir=Constants.REMOTE_FW_DIR,
153 papi_provider=Constants.RESOURCES_PAPI_PROVIDER,
157 ret_code, stdout, stderr = self._ssh.exec_command_sudo(
158 cmd=cmd, timeout=timeout)
160 logger.error('PAPI command(s) execution timeout on host {host}:'
161 '\n{apis}'.format(host=self._node['host'],
162 apis=self._api_data))
164 except (PapiInitError, PapiJsonFileError, PapiCommandError,
165 PapiCommandInputError):
166 logger.error('PAPI command(s) execution failed on host {host}'.
167 format(host=self._node['host']))
170 raise RuntimeError('PAPI command(s) execution on host {host} '
171 'failed: {apis}'.format(host=self._node['host'],
172 apis=self._api_data))
174 self._ret_code = ret_code
175 self._stdout = stdout
176 self._stderr = stderr
178 def papi_should_have_failed(self):
179 """Read return code from last executed script and raise exception if the
180 PAPI command(s) didn't fail.
182 :raises RuntimeError: When no PAPI command executed.
183 :raises AssertionError: If PAPI command(s) execution passed.
186 if self._ret_code is None:
187 raise RuntimeError("First execute the PAPI command(s)!")
188 if self._ret_code == 0:
189 raise AssertionError(
190 "PAPI command(s) execution passed, but failure was expected: "
191 "{apis}".format(apis=self._api_data))
193 def papi_should_have_passed(self):
194 """Read return code from last executed script and raise exception if the
195 PAPI command(s) failed.
197 :raises RuntimeError: When no PAPI command executed.
198 :raises AssertionError: If PAPI command(s) execution failed.
201 if self._ret_code is None:
202 raise RuntimeError("First execute the PAPI command(s)!")
203 if self._ret_code != 0:
204 raise AssertionError(
205 "PAPI command(s) execution failed, but success was expected: "
206 "{apis}".format(apis=self._api_data))
208 def get_papi_stdout(self):
209 """Returns value of stdout from last executed PAPI command(s)."""
213 def get_papi_stderr(self):
214 """Returns value of stderr from last executed PAPI command(s)."""
218 def get_papi_reply(self):
219 """Returns api reply from last executed PAPI command(s)."""
221 self._json_data = json.loads(self._stdout)
222 self._process_json_data()
224 return self._api_reply