class VatTerminal: """VAT interactive terminal. :param node: Node to open VAT terminal on. :param json_param: Defines if outputs from VAT are in JSON format. Default is True. :type node: dict :type json_param: bool """ __VAT_PROMPT = (u"vat# ", ) __LINUX_PROMPT = (u":~# ", u":~$ ", u"~]$ ", u"~]# ") def __init__(self, node, json_param=True): json_text = u" json" if json_param else u"" self.json = json_param self._node = node self._ssh = SSH() self._ssh.connect(self._node) try: self._tty = self._ssh.interactive_terminal_open() except Exception: raise RuntimeError(f"Cannot open interactive terminal on node " f"{self._node[u'host']}") for _ in range(3): try: self._ssh.interactive_terminal_exec_command( self._tty, f"sudo -S {Constants.VAT_BIN_NAME}{json_text}", self.__VAT_PROMPT) except Exception: continue else: break else: vpp_pid = get_vpp_pid(self._node) if vpp_pid: if isinstance(vpp_pid, int): logger.trace(f"VPP running on node {self._node[u'host']}") else: logger.error(f"More instances of VPP running " f"on node {self._node[u'host']}.") else: logger.error(f"VPP not running on node {self._node[u'host']}.") raise RuntimeError( f"Failed to open VAT console on node {self._node[u'host']}") self._exec_failure = False self.vat_stdout = None def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.vat_terminal_close() def vat_terminal_exec_cmd(self, cmd): """Execute command on the opened VAT terminal. :param cmd: Command to be executed. :returns: Command output in python representation of JSON format or None if not in JSON mode. """ PapiHistory.add_to_papi_history(self._node, cmd, papi=False) logger.debug(f"Executing command in VAT terminal: {cmd}") try: out = self._ssh.interactive_terminal_exec_command( self._tty, cmd, self.__VAT_PROMPT) self.vat_stdout = out except Exception: self._exec_failure = True vpp_pid = get_vpp_pid(self._node) if vpp_pid: if isinstance(vpp_pid, int): msg = f"VPP running on node {self._node[u'host']} " \ f"but VAT command {cmd} execution failed." else: msg = f"More instances of VPP running on node " \ f"{self._node[u'host']}. VAT command {cmd} " \ f"execution failed." else: msg = f"VPP not running on node {self._node[u'host']}. " \ f"VAT command {cmd} execution failed." raise RuntimeError(msg) logger.debug(f"VAT output: {out}") if self.json: obj_start = out.find(u"{") obj_end = out.rfind(u"}") array_start = out.find(u"[") array_end = out.rfind(u"]") if obj_start == -1 and array_start == -1: raise RuntimeError(f"VAT command {cmd}: no JSON data.") if obj_start < array_start or array_start == -1: start = obj_start end = obj_end + 1 else: start = array_start end = array_end + 1 out = out[start:end] json_out = json.loads(out) return json_out return None def vat_terminal_close(self): """Close VAT terminal.""" # interactive terminal is dead, we only need to close session if not self._exec_failure: try: self._ssh.interactive_terminal_exec_command( self._tty, u"quit", self.__LINUX_PROMPT) except Exception: vpp_pid = get_vpp_pid(self._node) if vpp_pid: if isinstance(vpp_pid, int): logger.trace( f"VPP running on node {self._node[u'host']}.") else: logger.error(f"More instances of VPP running " f"on node {self._node[u'host']}.") else: logger.error( f"VPP not running on node {self._node[u'host']}.") raise RuntimeError(f"Failed to close VAT console " f"on node {self._node[u'host']}") try: self._ssh.interactive_terminal_close(self._tty) except Exception: raise RuntimeError(f"Cannot close interactive terminal " f"on node {self._node[u'host']}") def vat_terminal_exec_cmd_from_template(self, vat_template_file, **args): """Execute VAT script from a file. :param vat_template_file: Template file name of a VAT script. :param args: Dictionary of parameters for VAT script. :returns: List of JSON objects returned by VAT. """ file_path = f"{Constants.RESOURCES_TPL_VAT}/{vat_template_file}" with open(file_path, u"rt") as template_file: cmd_template = template_file.readlines() ret = list() for line_tmpl in cmd_template: vat_cmd = line_tmpl.format(**args) ret.append(self.vat_terminal_exec_cmd(vat_cmd.replace(u"\n", u""))) return ret
class VatTerminal(object): """VAT interactive terminal. :param node: Node to open VAT terminal on. :param json_param: Defines if outputs from VAT are in JSON format. Default is True. :type node: dict :type json_param: bool """ __VAT_PROMPT = ("vat# ", ) __LINUX_PROMPT = (":~$ ", "~]$ ", "~]# ") def __init__(self, node, json_param=True): json_text = ' json' if json_param else '' self.json = json_param self._node = node self._ssh = SSH() self._ssh.connect(self._node) try: self._tty = self._ssh.interactive_terminal_open() except Exception: raise RuntimeError( "Cannot open interactive terminal on node {0}".format( self._node)) for _ in range(3): try: self._ssh.interactive_terminal_exec_command( self._tty, 'sudo -S {0}{1}'.format(Constants.VAT_BIN_NAME, json_text), self.__VAT_PROMPT) except Exception: continue else: break else: vpp_pid = get_vpp_pid(self._node) if vpp_pid: if isinstance(vpp_pid, int): logger.trace("VPP running on node {0}".format( self._node['host'])) else: logger.error( "More instances of VPP running on node {0}.".format( self._node['host'])) else: logger.error("VPP not running on node {0}.".format( self._node['host'])) raise RuntimeError("Failed to open VAT console on node {0}".format( self._node['host'])) self._exec_failure = False self.vat_stdout = None def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.vat_terminal_close() def vat_terminal_exec_cmd(self, cmd): """Execute command on the opened VAT terminal. :param cmd: Command to be executed. :returns: Command output in python representation of JSON format or None if not in JSON mode. """ VatHistory.add_to_vat_history(self._node, cmd) logger.debug("Executing command in VAT terminal: {0}".format(cmd)) try: out = self._ssh.interactive_terminal_exec_command( self._tty, cmd, self.__VAT_PROMPT) self.vat_stdout = out except Exception: self._exec_failure = True vpp_pid = get_vpp_pid(self._node) if vpp_pid: if isinstance(vpp_pid, int): raise RuntimeError( "VPP running on node {0} but VAT command" " {1} execution failed.".format( self._node['host'], cmd)) else: raise RuntimeError( "More instances of VPP running on node " "{0}. VAT command {1} execution failed.".format( self._node['host'], cmd)) else: raise RuntimeError("VPP not running on node {0}. VAT command " "{1} execution failed.".format( self._node['host'], cmd)) logger.debug("VAT output: {0}".format(out)) if self.json: obj_start = out.find('{') obj_end = out.rfind('}') array_start = out.find('[') array_end = out.rfind(']') if obj_start == -1 and array_start == -1: raise RuntimeError( "VAT command {0}: no JSON data.".format(cmd)) if obj_start < array_start or array_start == -1: start = obj_start end = obj_end + 1 else: start = array_start end = array_end + 1 out = out[start:end] json_out = json.loads(out) return json_out else: return None def vat_terminal_close(self): """Close VAT terminal.""" # interactive terminal is dead, we only need to close session if not self._exec_failure: try: self._ssh.interactive_terminal_exec_command( self._tty, 'quit', self.__LINUX_PROMPT) except Exception: vpp_pid = get_vpp_pid(self._node) if vpp_pid: if isinstance(vpp_pid, int): logger.trace("VPP running on node {0}.".format( self._node['host'])) else: logger.error("More instances of VPP running on node " "{0}.".format(self._node['host'])) else: logger.error("VPP not running on node {0}.".format( self._node['host'])) raise RuntimeError( "Failed to close VAT console on node {0}".format( self._node['host'])) try: self._ssh.interactive_terminal_close(self._tty) except: raise RuntimeError( "Cannot close interactive terminal on node {0}".format( self._node['host'])) def vat_terminal_exec_cmd_from_template(self, vat_template_file, **args): """Execute VAT script from a file. :param vat_template_file: Template file name of a VAT script. :param args: Dictionary of parameters for VAT script. :returns: List of JSON objects returned by VAT. """ file_path = '{}/{}'.format(Constants.RESOURCES_TPL_VAT, vat_template_file) with open(file_path, 'r') as template_file: cmd_template = template_file.readlines() ret = [] for line_tmpl in cmd_template: vat_cmd = line_tmpl.format(**args) ret.append(self.vat_terminal_exec_cmd(vat_cmd.replace('\n', ''))) return ret