From: "Juraj Linkeš" <juraj.linkes@pantheon.tech>
To: thomas@monjalon.net, david.marchand@redhat.com,
Honnappa.Nagarahalli@arm.com, ohilyard@iol.unh.edu,
lijuan.tu@intel.com
Cc: dev@dpdk.org, "Juraj Linkeš" <juraj.linkes@pantheon.tech>
Subject: [RFC PATCH v1 10/18] dts: merge DTS framework/ssh_pexpect.py to DPDK
Date: Wed, 6 Apr 2022 15:04:32 +0000 [thread overview]
Message-ID: <20220406150440.2914464-11-juraj.linkes@pantheon.tech> (raw)
In-Reply-To: <20220406150440.2914464-1-juraj.linkes@pantheon.tech>
---
dts/framework/ssh_pexpect.py | 263 +++++++++++++++++++++++++++++++++++
1 file changed, 263 insertions(+)
create mode 100644 dts/framework/ssh_pexpect.py
diff --git a/dts/framework/ssh_pexpect.py b/dts/framework/ssh_pexpect.py
new file mode 100644
index 0000000000..97406896f0
--- /dev/null
+++ b/dts/framework/ssh_pexpect.py
@@ -0,0 +1,263 @@
+import time
+
+import pexpect
+from pexpect import pxssh
+
+from .debugger import aware_keyintr, ignore_keyintr
+from .exception import SSHConnectionException, SSHSessionDeadException, TimeoutException
+from .utils import GREEN, RED, parallel_lock
+
+"""
+Module handle ssh sessions between tester and DUT.
+Implements send_expect function to send command and get output data.
+Also supports transfer files to tester or DUT.
+"""
+
+
+class SSHPexpect:
+ def __init__(self, host, username, password, dut_id):
+ self.magic_prompt = "MAGIC PROMPT"
+ self.logger = None
+
+ self.host = host
+ self.username = username
+ self.password = password
+
+ self._connect_host(dut_id=dut_id)
+
+ @parallel_lock(num=8)
+ def _connect_host(self, dut_id=0):
+ """
+ Create connection to assigned crb, parameter dut_id will be used in
+ parallel_lock thus can assure isolated locks for each crb.
+ Parallel ssh connections are limited to MaxStartups option in SSHD
+ configuration file. By default concurrent number is 10, so default
+ threads number is limited to 8 which less than 10. Lock number can
+ be modified along with MaxStartups value.
+ """
+ retry_times = 10
+ try:
+ if ":" in self.host:
+ while retry_times:
+ self.ip = self.host.split(":")[0]
+ self.port = int(self.host.split(":")[1])
+ self.session = pxssh.pxssh(encoding="utf-8")
+ try:
+ self.session.login(
+ self.ip,
+ self.username,
+ self.password,
+ original_prompt="[$#>]",
+ port=self.port,
+ login_timeout=20,
+ password_regex=r"(?i)(?:password:)|(?:passphrase for key)|(?i)(password for .+:)",
+ )
+ except Exception as e:
+ print(e)
+ time.sleep(2)
+ retry_times -= 1
+ print("retry %d times connecting..." % (10 - retry_times))
+ else:
+ break
+ else:
+ raise Exception("connect to %s:%s failed" % (self.ip, self.port))
+ else:
+ self.session = pxssh.pxssh(encoding="utf-8")
+ self.session.login(
+ self.host,
+ self.username,
+ self.password,
+ original_prompt="[$#>]",
+ password_regex=r"(?i)(?:password:)|(?:passphrase for key)|(?i)(password for .+:)",
+ )
+ self.send_expect("stty -echo", "#")
+ self.send_expect("stty columns 1000", "#")
+ except Exception as e:
+ print(RED(e))
+ if getattr(self, "port", None):
+ suggestion = (
+ "\nSuggession: Check if the firewall on [ %s ] " % self.ip
+ + "is stopped\n"
+ )
+ print(GREEN(suggestion))
+
+ raise SSHConnectionException(self.host)
+
+ def init_log(self, logger, name):
+ self.logger = logger
+ self.logger.info("ssh %s@%s" % (self.username, self.host))
+
+ def send_expect_base(self, command, expected, timeout):
+ ignore_keyintr()
+ self.clean_session()
+ self.session.PROMPT = expected
+ self.__sendline(command)
+ self.__prompt(command, timeout)
+ aware_keyintr()
+
+ before = self.get_output_before()
+ return before
+
+ def send_expect(self, command, expected, timeout=15, verify=False):
+
+ try:
+ ret = self.send_expect_base(command, expected, timeout)
+ if verify:
+ ret_status = self.send_expect_base("echo $?", expected, timeout)
+ if not int(ret_status):
+ return ret
+ else:
+ self.logger.error("Command: %s failure!" % command)
+ self.logger.error(ret)
+ return int(ret_status)
+ else:
+ return ret
+ except Exception as e:
+ print(
+ RED(
+ "Exception happened in [%s] and output is [%s]"
+ % (command, self.get_output_before())
+ )
+ )
+ raise (e)
+
+ def send_command(self, command, timeout=1):
+ try:
+ ignore_keyintr()
+ self.clean_session()
+ self.__sendline(command)
+ aware_keyintr()
+ except Exception as e:
+ raise (e)
+
+ output = self.get_session_before(timeout=timeout)
+ self.session.PROMPT = self.session.UNIQUE_PROMPT
+ self.session.prompt(0.1)
+
+ return output
+
+ def clean_session(self):
+ self.get_session_before(timeout=0.01)
+
+ def get_session_before(self, timeout=15):
+ """
+ Get all output before timeout
+ """
+ ignore_keyintr()
+ self.session.PROMPT = self.magic_prompt
+ try:
+ self.session.prompt(timeout)
+ except Exception as e:
+ pass
+
+ aware_keyintr()
+ before = self.get_output_all()
+ self.__flush()
+
+ return before
+
+ def __flush(self):
+ """
+ Clear all session buffer
+ """
+ self.session.buffer = ""
+ self.session.before = ""
+
+ def __prompt(self, command, timeout):
+ if not self.session.prompt(timeout):
+ raise TimeoutException(command, self.get_output_all()) from None
+
+ def __sendline(self, command):
+ if not self.isalive():
+ raise SSHSessionDeadException(self.host)
+ if len(command) == 2 and command.startswith("^"):
+ self.session.sendcontrol(command[1])
+ else:
+ self.session.sendline(command)
+
+ def get_output_before(self):
+ if not self.isalive():
+ raise SSHSessionDeadException(self.host)
+ before = self.session.before.rsplit("\r\n", 1)
+ if before[0] == "[PEXPECT]":
+ before[0] = ""
+
+ return before[0]
+
+ def get_output_all(self):
+ output = self.session.before
+ output.replace("[PEXPECT]", "")
+ return output
+
+ def close(self, force=False):
+ if force is True:
+ self.session.close()
+ else:
+ if self.isalive():
+ self.session.logout()
+
+ def isalive(self):
+ return self.session.isalive()
+
+ def copy_file_from(self, src, dst=".", password="", crb_session=None):
+ """
+ Copies a file from a remote place into local.
+ """
+ command = "scp -v {0}@{1}:{2} {3}".format(self.username, self.host, src, dst)
+ if ":" in self.host:
+ command = "scp -v -P {0} -o NoHostAuthenticationForLocalhost=yes {1}@{2}:{3} {4}".format(
+ str(self.port), self.username, self.ip, src, dst
+ )
+ if password == "":
+ self._spawn_scp(command, self.password, crb_session)
+ else:
+ self._spawn_scp(command, password, crb_session)
+
+ def copy_file_to(self, src, dst="~/", password="", crb_session=None):
+ """
+ Sends a local file to a remote place.
+ """
+ command = "scp {0} {1}@{2}:{3}".format(src, self.username, self.host, dst)
+ if ":" in self.host:
+ command = "scp -v -P {0} -o NoHostAuthenticationForLocalhost=yes {1} {2}@{3}:{4}".format(
+ str(self.port), src, self.username, self.ip, dst
+ )
+ else:
+ command = "scp -v {0} {1}@{2}:{3}".format(
+ src, self.username, self.host, dst
+ )
+ if password == "":
+ self._spawn_scp(command, self.password, crb_session)
+ else:
+ self._spawn_scp(command, password, crb_session)
+
+ def _spawn_scp(self, scp_cmd, password, crb_session):
+ """
+ Transfer a file with SCP
+ """
+ self.logger.info(scp_cmd)
+ # if crb_session is not None, copy file from/to crb env
+ # if crb_session is None, copy file from/to current dts env
+ if crb_session is not None:
+ crb_session.session.clean_session()
+ crb_session.session.__sendline(scp_cmd)
+ p = crb_session.session.session
+ else:
+ p = pexpect.spawn(scp_cmd)
+ time.sleep(0.5)
+ ssh_newkey = "Are you sure you want to continue connecting"
+ i = p.expect(
+ [ssh_newkey, "[pP]assword", "# ", pexpect.EOF, pexpect.TIMEOUT], 120
+ )
+ if i == 0: # add once in trust list
+ p.sendline("yes")
+ i = p.expect([ssh_newkey, "[pP]assword", pexpect.EOF], 2)
+
+ if i == 1:
+ time.sleep(0.5)
+ p.sendline(password)
+ p.expect("Exit status 0", 60)
+ if i == 4:
+ self.logger.error("SCP TIMEOUT error %d" % i)
+ if crb_session is None:
+ p.close()
--
2.20.1
next prev parent reply other threads:[~2022-04-06 15:06 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-04-06 15:04 [RFC PATCH v1 00/18] merge DTS component files " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 01/18] dts: merge DTS framework/crb.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 02/18] dts: merge DTS framework/dut.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 03/18] dts: merge DTS framework/ixia_buffer_parser.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 04/18] dts: merge DTS framework/pktgen.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 05/18] dts: merge DTS framework/pktgen_base.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 06/18] dts: merge DTS framework/pktgen_ixia.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 07/18] dts: merge DTS framework/pktgen_ixia_network.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 08/18] dts: merge DTS framework/pktgen_trex.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 09/18] dts: merge DTS framework/ssh_connection.py " Juraj Linkeš
2022-04-06 15:04 ` Juraj Linkeš [this message]
2022-04-06 15:04 ` [RFC PATCH v1 11/18] dts: merge DTS framework/tester.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 12/18] dts: merge DTS framework/ixia_network/__init__.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 13/18] dts: merge DTS framework/ixia_network/ixnet.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 14/18] dts: merge DTS framework/ixia_network/ixnet_config.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 15/18] dts: merge DTS framework/ixia_network/ixnet_stream.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 16/18] dts: merge DTS framework/ixia_network/packet_parser.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 17/18] dts: merge DTS nics/__init__.py " Juraj Linkeš
2022-04-06 15:04 ` [RFC PATCH v1 18/18] dts: merge DTS nics/net_device.py " Juraj Linkeš
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220406150440.2914464-11-juraj.linkes@pantheon.tech \
--to=juraj.linkes@pantheon.tech \
--cc=Honnappa.Nagarahalli@arm.com \
--cc=david.marchand@redhat.com \
--cc=dev@dpdk.org \
--cc=lijuan.tu@intel.com \
--cc=ohilyard@iol.unh.edu \
--cc=thomas@monjalon.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).