From patchwork Fri Nov 4 11:05:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 119479 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 37A8BA00C5; Fri, 4 Nov 2022 12:05:37 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 6083742D1A; Fri, 4 Nov 2022 12:05:33 +0100 (CET) Received: from lb.pantheon.sk (lb.pantheon.sk [46.229.239.20]) by mails.dpdk.org (Postfix) with ESMTP id CEA0A42D10 for ; Fri, 4 Nov 2022 12:05:31 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by lb.pantheon.sk (Postfix) with ESMTP id 226E51BA5B6; Fri, 4 Nov 2022 12:05:29 +0100 (CET) X-Virus-Scanned: amavisd-new at siecit.sk Received: from lb.pantheon.sk ([127.0.0.1]) by localhost (lb.pantheon.sk [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id KG7ZTI3Iekoo; Fri, 4 Nov 2022 12:05:26 +0100 (CET) Received: from entguard.lab.pantheon.local (unknown [46.229.239.141]) by lb.pantheon.sk (Postfix) with ESMTP id 7D6551BA5A5; Fri, 4 Nov 2022 12:05:26 +0100 (CET) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, ohilyard@iol.unh.edu, lijuan.tu@intel.com, kda@semihalf.com, bruce.richardson@intel.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v8 1/9] dts: add project tools config Date: Fri, 4 Nov 2022 11:05:15 +0000 Message-Id: <20221104110523.511367-2-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20221104110523.511367-1-juraj.linkes@pantheon.tech> References: <20221103151934.450887-1-juraj.linkes@pantheon.tech> <20221104110523.511367-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Add configuration for Python tools used in DTS: Poetry, dependency and package manager Black, formatter Pylama, static analysis Isort, import sorting Signed-off-by: Owen Hilyard Signed-off-by: Juraj Linkeš --- MAINTAINERS | 5 + doc/guides/tools/dts.rst | 56 ++++++ doc/guides/tools/index.rst | 1 + dts/poetry.lock | 337 +++++++++++++++++++++++++++++++++++++ dts/pyproject.toml | 46 +++++ 5 files changed, 445 insertions(+) create mode 100644 doc/guides/tools/dts.rst create mode 100644 dts/poetry.lock create mode 100644 dts/pyproject.toml diff --git a/MAINTAINERS b/MAINTAINERS index 51d77460ec..e018e396d0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -114,6 +114,11 @@ F: buildtools/symlink-drivers-solibs.py F: devtools/test-meson-builds.sh F: devtools/check-meson.py +DTS +M: Lijuan Tu +M: Owen Hilyard +F: dts/ + Public CI M: Aaron Conole M: Michael Santana diff --git a/doc/guides/tools/dts.rst b/doc/guides/tools/dts.rst new file mode 100644 index 0000000000..0a88901119 --- /dev/null +++ b/doc/guides/tools/dts.rst @@ -0,0 +1,56 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2022 PANTHEON.tech s.r.o. + +DPDK Test Suite +=============== + +The DPDK Test Suite, abbreviated DTS, is a Python test framework with test suites +implementing functional and performance tests used to test DPDK. + + +DTS Environment +--------------- + +DTS is written entirely in Python using a variety of dependencies. +DTS uses Poetry as its Python dependency management. +Python build/development and runtime environments are the same and DTS development environment, +DTS runtime environment or just plain DTS environment are used interchangeably. + + +Setting up DTS environment +-------------------------- + +#. **Python Version** + + The Python Version required by DTS is specified in ``dts/pyproject.toml`` in the + **[tool.poetry.dependencies]** section: + + .. literalinclude:: ../../../dts/pyproject.toml + :language: cfg + :start-at: [tool.poetry.dependencies] + :end-at: python + + The Python dependency manager DTS uses, Poetry, doesn't install Python, so you may need + to satisfy this requirement by other means if your Python is not up-to-date. + A tool such as `Pyenv `_ is a good way to get Python, + though not the only one. + +#. **Poetry** + + The typical style of python dependency management, pip with ``requirements.txt``, + has a few issues. + The advantages of Poetry include specifying what Python version is required and forcing you + to specify versions, enforced by a lockfile, both of which help prevent broken dependencies. + Another benefit is the usage of ``pyproject.toml``, which has become the standard config file + for python projects, improving project organization. + To install Poetry, visit their `doc pages `_. + +#. **Getting a Poetry shell** + + Once you have Poetry along with the proper Python version all set up, it's just a matter + of installing dependencies via Poetry and using the virtual environment Poetry provides: + + .. code-block:: console + + poetry install + poetry shell diff --git a/doc/guides/tools/index.rst b/doc/guides/tools/index.rst index 0e5041a3f0..f21ef0aac9 100644 --- a/doc/guides/tools/index.rst +++ b/doc/guides/tools/index.rst @@ -20,3 +20,4 @@ DPDK Tools User Guides comp_perf testeventdev testregex + dts diff --git a/dts/poetry.lock b/dts/poetry.lock new file mode 100644 index 0000000000..0b2a007d4d --- /dev/null +++ b/dts/poetry.lock @@ -0,0 +1,337 @@ +[[package]] +name = "attrs" +version = "22.1.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] + +[[package]] +name = "black" +version = "22.10.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" + +[[package]] +name = "isort" +version = "5.10.1" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.6.1,<4.0" + +[package.extras] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +requirements_deprecated_finder = ["pipreqs", "pip-api"] +colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] + +[[package]] +name = "jsonpatch" +version = "1.32" +description = "Apply JSON-Patches (RFC 6902)" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +jsonpointer = ">=1.9" + +[[package]] +name = "jsonpointer" +version = "2.3" +description = "Identify specific nodes in a JSON document (RFC 6901)" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "jsonschema" +version = "4.17.0" +description = "An implementation of JSON Schema validation for Python" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "mypy" +version = "0.961" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "pathspec" +version = "0.10.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "pexpect" +version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "platformdirs" +version = "2.5.2" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] +test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pycodestyle" +version = "2.9.1" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "pydocstyle" +version = "6.1.1" +description = "Python docstring style checker" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +snowballstemmer = "*" + +[package.extras] +toml = ["toml"] + +[[package]] +name = "pyflakes" +version = "2.5.0" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "pylama" +version = "8.4.1" +description = "Code audit tool for python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +mccabe = ">=0.7.0" +pycodestyle = ">=2.9.1" +pydocstyle = ">=6.1.1" +pyflakes = ">=2.5.0" + +[package.extras] +all = ["pylint", "eradicate", "radon", "mypy", "vulture"] +eradicate = ["eradicate"] +mypy = ["mypy"] +pylint = ["pylint"] +radon = ["radon"] +tests = ["pytest (>=7.1.2)", "pytest-mypy", "eradicate (>=2.0.0)", "radon (>=5.1.0)", "mypy", "pylint (>=2.11.1)", "pylama-quotes", "toml", "vulture", "types-setuptools", "types-toml"] +toml = ["toml (>=0.10.2)"] +vulture = ["vulture"] + +[[package]] +name = "pyrsistent" +version = "0.19.1" +description = "Persistent/Functional/Immutable data structures" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "types-pyyaml" +version = "6.0.12.1" +description = "Typing stubs for PyYAML" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "typing-extensions" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "warlock" +version = "2.0.1" +description = "Python object model built on JSON schema and JSON patch." +category = "main" +optional = false +python-versions = ">=3.7,<4.0" + +[package.dependencies] +jsonpatch = ">=1,<2" +jsonschema = ">=4,<5" + +[metadata] +lock-version = "1.1" +python-versions = "^3.10" +content-hash = "a0f040b07fc6ce4deb0be078b9a88c2a465cb6bccb9e260a67e92c2403e2319f" + +[metadata.files] +attrs = [] +black = [] +click = [] +colorama = [] +isort = [] +jsonpatch = [] +jsonpointer = [] +jsonschema = [] +mccabe = [] +mypy = [] +mypy-extensions = [] +pathspec = [] +pexpect = [ + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] +platformdirs = [ + {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, + {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, +] +ptyprocess = [] +pycodestyle = [] +pydocstyle = [] +pyflakes = [] +pylama = [] +pyrsistent = [] +pyyaml = [] +snowballstemmer = [] +toml = [] +tomli = [] +types-pyyaml = [] +typing-extensions = [] +warlock = [] diff --git a/dts/pyproject.toml b/dts/pyproject.toml new file mode 100644 index 0000000000..a136c91e5e --- /dev/null +++ b/dts/pyproject.toml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2022 University of New Hampshire + +[tool.poetry] +name = "dts" +version = "0.1.0" +description = "" +authors = ["Owen Hilyard ", "dts@dpdk.org"] + +[tool.poetry.dependencies] +python = "^3.10" +pexpect = "^4.8.0" +warlock = "^2.0.1" +PyYAML = "^6.0" +types-PyYAML = "^6.0.8" + +[tool.poetry.dev-dependencies] +mypy = "^0.961" +black = "^22.6.0" +isort = "^5.10.1" +pylama = "^8.4.1" +pyflakes = "2.5.0" +toml = "^0.10.2" + +[tool.poetry.scripts] +dts = "main:main" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.pylama] +linters = "pep8,pylint,mccabe,mypy,pycodestyle,pyflakes" +format = "pylint" +max_line_length = 88 # https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#line-length + +[tool.mypy] +python_version = "3.10" + +[tool.isort] +profile = "black" + +[tool.black] +target-version = ['py310'] +include = '\.pyi?$' +line-length = 88 # https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#line-length From patchwork Fri Nov 4 11:05:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 119481 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 6450DA00C5; Fri, 4 Nov 2022 12:05:49 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 1B21842D2A; Fri, 4 Nov 2022 12:05:37 +0100 (CET) Received: from lb.pantheon.sk (lb.pantheon.sk [46.229.239.20]) by mails.dpdk.org (Postfix) with ESMTP id 7209A42D23 for ; Fri, 4 Nov 2022 12:05:34 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by lb.pantheon.sk (Postfix) with ESMTP id 86F3E1BA5A5; Fri, 4 Nov 2022 12:05:33 +0100 (CET) X-Virus-Scanned: amavisd-new at siecit.sk Received: from lb.pantheon.sk ([127.0.0.1]) by localhost (lb.pantheon.sk [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id IFZf3jO2sjbV; Fri, 4 Nov 2022 12:05:31 +0100 (CET) Received: from entguard.lab.pantheon.local (unknown [46.229.239.141]) by lb.pantheon.sk (Postfix) with ESMTP id 0170C1BA5A8; Fri, 4 Nov 2022 12:05:26 +0100 (CET) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, ohilyard@iol.unh.edu, lijuan.tu@intel.com, kda@semihalf.com, bruce.richardson@intel.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v8 2/9] dts: add developer tools Date: Fri, 4 Nov 2022 11:05:16 +0000 Message-Id: <20221104110523.511367-3-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20221104110523.511367-1-juraj.linkes@pantheon.tech> References: <20221103151934.450887-1-juraj.linkes@pantheon.tech> <20221104110523.511367-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org The devtools that check Python code are Black and Isort to format the code and Pylama to do static analysis. Signed-off-by: Owen Hilyard Signed-off-by: Juraj Linkeš --- devtools/dts-check-format.sh | 87 ++++++++++++++++++++++++++++++++++++ doc/guides/tools/dts.rst | 67 +++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100755 devtools/dts-check-format.sh diff --git a/devtools/dts-check-format.sh b/devtools/dts-check-format.sh new file mode 100755 index 0000000000..5a1e495634 --- /dev/null +++ b/devtools/dts-check-format.sh @@ -0,0 +1,87 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2022 University of New Hampshire + +usage() { + echo "Run formatting and linting programs for DTS. Usage:" + + # Get source code comments after getopts arguments and print them both + grep -E '[a-zA-Z]+\) +#' "$0" | tr -d '#' + exit 0 +} + +format=true +lint=true + +# Comments after args serve as documentation; must be present +while getopts "hfl" arg; do + case $arg in + h) # Display this message + usage + ;; + f) # Don't run formatters + format=false + ;; + l) # Don't run linter + lint=false + ;; + *) + esac +done + + +errors=0 + +if $format; then + if command -v git > /dev/null; then + if git rev-parse --is-inside-work-tree >&-; then + echo "Formatting:" + if command -v black > /dev/null; then + echo "Formatting code with black:" + black . + else + echo "black is not installed, not formatting" + errors=$((errors + 1)) + fi + if command -v isort > /dev/null; then + echo "Sorting imports with isort:" + isort . + else + echo "isort is not installed, not sorting imports" + errors=$((errors + 1)) + fi + + git update-index --refresh + retval=$? + if [ $retval -ne 0 ]; then + echo 'The "needs update" files have been reformatted.' + echo 'Please update your commit.' + fi + errors=$((errors + retval)) + else + echo ".git directory not found, not formatting code" + errors=$((errors + 1)) + fi + else + echo "git command not found, not formatting code" + errors=$((errors + 1)) + fi +fi + +if $lint; then + if $format; then + echo + fi + echo "Linting:" + if command -v pylama > /dev/null; then + pylama . + errors=$((errors + $?)) + else + echo "pylama not found, unable to run linter" + errors=$((errors + 1)) + fi +fi + +echo +echo "Found $errors errors" +exit $errors diff --git a/doc/guides/tools/dts.rst b/doc/guides/tools/dts.rst index 0a88901119..5f67391145 100644 --- a/doc/guides/tools/dts.rst +++ b/doc/guides/tools/dts.rst @@ -8,6 +8,44 @@ The DPDK Test Suite, abbreviated DTS, is a Python test framework with test suite implementing functional and performance tests used to test DPDK. +DTS Terminology +--------------- + +DTS node + A generic description of any host/server DTS connects to. + +DTS runtime environment + An environment containing Python with packages needed to run DTS. + +DTS runtime environment node + A node where at least one DTS runtime environment is present. + This is the node where we run DTS and from which DTS connects to other nodes. + +System under test + An SUT is the combination of DPDK and the hardware we're testing + in conjunction with DPDK (NICs, crypto and other devices). + +System under test node + A node where at least one SUT is present. + +Traffic generator + A TG is either software or hardware capable of sending packets. + +Traffic generator node + A node where at least one TG is present. + In case of hardware traffic generators, the TG and the node are literally the same. + + +In most cases, interchangeably referring to a runtime environment, SUT, TG or the node +they're running on (e.g. using SUT and SUT node interchangeably) doesn't cause confusion. +There could theoretically be more than of these running on the same node and in that case +it's useful to have stricter definitions. +An example would be two different traffic generators (such as Trex and Scapy) +running on the same node. +A different example would be a node containing both a DTS runtime environment +and a traffic generator, in which case it's both a DTS runtime environment node and a TG node. + + DTS Environment --------------- @@ -54,3 +92,32 @@ Setting up DTS environment poetry install poetry shell + + +DTS Developer Tools +------------------- + +There are three tools used in DTS to help with code checking, style and formatting: + +* `isort `_ + + Alphabetically sorts python imports within blocks. + +* `black `_ + + Does most of the actual formatting (whitespaces, comments, line length etc.) + and works similarly to clang-format. + +* `pylama `_ + + Runs a collection of python linters and aggregates output. + It will run these tools over the repository: + + .. literalinclude:: ../../../dts/pyproject.toml + :language: cfg + :start-after: [tool.pylama] + :end-at: linters + +These three tools are all used in ``devtools/dts-check-format.sh``, +the DTS code check and format script. +Refer to the script for usage: ``devtools/dts-check-format.sh -h`` From patchwork Fri Nov 4 11:05:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 119480 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 794EFA00C5; Fri, 4 Nov 2022 12:05:43 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 38CE442D27; Fri, 4 Nov 2022 12:05:36 +0100 (CET) Received: from lb.pantheon.sk (lb.pantheon.sk [46.229.239.20]) by mails.dpdk.org (Postfix) with ESMTP id 162AA42D23 for ; Fri, 4 Nov 2022 12:05:34 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by lb.pantheon.sk (Postfix) with ESMTP id 1FE381B68D8; Fri, 4 Nov 2022 12:05:33 +0100 (CET) X-Virus-Scanned: amavisd-new at siecit.sk Received: from lb.pantheon.sk ([127.0.0.1]) by localhost (lb.pantheon.sk [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id U5kweRVLMsdC; Fri, 4 Nov 2022 12:05:31 +0100 (CET) Received: from entguard.lab.pantheon.local (unknown [46.229.239.141]) by lb.pantheon.sk (Postfix) with ESMTP id 7115C1BA5A9; Fri, 4 Nov 2022 12:05:27 +0100 (CET) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, ohilyard@iol.unh.edu, lijuan.tu@intel.com, kda@semihalf.com, bruce.richardson@intel.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v8 3/9] dts: add config parser module Date: Fri, 4 Nov 2022 11:05:17 +0000 Message-Id: <20221104110523.511367-4-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20221104110523.511367-1-juraj.linkes@pantheon.tech> References: <20221103151934.450887-1-juraj.linkes@pantheon.tech> <20221104110523.511367-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org From: Owen Hilyard The configuration is split into two parts, one defining the parameters of the test run and the other defining the topology to be used. The format of the configuration is YAML. It is validated according to a json schema which also server as detailed documentation of the various configuration fields. This means that the complete set of allowed values are tied to the schema as a source of truth. This enables making changes to parts of DTS that interface with config files without a high risk of breaking someone's configuration. This configuration system uses immutable objects to represent the configuration, making IDE/LSP autocomplete work properly. There are two ways to specify the configuration file path, an environment variable or a command line argument, applied in that order. Signed-off-by: Owen Hilyard Signed-off-by: Juraj Linkeš --- dts/conf.yaml | 6 ++ dts/framework/__init__.py | 3 + dts/framework/config/__init__.py | 99 ++++++++++++++++++++++ dts/framework/config/conf_yaml_schema.json | 65 ++++++++++++++ dts/framework/settings.py | 84 ++++++++++++++++++ 5 files changed, 257 insertions(+) create mode 100644 dts/conf.yaml create mode 100644 dts/framework/__init__.py create mode 100644 dts/framework/config/__init__.py create mode 100644 dts/framework/config/conf_yaml_schema.json create mode 100644 dts/framework/settings.py diff --git a/dts/conf.yaml b/dts/conf.yaml new file mode 100644 index 0000000000..75947dc234 --- /dev/null +++ b/dts/conf.yaml @@ -0,0 +1,6 @@ +executions: + - system_under_test: "SUT 1" +nodes: + - name: "SUT 1" + hostname: sut1.change.me.localhost + user: root diff --git a/dts/framework/__init__.py b/dts/framework/__init__.py new file mode 100644 index 0000000000..d551ad4bf0 --- /dev/null +++ b/dts/framework/__init__.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2022 PANTHEON.tech s.r.o. +# Copyright(c) 2022 University of New Hampshire diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py new file mode 100644 index 0000000000..214be8e7f4 --- /dev/null +++ b/dts/framework/config/__init__.py @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2021 Intel Corporation +# Copyright(c) 2022 University of New Hampshire + +""" +Generic port and topology nodes configuration file load function +""" + +import json +import os.path +import pathlib +from dataclasses import dataclass +from typing import Any + +import warlock # type: ignore +import yaml + +from framework.settings import SETTINGS + + +# Slots enables some optimizations, by pre-allocating space for the defined +# attributes in the underlying data structure. +# +# Frozen makes the object immutable. This enables further optimizations, +# and makes it thread safe should we every want to move in that direction. +@dataclass(slots=True, frozen=True) +class NodeConfiguration: + name: str + hostname: str + user: str + password: str | None + + @staticmethod + def from_dict(d: dict) -> "NodeConfiguration": + return NodeConfiguration( + name=d["name"], + hostname=d["hostname"], + user=d["user"], + password=d.get("password"), + ) + + +@dataclass(slots=True, frozen=True) +class ExecutionConfiguration: + system_under_test: NodeConfiguration + + @staticmethod + def from_dict(d: dict, node_map: dict) -> "ExecutionConfiguration": + sut_name = d["system_under_test"] + assert sut_name in node_map, f"Unknown SUT {sut_name} in execution {d}" + + return ExecutionConfiguration( + system_under_test=node_map[sut_name], + ) + + +@dataclass(slots=True, frozen=True) +class Configuration: + executions: list[ExecutionConfiguration] + + @staticmethod + def from_dict(d: dict) -> "Configuration": + nodes: list[NodeConfiguration] = list( + map(NodeConfiguration.from_dict, d["nodes"]) + ) + assert len(nodes) > 0, "There must be a node to test" + + node_map = {node.name: node for node in nodes} + assert len(nodes) == len(node_map), "Duplicate node names are not allowed" + + executions: list[ExecutionConfiguration] = list( + map( + ExecutionConfiguration.from_dict, d["executions"], [node_map for _ in d] + ) + ) + + return Configuration(executions=executions) + + +def load_config() -> Configuration: + """ + Loads the configuration file and the configuration file schema, + validates the configuration file, and creates a configuration object. + """ + with open(SETTINGS.config_file_path, "r") as f: + config_data = yaml.safe_load(f) + + schema_path = os.path.join( + pathlib.Path(__file__).parent.resolve(), "conf_yaml_schema.json" + ) + + with open(schema_path, "r") as f: + schema = json.load(f) + config: dict[str, Any] = warlock.model_factory(schema, name="_Config")(config_data) + config_obj: Configuration = Configuration.from_dict(dict(config)) + return config_obj + + +CONFIGURATION = load_config() diff --git a/dts/framework/config/conf_yaml_schema.json b/dts/framework/config/conf_yaml_schema.json new file mode 100644 index 0000000000..6b8d6ccd05 --- /dev/null +++ b/dts/framework/config/conf_yaml_schema.json @@ -0,0 +1,65 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema", + "title": "DTS Config Schema", + "definitions": { + "node_name": { + "type": "string", + "description": "A unique identifier for a node" + } + }, + "type": "object", + "properties": { + "nodes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "A unique identifier for this node" + }, + "hostname": { + "type": "string", + "description": "A hostname from which the node running DTS can access this node. This can also be an IP address." + }, + "user": { + "type": "string", + "description": "The user to access this node with." + }, + "password": { + "type": "string", + "description": "The password to use on this node. Use only as a last resort. SSH keys are STRONGLY preferred." + } + }, + "additionalProperties": false, + "required": [ + "name", + "hostname", + "user" + ] + }, + "minimum": 1 + }, + "executions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "system_under_test": { + "$ref": "#/definitions/node_name" + } + }, + "additionalProperties": false, + "required": [ + "system_under_test" + ] + }, + "minimum": 1 + } + }, + "required": [ + "executions", + "nodes" + ], + "additionalProperties": false +} diff --git a/dts/framework/settings.py b/dts/framework/settings.py new file mode 100644 index 0000000000..007ab46c32 --- /dev/null +++ b/dts/framework/settings.py @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2021 Intel Corporation +# Copyright(c) 2022 PANTHEON.tech s.r.o. +# Copyright(c) 2022 University of New Hampshire + +import argparse +import os +from collections.abc import Callable, Iterable, Sequence +from dataclasses import dataclass +from typing import Any, TypeVar + +_T = TypeVar("_T") + + +def _env_arg(env_var: str) -> Any: + class _EnvironmentArgument(argparse.Action): + def __init__( + self, + option_strings: Sequence[str], + dest: str, + nargs: str | int | None = None, + const: str | None = None, + default: str = None, + type: Callable[[str], _T | argparse.FileType | None] = None, + choices: Iterable[_T] | None = None, + required: bool = True, + help: str | None = None, + metavar: str | tuple[str, ...] | None = None, + ) -> None: + env_var_value = os.environ.get(env_var) + default = env_var_value or default + super(_EnvironmentArgument, self).__init__( + option_strings, + dest, + nargs=nargs, + const=const, + default=default, + type=type, + choices=choices, + required=required, + help=help, + metavar=metavar, + ) + + def __call__( + self, + parser: argparse.ArgumentParser, + namespace: argparse.Namespace, + values: Any, + option_string: str = None, + ) -> None: + setattr(namespace, self.dest, values) + + return _EnvironmentArgument + + +@dataclass(slots=True, frozen=True) +class _Settings: + config_file_path: str + + +def _get_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="DPDK test framework.") + + parser.add_argument( + "--config-file", + action=_env_arg("DTS_CFG_FILE"), + default="conf.yaml", + required=False, + help="[DTS_CFG_FILE] configuration file that describes the test cases, SUTs " + "and targets.", + ) + + return parser + + +def _get_settings() -> _Settings: + parsed_args = _get_parser().parse_args() + return _Settings( + config_file_path=parsed_args.config_file, + ) + + +SETTINGS: _Settings = _get_settings() From patchwork Fri Nov 4 11:05:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 119483 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 76005A00C5; Fri, 4 Nov 2022 12:06:03 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 514EC42D3D; Fri, 4 Nov 2022 12:05:39 +0100 (CET) Received: from lb.pantheon.sk (lb.pantheon.sk [46.229.239.20]) by mails.dpdk.org (Postfix) with ESMTP id 9E76742D2C for ; Fri, 4 Nov 2022 12:05:37 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by lb.pantheon.sk (Postfix) with ESMTP id E100E1BA5AD; Fri, 4 Nov 2022 12:05:36 +0100 (CET) X-Virus-Scanned: amavisd-new at siecit.sk Received: from lb.pantheon.sk ([127.0.0.1]) by localhost (lb.pantheon.sk [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id LloK4K_XWtvg; Fri, 4 Nov 2022 12:05:33 +0100 (CET) Received: from entguard.lab.pantheon.local (unknown [46.229.239.141]) by lb.pantheon.sk (Postfix) with ESMTP id F082B1BA5AB; Fri, 4 Nov 2022 12:05:27 +0100 (CET) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, ohilyard@iol.unh.edu, lijuan.tu@intel.com, kda@semihalf.com, bruce.richardson@intel.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v8 4/9] dts: add basic logging facility Date: Fri, 4 Nov 2022 11:05:18 +0000 Message-Id: <20221104110523.511367-5-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20221104110523.511367-1-juraj.linkes@pantheon.tech> References: <20221103151934.450887-1-juraj.linkes@pantheon.tech> <20221104110523.511367-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org The logging module provides loggers distinguished by two attributes, a custom format and a verbosity switch. The loggers log to both console and more verbosely to files. Signed-off-by: Owen Hilyard Signed-off-by: Juraj Linkeš --- .gitignore | 3 + dts/framework/logger.py | 113 ++++++++++++++++++++++++++++++++++++++ dts/framework/settings.py | 23 ++++++++ 3 files changed, 139 insertions(+) create mode 100644 dts/framework/logger.py diff --git a/.gitignore b/.gitignore index 212c7aa28e..01a47a7606 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,9 @@ TAGS # ignore python bytecode files *.pyc +# DTS results +dts/output + # ignore default build directory, and directories from test-meson-builds.sh build build-* diff --git a/dts/framework/logger.py b/dts/framework/logger.py new file mode 100644 index 0000000000..a31fcc8242 --- /dev/null +++ b/dts/framework/logger.py @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation +# Copyright(c) 2022 PANTHEON.tech s.r.o. +# Copyright(c) 2022 University of New Hampshire + +""" +DTS logger module with several log level. DTS framework and TestSuite logs +are saved in different log files. +""" + +import logging +import os.path +from typing import TypedDict + +from .settings import SETTINGS + +date_fmt = "%Y/%m/%d %H:%M:%S" +stream_fmt = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + + +class LoggerDictType(TypedDict): + logger: "DTSLOG" + name: str + node: str + + +# List for saving all using loggers +Loggers: list[LoggerDictType] = [] + + +class DTSLOG(logging.LoggerAdapter): + """ + DTS log class for framework and testsuite. + """ + + logger: logging.Logger + node: str + sh: logging.StreamHandler + fh: logging.FileHandler + verbose_fh: logging.FileHandler + + def __init__(self, logger: logging.Logger, node: str = "suite"): + self.logger = logger + # 1 means log everything, this will be used by file handlers if their level + # is not set + self.logger.setLevel(1) + + self.node = node + + # add handler to emit to stdout + sh = logging.StreamHandler() + sh.setFormatter(logging.Formatter(stream_fmt, date_fmt)) + sh.setLevel(logging.INFO) # console handler default level + + if SETTINGS.verbose is True: + sh.setLevel(logging.DEBUG) + + self.logger.addHandler(sh) + self.sh = sh + + logging_path_prefix = os.path.join(SETTINGS.output_dir, node) + + fh = logging.FileHandler(f"{logging_path_prefix}.log") + fh.setFormatter( + logging.Formatter( + fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s", + datefmt=date_fmt, + ) + ) + + self.logger.addHandler(fh) + self.fh = fh + + # This outputs EVERYTHING, intended for post-mortem debugging + # Also optimized for processing via AWK (awk -F '|' ...) + verbose_fh = logging.FileHandler(f"{logging_path_prefix}.verbose.log") + verbose_fh.setFormatter( + logging.Formatter( + fmt="%(asctime)s|%(name)s|%(levelname)s|%(pathname)s|%(lineno)d|" + "%(funcName)s|%(process)d|%(thread)d|%(threadName)s|%(message)s", + datefmt=date_fmt, + ) + ) + + self.logger.addHandler(verbose_fh) + self.verbose_fh = verbose_fh + + super(DTSLOG, self).__init__(self.logger, dict(node=self.node)) + + def logger_exit(self) -> None: + """ + Remove stream handler and logfile handler. + """ + for handler in (self.sh, self.fh, self.verbose_fh): + handler.flush() + self.logger.removeHandler(handler) + + +def getLogger(name: str, node: str = "suite") -> DTSLOG: + """ + Get logger handler and if there's no handler for specified Node will create one. + """ + global Loggers + # return saved logger + logger: LoggerDictType + for logger in Loggers: + if logger["name"] == name and logger["node"] == node: + return logger["logger"] + + # return new logger + dts_logger: DTSLOG = DTSLOG(logging.getLogger(name), node) + Loggers.append({"logger": dts_logger, "name": name, "node": node}) + return dts_logger diff --git a/dts/framework/settings.py b/dts/framework/settings.py index 007ab46c32..b6c5bba2b9 100644 --- a/dts/framework/settings.py +++ b/dts/framework/settings.py @@ -57,6 +57,8 @@ def __call__( @dataclass(slots=True, frozen=True) class _Settings: config_file_path: str + output_dir: str + verbose: bool def _get_parser() -> argparse.ArgumentParser: @@ -71,6 +73,25 @@ def _get_parser() -> argparse.ArgumentParser: "and targets.", ) + parser.add_argument( + "--output-dir", + "--output", + action=_env_arg("DTS_OUTPUT_DIR"), + default="output", + required=False, + help="[DTS_OUTPUT_DIR] Output directory where dts logs and results are saved.", + ) + + parser.add_argument( + "-v", + "--verbose", + action=_env_arg("DTS_VERBOSE"), + default="N", + required=False, + help="[DTS_VERBOSE] Set to 'Y' to enable verbose output, logging all messages " + "to the console.", + ) + return parser @@ -78,6 +99,8 @@ def _get_settings() -> _Settings: parsed_args = _get_parser().parse_args() return _Settings( config_file_path=parsed_args.config_file, + output_dir=parsed_args.output_dir, + verbose=(parsed_args.verbose == "Y"), ) From patchwork Fri Nov 4 11:05:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 119482 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 0F04DA00C5; Fri, 4 Nov 2022 12:05:58 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 843C642D36; Fri, 4 Nov 2022 12:05:38 +0100 (CET) Received: from lb.pantheon.sk (lb.pantheon.sk [46.229.239.20]) by mails.dpdk.org (Postfix) with ESMTP id EF16C42D23 for ; Fri, 4 Nov 2022 12:05:35 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by lb.pantheon.sk (Postfix) with ESMTP id 467C61BA5A9; Fri, 4 Nov 2022 12:05:35 +0100 (CET) X-Virus-Scanned: amavisd-new at siecit.sk Received: from lb.pantheon.sk ([127.0.0.1]) by localhost (lb.pantheon.sk [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id lx5LziPEeRq5; Fri, 4 Nov 2022 12:05:34 +0100 (CET) Received: from entguard.lab.pantheon.local (unknown [46.229.239.141]) by lb.pantheon.sk (Postfix) with ESMTP id 73AF41BA5AD; Fri, 4 Nov 2022 12:05:28 +0100 (CET) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, ohilyard@iol.unh.edu, lijuan.tu@intel.com, kda@semihalf.com, bruce.richardson@intel.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v8 5/9] dts: add remote session abstraction Date: Fri, 4 Nov 2022 11:05:19 +0000 Message-Id: <20221104110523.511367-6-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20221104110523.511367-1-juraj.linkes@pantheon.tech> References: <20221103151934.450887-1-juraj.linkes@pantheon.tech> <20221104110523.511367-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org The abstraction allows for easy switching of implementations of remote connections (ssh, telnet, etc.). It implements some common features, such as logging of commands and their outputs and history bookkeeping and defines methods that must be implemented by derived classes. Signed-off-by: Owen Hilyard Signed-off-by: Juraj Linkeš --- dts/framework/remote_session/__init__.py | 2 + .../remote_session/remote_session.py | 95 +++++++++++++++++++ dts/framework/settings.py | 12 +++ 3 files changed, 109 insertions(+) create mode 100644 dts/framework/remote_session/__init__.py create mode 100644 dts/framework/remote_session/remote_session.py diff --git a/dts/framework/remote_session/__init__.py b/dts/framework/remote_session/__init__.py new file mode 100644 index 0000000000..9bb042a482 --- /dev/null +++ b/dts/framework/remote_session/__init__.py @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2022 PANTHEON.tech s.r.o. diff --git a/dts/framework/remote_session/remote_session.py b/dts/framework/remote_session/remote_session.py new file mode 100644 index 0000000000..33047d9d0a --- /dev/null +++ b/dts/framework/remote_session/remote_session.py @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation +# Copyright(c) 2022 PANTHEON.tech s.r.o. +# Copyright(c) 2022 University of New Hampshire + +import dataclasses +from abc import ABC, abstractmethod + +from framework.config import NodeConfiguration +from framework.logger import DTSLOG +from framework.settings import SETTINGS + + +@dataclasses.dataclass(slots=True, frozen=True) +class HistoryRecord: + name: str + command: str + output: str | int + + +class RemoteSession(ABC): + name: str + hostname: str + ip: str + port: int | None + username: str + password: str + logger: DTSLOG + history: list[HistoryRecord] + _node_config: NodeConfiguration + + def __init__( + self, + node_config: NodeConfiguration, + session_name: str, + logger: DTSLOG, + ): + self._node_config = node_config + + self.name = session_name + self.hostname = node_config.hostname + self.ip = self.hostname + self.port = None + if ":" in self.hostname: + self.ip, port = self.hostname.split(":") + self.port = int(port) + self.username = node_config.user + self.password = node_config.password or "" + self.logger = logger + self.history = [] + + self.logger.info(f"Connecting to {self.username}@{self.hostname}.") + self._connect() + self.logger.info(f"Connection to {self.username}@{self.hostname} successful.") + + @abstractmethod + def _connect(self) -> None: + """ + Create connection to assigned node. + """ + pass + + def send_command(self, command: str, timeout: float = SETTINGS.timeout) -> str: + self.logger.info(f"Sending: {command}") + out = self._send_command(command, timeout) + self.logger.debug(f"Received from {command}: {out}") + self._history_add(command=command, output=out) + return out + + @abstractmethod + def _send_command(self, command: str, timeout: float) -> str: + """ + Send a command and return the output. + """ + + def _history_add(self, command: str, output: str) -> None: + self.history.append( + HistoryRecord(name=self.name, command=command, output=output) + ) + + def close(self, force: bool = False) -> None: + self.logger.logger_exit() + self._close(force) + + @abstractmethod + def _close(self, force: bool = False) -> None: + """ + Close the remote session, freeing all used resources. + """ + + @abstractmethod + def is_alive(self) -> bool: + """ + Check whether the session is still responding. + """ diff --git a/dts/framework/settings.py b/dts/framework/settings.py index b6c5bba2b9..800f2c7b7f 100644 --- a/dts/framework/settings.py +++ b/dts/framework/settings.py @@ -58,6 +58,7 @@ def __call__( class _Settings: config_file_path: str output_dir: str + timeout: float verbose: bool @@ -82,6 +83,16 @@ def _get_parser() -> argparse.ArgumentParser: help="[DTS_OUTPUT_DIR] Output directory where dts logs and results are saved.", ) + parser.add_argument( + "-t", + "--timeout", + action=_env_arg("DTS_TIMEOUT"), + default=15, + required=False, + help="[DTS_TIMEOUT] The default timeout for all DTS operations except for " + "compiling DPDK.", + ) + parser.add_argument( "-v", "--verbose", @@ -100,6 +111,7 @@ def _get_settings() -> _Settings: return _Settings( config_file_path=parsed_args.config_file, output_dir=parsed_args.output_dir, + timeout=float(parsed_args.timeout), verbose=(parsed_args.verbose == "Y"), ) From patchwork Fri Nov 4 11:05:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 119484 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 54EA7A00C5; Fri, 4 Nov 2022 12:06:09 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 2100142D3B; Fri, 4 Nov 2022 12:05:40 +0100 (CET) Received: from lb.pantheon.sk (lb.pantheon.sk [46.229.239.20]) by mails.dpdk.org (Postfix) with ESMTP id F3CA842D3A for ; Fri, 4 Nov 2022 12:05:38 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by lb.pantheon.sk (Postfix) with ESMTP id 0DF5A1BA5AB; Fri, 4 Nov 2022 12:05:38 +0100 (CET) X-Virus-Scanned: amavisd-new at siecit.sk Received: from lb.pantheon.sk ([127.0.0.1]) by localhost (lb.pantheon.sk [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id pSWDT9BHOqCG; Fri, 4 Nov 2022 12:05:35 +0100 (CET) Received: from entguard.lab.pantheon.local (unknown [46.229.239.141]) by lb.pantheon.sk (Postfix) with ESMTP id E46E71BA5AE; Fri, 4 Nov 2022 12:05:28 +0100 (CET) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, ohilyard@iol.unh.edu, lijuan.tu@intel.com, kda@semihalf.com, bruce.richardson@intel.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v8 6/9] dts: add ssh session module Date: Fri, 4 Nov 2022 11:05:20 +0000 Message-Id: <20221104110523.511367-7-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20221104110523.511367-1-juraj.linkes@pantheon.tech> References: <20221103151934.450887-1-juraj.linkes@pantheon.tech> <20221104110523.511367-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org The module uses the pexpect python library and implements connection to a node and two ways to interact with the node: 1. Send a string with specified prompt which will be matched after the string has been sent to the node. 2. Send a command to be executed. No prompt is specified here. Signed-off-by: Owen Hilyard Signed-off-by: Juraj Linkeš --- dts/framework/exception.py | 56 ++++++ dts/framework/remote_session/__init__.py | 12 ++ dts/framework/remote_session/ssh_session.py | 184 ++++++++++++++++++++ dts/framework/utils.py | 12 ++ 4 files changed, 264 insertions(+) create mode 100644 dts/framework/exception.py create mode 100644 dts/framework/remote_session/ssh_session.py create mode 100644 dts/framework/utils.py diff --git a/dts/framework/exception.py b/dts/framework/exception.py new file mode 100644 index 0000000000..8b2f08a8f0 --- /dev/null +++ b/dts/framework/exception.py @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation +# Copyright(c) 2022 PANTHEON.tech s.r.o. +# Copyright(c) 2022 University of New Hampshire + +""" +User-defined exceptions used across the framework. +""" + + +class SSHTimeoutError(Exception): + """ + Command execution timeout. + """ + + command: str + output: str + + def __init__(self, command: str, output: str): + self.command = command + self.output = output + + def __str__(self) -> str: + return f"TIMEOUT on {self.command}" + + def get_output(self) -> str: + return self.output + + +class SSHConnectionError(Exception): + """ + SSH connection error. + """ + + host: str + + def __init__(self, host: str): + self.host = host + + def __str__(self) -> str: + return f"Error trying to connect with {self.host}" + + +class SSHSessionDeadError(Exception): + """ + SSH session is not alive. + It can no longer be used. + """ + + host: str + + def __init__(self, host: str): + self.host = host + + def __str__(self) -> str: + return f"SSH session with {self.host} has died" diff --git a/dts/framework/remote_session/__init__.py b/dts/framework/remote_session/__init__.py index 9bb042a482..a227d8db22 100644 --- a/dts/framework/remote_session/__init__.py +++ b/dts/framework/remote_session/__init__.py @@ -1,2 +1,14 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright(c) 2022 PANTHEON.tech s.r.o. + +from framework.config import NodeConfiguration +from framework.logger import DTSLOG + +from .remote_session import RemoteSession +from .ssh_session import SSHSession + + +def create_remote_session( + node_config: NodeConfiguration, name: str, logger: DTSLOG +) -> RemoteSession: + return SSHSession(node_config, name, logger) diff --git a/dts/framework/remote_session/ssh_session.py b/dts/framework/remote_session/ssh_session.py new file mode 100644 index 0000000000..7ec327054d --- /dev/null +++ b/dts/framework/remote_session/ssh_session.py @@ -0,0 +1,184 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation +# Copyright(c) 2022 PANTHEON.tech s.r.o. +# Copyright(c) 2022 University of New Hampshire + +import time + +from pexpect import pxssh # type: ignore + +from framework.config import NodeConfiguration +from framework.exception import SSHConnectionError, SSHSessionDeadError, SSHTimeoutError +from framework.logger import DTSLOG +from framework.utils import GREEN, RED + +from .remote_session import RemoteSession + + +class SSHSession(RemoteSession): + """ + Module for creating Pexpect SSH sessions to a node. + """ + + session: pxssh.pxssh + magic_prompt: str + + def __init__( + self, + node_config: NodeConfiguration, + session_name: str, + logger: DTSLOG, + ): + self.magic_prompt = "MAGIC PROMPT" + super(SSHSession, self).__init__(node_config, session_name, logger) + + def _connect(self) -> None: + """ + Create connection to assigned node. + """ + retry_attempts = 10 + login_timeout = 20 if self.port else 10 + password_regex = ( + r"(?i)(?:password:)|(?:passphrase for key)|(?i)(password for .+:)" + ) + try: + for retry_attempt in range(retry_attempts): + self.session = pxssh.pxssh(encoding="utf-8") + try: + self.session.login( + self.ip, + self.username, + self.password, + original_prompt="[$#>]", + port=self.port, + login_timeout=login_timeout, + password_regex=password_regex, + ) + break + except Exception as e: + self.logger.warning(e) + time.sleep(2) + self.logger.info( + f"Retrying connection: retry number {retry_attempt + 1}." + ) + else: + raise Exception(f"Connection to {self.hostname} failed") + + self.send_expect("stty -echo", "#") + self.send_expect("stty columns 1000", "#") + except Exception as e: + self.logger.error(RED(str(e))) + if getattr(self, "port", None): + suggestion = ( + f"\nSuggestion: Check if the firewall on {self.hostname} is " + f"stopped.\n" + ) + self.logger.info(GREEN(suggestion)) + + raise SSHConnectionError(self.hostname) + + def send_expect( + self, command: str, prompt: str, timeout: float = 15, verify: bool = False + ) -> str | int: + try: + ret = self.send_expect_base(command, prompt, timeout) + if verify: + ret_status = self.send_expect_base("echo $?", prompt, timeout) + try: + retval = int(ret_status) + if retval: + self.logger.error(f"Command: {command} failure!") + self.logger.error(ret) + return retval + else: + return ret + except ValueError: + return ret + else: + return ret + except Exception as e: + self.logger.error( + f"Exception happened in [{command}] and output is " + f"[{self._get_output()}]" + ) + raise e + + def send_expect_base(self, command: str, prompt: str, timeout: float) -> str: + self._clean_session() + original_prompt = self.session.PROMPT + self.session.PROMPT = prompt + self._send_line(command) + self._prompt(command, timeout) + + before = self._get_output() + self.session.PROMPT = original_prompt + return before + + def _clean_session(self) -> None: + self.session.PROMPT = self.magic_prompt + self.get_output(timeout=0.01) + self.session.PROMPT = self.session.UNIQUE_PROMPT + + def _send_line(self, command: str) -> None: + if not self.is_alive(): + raise SSHSessionDeadError(self.hostname) + if len(command) == 2 and command.startswith("^"): + self.session.sendcontrol(command[1]) + else: + self.session.sendline(command) + + def _prompt(self, command: str, timeout: float) -> None: + if not self.session.prompt(timeout): + raise SSHTimeoutError(command, self._get_output()) from None + + def get_output(self, timeout: float = 15) -> str: + """ + Get all output before timeout + """ + try: + self.session.prompt(timeout) + except Exception: + pass + + before = self._get_output() + self._flush() + + return before + + def _get_output(self) -> str: + if not self.is_alive(): + raise SSHSessionDeadError(self.hostname) + before = self.session.before.rsplit("\r\n", 1)[0] + if before == "[PEXPECT]": + return "" + return before + + def _flush(self) -> None: + """ + Clear all session buffer + """ + self.session.buffer = "" + self.session.before = "" + + def is_alive(self) -> bool: + return self.session.isalive() + + def _send_command(self, command: str, timeout: float) -> str: + try: + self._clean_session() + self._send_line(command) + except Exception as e: + raise e + + output = self.get_output(timeout=timeout) + self.session.PROMPT = self.session.UNIQUE_PROMPT + self.session.prompt(0.1) + + return output + + def _close(self, force: bool = False) -> None: + if force is True: + self.session.close() + else: + if self.is_alive(): + self.session.logout() diff --git a/dts/framework/utils.py b/dts/framework/utils.py new file mode 100644 index 0000000000..9c1975a22f --- /dev/null +++ b/dts/framework/utils.py @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation +# Copyright(c) 2022 PANTHEON.tech s.r.o. +# Copyright(c) 2022 University of New Hampshire + + +def GREEN(text: str) -> str: + return f"\u001B[32;1m{str(text)}\u001B[0m" + + +def RED(text: str) -> str: + return f"\u001B[31;1m{str(text)}\u001B[0m" From patchwork Fri Nov 4 11:05:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 119485 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 22A4DA00C5; Fri, 4 Nov 2022 12:06:14 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 15DD242D46; Fri, 4 Nov 2022 12:05:41 +0100 (CET) Received: from lb.pantheon.sk (lb.pantheon.sk [46.229.239.20]) by mails.dpdk.org (Postfix) with ESMTP id 478D442D3B for ; Fri, 4 Nov 2022 12:05:39 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by lb.pantheon.sk (Postfix) with ESMTP id 6AD421BA5B7; Fri, 4 Nov 2022 12:05:38 +0100 (CET) X-Virus-Scanned: amavisd-new at siecit.sk Received: from lb.pantheon.sk ([127.0.0.1]) by localhost (lb.pantheon.sk [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id n3uaic1TpF_Z; Fri, 4 Nov 2022 12:05:37 +0100 (CET) Received: from entguard.lab.pantheon.local (unknown [46.229.239.141]) by lb.pantheon.sk (Postfix) with ESMTP id 5EB701BA5B1; Fri, 4 Nov 2022 12:05:29 +0100 (CET) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, ohilyard@iol.unh.edu, lijuan.tu@intel.com, kda@semihalf.com, bruce.richardson@intel.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v8 7/9] dts: add node base class Date: Fri, 4 Nov 2022 11:05:21 +0000 Message-Id: <20221104110523.511367-8-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20221104110523.511367-1-juraj.linkes@pantheon.tech> References: <20221103151934.450887-1-juraj.linkes@pantheon.tech> <20221104110523.511367-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org The base class implements basic node management methods - connect and execute commands. Signed-off-by: Owen Hilyard Signed-off-by: Juraj Linkeš --- dts/framework/testbed_model/__init__.py | 7 +++ dts/framework/testbed_model/node.py | 62 +++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 dts/framework/testbed_model/__init__.py create mode 100644 dts/framework/testbed_model/node.py diff --git a/dts/framework/testbed_model/__init__.py b/dts/framework/testbed_model/__init__.py new file mode 100644 index 0000000000..c5512e5812 --- /dev/null +++ b/dts/framework/testbed_model/__init__.py @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2022 University of New Hampshire + +""" +This module contains the classes used to model the physical traffic generator, +system under test and any other components that need to be interacted with. +""" diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py new file mode 100644 index 0000000000..8437975416 --- /dev/null +++ b/dts/framework/testbed_model/node.py @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation +# Copyright(c) 2022 PANTHEON.tech s.r.o. +# Copyright(c) 2022 University of New Hampshire + +""" +A node is a generic host that DTS connects to and manages. +""" + +from framework.config import NodeConfiguration +from framework.logger import DTSLOG, getLogger +from framework.remote_session import RemoteSession, create_remote_session +from framework.settings import SETTINGS + + +class Node(object): + """ + Basic module for node management. This module implements methods that + manage a node, such as information gathering (of CPU/PCI/NIC) and + environment setup. + """ + + name: str + main_session: RemoteSession + logger: DTSLOG + _config: NodeConfiguration + _other_sessions: list[RemoteSession] + + def __init__(self, node_config: NodeConfiguration): + self._config = node_config + self._other_sessions = [] + + self.name = node_config.name + self.logger = getLogger(self.name) + self.logger.info(f"Created node: {self.name}") + self.main_session = create_remote_session(self._config, self.name, self.logger) + + def send_command(self, cmds: str, timeout: float = SETTINGS.timeout) -> str: + """ + Send commands to node and return string before timeout. + """ + + return self.main_session.send_command(cmds, timeout) + + def create_session(self, name: str) -> RemoteSession: + connection = create_remote_session( + self._config, + name, + getLogger(name, node=self.name), + ) + self._other_sessions.append(connection) + return connection + + def node_exit(self) -> None: + """ + Recover all resource before node exit + """ + if self.main_session: + self.main_session.close() + for session in self._other_sessions: + session.close() + self.logger.logger_exit() From patchwork Fri Nov 4 11:05:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 119486 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 85F01A00C5; Fri, 4 Nov 2022 12:06:22 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 5DB0142D4F; Fri, 4 Nov 2022 12:05:42 +0100 (CET) Received: from lb.pantheon.sk (lb.pantheon.sk [46.229.239.20]) by mails.dpdk.org (Postfix) with ESMTP id BCC5B42D42 for ; Fri, 4 Nov 2022 12:05:40 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by lb.pantheon.sk (Postfix) with ESMTP id BF5811BA5AE; Fri, 4 Nov 2022 12:05:39 +0100 (CET) X-Virus-Scanned: amavisd-new at siecit.sk Received: from lb.pantheon.sk ([127.0.0.1]) by localhost (lb.pantheon.sk [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id uVcao2E68788; Fri, 4 Nov 2022 12:05:38 +0100 (CET) Received: from entguard.lab.pantheon.local (unknown [46.229.239.141]) by lb.pantheon.sk (Postfix) with ESMTP id E06951BA5B5; Fri, 4 Nov 2022 12:05:29 +0100 (CET) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, ohilyard@iol.unh.edu, lijuan.tu@intel.com, kda@semihalf.com, bruce.richardson@intel.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v8 8/9] dts: add dts workflow module Date: Fri, 4 Nov 2022 11:05:22 +0000 Message-Id: <20221104110523.511367-9-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20221104110523.511367-1-juraj.linkes@pantheon.tech> References: <20221103151934.450887-1-juraj.linkes@pantheon.tech> <20221104110523.511367-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org The module implements methods needed to run DTS. It handles the creation of objects and eventually the whole DTS workflow, such as running node setups, test gathering, setup and execution and various cleanups. Signed-off-by: Owen Hilyard Signed-off-by: Juraj Linkeš --- dts/framework/dts.py | 67 ++++++++++++++++++++++++++++++++++++++++++ dts/framework/utils.py | 18 ++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 dts/framework/dts.py diff --git a/dts/framework/dts.py b/dts/framework/dts.py new file mode 100644 index 0000000000..d23cfc4526 --- /dev/null +++ b/dts/framework/dts.py @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2019 Intel Corporation +# Copyright(c) 2022 PANTHEON.tech s.r.o. +# Copyright(c) 2022 University of New Hampshire + +import sys +import traceback +from collections.abc import Iterable + +from framework.testbed_model.node import Node + +from .config import CONFIGURATION +from .logger import DTSLOG, getLogger +from .utils import check_dts_python_version + +dts_logger: DTSLOG | None = None + + +def run_all() -> None: + """ + Main process of DTS, it will run all test suites in the config file. + """ + + global dts_logger + + # check the python version of the server that run dts + check_dts_python_version() + + dts_logger = getLogger("dts") + + nodes = {} + # This try/finally block means "Run the try block, if there is an exception, + # run the finally block before passing it upward. If there is not an exception, + # run the finally block after the try block is finished." This helps avoid the + # problem of python's interpreter exit context, which essentially prevents you + # from making certain system calls. This makes cleaning up resources difficult, + # since most of the resources in DTS are network-based, which is restricted. + try: + # for all Execution sections + for execution in CONFIGURATION.executions: + sut_config = execution.system_under_test + if sut_config.name not in nodes: + node = Node(sut_config) + nodes[sut_config.name] = node + node.send_command("echo Hello World") + + except Exception as e: + # sys.exit() doesn't produce a stack trace, need to print it explicitly + traceback.print_exc() + raise e + + finally: + quit_execution(nodes.values()) + + +def quit_execution(sut_nodes: Iterable[Node]) -> None: + """ + Close session to SUT and TG before quit. + Return exit status when failure occurred. + """ + for sut_node in sut_nodes: + # close all session + sut_node.node_exit() + + if dts_logger is not None: + dts_logger.info("DTS execution has ended.") + sys.exit(0) diff --git a/dts/framework/utils.py b/dts/framework/utils.py index 9c1975a22f..c28c8f1082 100644 --- a/dts/framework/utils.py +++ b/dts/framework/utils.py @@ -3,6 +3,24 @@ # Copyright(c) 2022 PANTHEON.tech s.r.o. # Copyright(c) 2022 University of New Hampshire +import sys + + +def check_dts_python_version() -> None: + if sys.version_info.major < 3 or ( + sys.version_info.major == 3 and sys.version_info.minor < 10 + ): + print( + RED( + ( + "WARNING: DTS execution node's python version is lower than" + "python 3.10, is deprecated and will not work in future releases." + ) + ), + file=sys.stderr, + ) + print(RED("Please use Python >= 3.10 instead"), file=sys.stderr) + def GREEN(text: str) -> str: return f"\u001B[32;1m{str(text)}\u001B[0m" From patchwork Fri Nov 4 11:05:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 119487 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id CBFC2A00C5; Fri, 4 Nov 2022 12:06:27 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 2E33E42D53; Fri, 4 Nov 2022 12:05:43 +0100 (CET) Received: from lb.pantheon.sk (lb.pantheon.sk [46.229.239.20]) by mails.dpdk.org (Postfix) with ESMTP id ECED442D44 for ; Fri, 4 Nov 2022 12:05:40 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by lb.pantheon.sk (Postfix) with ESMTP id E88501BA5B1; Fri, 4 Nov 2022 12:05:39 +0100 (CET) X-Virus-Scanned: amavisd-new at siecit.sk Received: from lb.pantheon.sk ([127.0.0.1]) by localhost (lb.pantheon.sk [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 6ppvKmW-jAbc; Fri, 4 Nov 2022 12:05:39 +0100 (CET) Received: from entguard.lab.pantheon.local (unknown [46.229.239.141]) by lb.pantheon.sk (Postfix) with ESMTP id 256381BA58E; Fri, 4 Nov 2022 12:05:31 +0100 (CET) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, ohilyard@iol.unh.edu, lijuan.tu@intel.com, kda@semihalf.com, bruce.richardson@intel.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v8 9/9] dts: add dts executable script Date: Fri, 4 Nov 2022 11:05:23 +0000 Message-Id: <20221104110523.511367-10-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20221104110523.511367-1-juraj.linkes@pantheon.tech> References: <20221103151934.450887-1-juraj.linkes@pantheon.tech> <20221104110523.511367-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org The script is an interface to run DTS. Signed-off-by: Owen Hilyard Signed-off-by: Juraj Linkeš --- dts/main.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100755 dts/main.py diff --git a/dts/main.py b/dts/main.py new file mode 100755 index 0000000000..43311fa847 --- /dev/null +++ b/dts/main.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation +# Copyright(c) 2022 PANTHEON.tech s.r.o. +# Copyright(c) 2022 University of New Hampshire + +""" +A test framework for testing DPDK. +""" + +import logging + +from framework import dts + + +def main() -> None: + dts.run_all() + + +# Main program begins here +if __name__ == "__main__": + logging.raiseExceptions = True + main()