Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/113246/?format=api
http://patches.dpdk.org/api/patches/113246/?format=api", "web_url": "http://patches.dpdk.org/project/dpdk/patch/20220622121448.3304251-2-juraj.linkes@pantheon.tech/", "project": { "id": 1, "url": "http://patches.dpdk.org/api/projects/1/?format=api", "name": "DPDK", "link_name": "dpdk", "list_id": "dev.dpdk.org", "list_email": "dev@dpdk.org", "web_url": "http://core.dpdk.org", "scm_url": "git://dpdk.org/dpdk", "webscm_url": "http://git.dpdk.org/dpdk", "list_archive_url": "https://inbox.dpdk.org/dev", "list_archive_url_format": "https://inbox.dpdk.org/dev/{}", "commit_url_format": "" }, "msgid": "<20220622121448.3304251-2-juraj.linkes@pantheon.tech>", "list_archive_url": "https://inbox.dpdk.org/dev/20220622121448.3304251-2-juraj.linkes@pantheon.tech", "date": "2022-06-22T12:14:41", "name": "[v1,1/8] dts: add ssh pexpect library", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": true, "hash": "0ec3bf8a1465a540ad3cf094c70eb416ca525a1b", "submitter": { "id": 1626, "url": "http://patches.dpdk.org/api/people/1626/?format=api", "name": "Juraj Linkeš", "email": "juraj.linkes@pantheon.tech" }, "delegate": { "id": 1, "url": "http://patches.dpdk.org/api/users/1/?format=api", "username": "tmonjalo", "first_name": "Thomas", "last_name": "Monjalon", "email": "thomas@monjalon.net" }, "mbox": "http://patches.dpdk.org/project/dpdk/patch/20220622121448.3304251-2-juraj.linkes@pantheon.tech/mbox/", "series": [ { "id": 23695, "url": "http://patches.dpdk.org/api/series/23695/?format=api", "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=23695", "date": "2022-06-22T12:14:40", "name": "dts: ssh connection to a node", "version": 1, "mbox": "http://patches.dpdk.org/series/23695/mbox/" } ], "comments": "http://patches.dpdk.org/api/patches/113246/comments/", "check": "success", "checks": "http://patches.dpdk.org/api/patches/113246/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<dev-bounces@dpdk.org>", "X-Original-To": "patchwork@inbox.dpdk.org", "Delivered-To": "patchwork@inbox.dpdk.org", "Received": [ "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 136CBA04FD;\n\tWed, 22 Jun 2022 14:15:08 +0200 (CEST)", "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 6FCB34281C;\n\tWed, 22 Jun 2022 14:14:56 +0200 (CEST)", "from lb.pantheon.sk (lb.pantheon.sk [46.229.239.20])\n by mails.dpdk.org (Postfix) with ESMTP id 063F640DDB\n for <dev@dpdk.org>; Wed, 22 Jun 2022 14:14:55 +0200 (CEST)", "from localhost (localhost [127.0.0.1])\n by lb.pantheon.sk (Postfix) with ESMTP id CB4AE86AAF;\n Wed, 22 Jun 2022 14:14:53 +0200 (CEST)", "from lb.pantheon.sk ([127.0.0.1])\n by localhost (lb.pantheon.sk [127.0.0.1]) (amavisd-new, port 10024)\n with ESMTP id ztH70gwXwrqb; Wed, 22 Jun 2022 14:14:49 +0200 (CEST)", "from entguard.lab.pantheon.local (unknown [46.229.239.141])\n by lb.pantheon.sk (Postfix) with ESMTP id 46C6C31469;\n Wed, 22 Jun 2022 14:14:49 +0200 (CEST)" ], "X-Virus-Scanned": "amavisd-new at siecit.sk", "From": "=?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>", "To": "thomas@monjalon.net, david.marchand@redhat.com, jerinjacobk@gmail.com,\n ronan.randles@intel.com, Honnappa.Nagarahalli@arm.com,\n ohilyard@iol.unh.edu, lijuan.tu@intel.com", "Cc": "dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>", "Subject": "[PATCH v1 1/8] dts: add ssh pexpect library", "Date": "Wed, 22 Jun 2022 12:14:41 +0000", "Message-Id": "<20220622121448.3304251-2-juraj.linkes@pantheon.tech>", "X-Mailer": "git-send-email 2.25.1", "In-Reply-To": "<20220622121448.3304251-1-juraj.linkes@pantheon.tech>", "References": "<20220622121448.3304251-1-juraj.linkes@pantheon.tech>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=UTF-8", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "dev@dpdk.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "DPDK patches and discussions <dev.dpdk.org>", "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>", "List-Archive": "<http://mails.dpdk.org/archives/dev/>", "List-Post": "<mailto:dev@dpdk.org>", "List-Help": "<mailto:dev-request@dpdk.org?subject=help>", "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>", "Errors-To": "dev-bounces@dpdk.org" }, "content": "The library uses the pexpect python library and implements connection to\na node and two ways to interact with the node:\n1. Send a string with specified prompt which will be matched after\n the string has been sent to the node.\n2. Send a command to be executed. No prompt is specified here.\n\nSigned-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>\n---\n dts/framework/exception.py | 48 +++++++++\n dts/framework/ssh_pexpect.py | 185 +++++++++++++++++++++++++++++++++++\n dts/framework/utils.py | 11 +++\n 3 files changed, 244 insertions(+)\n create mode 100644 dts/framework/exception.py\n create mode 100644 dts/framework/ssh_pexpect.py\n create mode 100644 dts/framework/utils.py", "diff": "diff --git a/dts/framework/exception.py b/dts/framework/exception.py\nnew file mode 100644\nindex 0000000000..a109dd1fb8\n--- /dev/null\n+++ b/dts/framework/exception.py\n@@ -0,0 +1,48 @@\n+\"\"\"\n+User-defined exceptions used across the framework.\n+\"\"\"\n+\n+\n+class TimeoutException(Exception):\n+\n+ \"\"\"\n+ Command execution timeout.\n+ \"\"\"\n+\n+ def __init__(self, command, output):\n+ self.command = command\n+ self.output = output\n+\n+ def __str__(self):\n+ msg = \"TIMEOUT on %s\" % (self.command)\n+ return msg\n+\n+ def get_output(self):\n+ return self.output\n+\n+\n+class SSHConnectionException(Exception):\n+\n+ \"\"\"\n+ SSH connection error.\n+ \"\"\"\n+\n+ def __init__(self, host):\n+ self.host = host\n+\n+ def __str__(self):\n+ return \"Error trying to connect with %s\" % self.host\n+\n+\n+class SSHSessionDeadException(Exception):\n+\n+ \"\"\"\n+ SSH session is not alive.\n+ It can no longer be used.\n+ \"\"\"\n+\n+ def __init__(self, host):\n+ self.host = host\n+\n+ def __str__(self):\n+ return \"SSH session with %s has been dead\" % self.host\ndiff --git a/dts/framework/ssh_pexpect.py b/dts/framework/ssh_pexpect.py\nnew file mode 100644\nindex 0000000000..bccc6fae94\n--- /dev/null\n+++ b/dts/framework/ssh_pexpect.py\n@@ -0,0 +1,185 @@\n+import time\n+\n+from pexpect import pxssh\n+\n+from .exception import SSHConnectionException, SSHSessionDeadException, TimeoutException\n+from .utils import GREEN, RED\n+\n+\"\"\"\n+Module handles ssh sessions to TG and SUT.\n+Implements send_expect function to send commands and get output data.\n+\"\"\"\n+\n+\n+class SSHPexpect:\n+ def __init__(self, node, username, password):\n+ self.magic_prompt = \"MAGIC PROMPT\"\n+ self.logger = None\n+\n+ self.node = node\n+ self.username = username\n+ self.password = password\n+\n+ self._connect_host()\n+\n+ def _connect_host(self):\n+ \"\"\"\n+ Create connection to assigned node.\n+ \"\"\"\n+ retry_times = 10\n+ try:\n+ if \":\" in self.node:\n+ while retry_times:\n+ self.ip = self.node.split(\":\")[0]\n+ self.port = int(self.node.split(\":\")[1])\n+ self.session = pxssh.pxssh(encoding=\"utf-8\")\n+ try:\n+ self.session.login(\n+ self.ip,\n+ self.username,\n+ self.password,\n+ original_prompt=\"[$#>]\",\n+ port=self.port,\n+ login_timeout=20,\n+ password_regex=r\"(?i)(?:password:)|(?:passphrase for key)|(?i)(password for .+:)\",\n+ )\n+ except Exception as e:\n+ print(e)\n+ time.sleep(2)\n+ retry_times -= 1\n+ print(\"retry %d times connecting...\" % (10 - retry_times))\n+ else:\n+ break\n+ else:\n+ raise Exception(\"connect to %s:%s failed\" % (self.ip, self.port))\n+ else:\n+ self.session = pxssh.pxssh(encoding=\"utf-8\")\n+ self.session.login(\n+ self.node,\n+ self.username,\n+ self.password,\n+ original_prompt=\"[$#>]\",\n+ password_regex=r\"(?i)(?:password:)|(?:passphrase for key)|(?i)(password for .+:)\",\n+ )\n+ self.send_expect(\"stty -echo\", \"#\")\n+ self.send_expect(\"stty columns 1000\", \"#\")\n+ except Exception as e:\n+ print(RED(e))\n+ if getattr(self, \"port\", None):\n+ suggestion = (\n+ \"\\nSuggession: Check if the firewall on [ %s ] \" % self.ip\n+ + \"is stopped\\n\"\n+ )\n+ print(GREEN(suggestion))\n+\n+ raise SSHConnectionException(self.node)\n+\n+ def init_log(self, logger):\n+ self.logger = logger\n+ self.logger.info(\"ssh %s@%s\" % (self.username, self.node))\n+\n+ def send_expect_base(self, command, expected, timeout):\n+ self.clean_session()\n+ self.session.PROMPT = expected\n+ self.__sendline(command)\n+ self.__prompt(command, timeout)\n+\n+ before = self.get_output_before()\n+ return before\n+\n+ def send_expect(self, command, expected, timeout=15, verify=False):\n+\n+ try:\n+ ret = self.send_expect_base(command, expected, timeout)\n+ if verify:\n+ ret_status = self.send_expect_base(\"echo $?\", expected, timeout)\n+ if not int(ret_status):\n+ return ret\n+ else:\n+ self.logger.error(\"Command: %s failure!\" % command)\n+ self.logger.error(ret)\n+ return int(ret_status)\n+ else:\n+ return ret\n+ except Exception as e:\n+ print(\n+ RED(\n+ \"Exception happened in [%s] and output is [%s]\"\n+ % (command, self.get_output_before())\n+ )\n+ )\n+ raise e\n+\n+ def send_command(self, command, timeout=1):\n+ try:\n+ self.clean_session()\n+ self.__sendline(command)\n+ except Exception as e:\n+ raise e\n+\n+ output = self.get_session_before(timeout=timeout)\n+ self.session.PROMPT = self.session.UNIQUE_PROMPT\n+ self.session.prompt(0.1)\n+\n+ return output\n+\n+ def clean_session(self):\n+ self.get_session_before(timeout=0.01)\n+\n+ def get_session_before(self, timeout=15):\n+ \"\"\"\n+ Get all output before timeout\n+ \"\"\"\n+ self.session.PROMPT = self.magic_prompt\n+ try:\n+ self.session.prompt(timeout)\n+ except Exception as e:\n+ pass\n+\n+ before = self.get_output_all()\n+ self.__flush()\n+\n+ return before\n+\n+ def __flush(self):\n+ \"\"\"\n+ Clear all session buffer\n+ \"\"\"\n+ self.session.buffer = \"\"\n+ self.session.before = \"\"\n+\n+ def __prompt(self, command, timeout):\n+ if not self.session.prompt(timeout):\n+ raise TimeoutException(command, self.get_output_all()) from None\n+\n+ def __sendline(self, command):\n+ if not self.isalive():\n+ raise SSHSessionDeadException(self.node)\n+ if len(command) == 2 and command.startswith(\"^\"):\n+ self.session.sendcontrol(command[1])\n+ else:\n+ self.session.sendline(command)\n+\n+ def get_output_before(self):\n+ if not self.isalive():\n+ raise SSHSessionDeadException(self.node)\n+ before = self.session.before.rsplit(\"\\r\\n\", 1)\n+ if before[0] == \"[PEXPECT]\":\n+ before[0] = \"\"\n+\n+ return before[0]\n+\n+ def get_output_all(self):\n+ output = self.session.before\n+ output.replace(\"[PEXPECT]\", \"\")\n+ return output\n+\n+ def close(self, force=False):\n+ if force is True:\n+ self.session.close()\n+ else:\n+ if self.isalive():\n+ self.session.logout()\n+\n+ def isalive(self):\n+ return self.session.isalive()\ndiff --git a/dts/framework/utils.py b/dts/framework/utils.py\nnew file mode 100644\nindex 0000000000..0ffd992952\n--- /dev/null\n+++ b/dts/framework/utils.py\n@@ -0,0 +1,11 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(c) 2010-2014 Intel Corporation\n+#\n+\n+\n+def RED(text):\n+ return \"\\x1B[\" + \"31;1m\" + str(text) + \"\\x1B[\" + \"0m\"\n+\n+\n+def GREEN(text):\n+ return \"\\x1B[\" + \"32;1m\" + str(text) + \"\\x1B[\" + \"0m\"\n", "prefixes": [ "v1", "1/8" ] }{ "id": 113246, "url": "