diff --git a/.crossbar/.gitignore b/.crossbar/.gitignore deleted file mode 100644 index a6c031384..000000000 --- a/.crossbar/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -key.priv -key.pub -node.pid diff --git a/.crossbar/config-anonymous.yaml b/.crossbar/config-anonymous.yaml deleted file mode 100644 index b12e8d70d..000000000 --- a/.crossbar/config-anonymous.yaml +++ /dev/null @@ -1,45 +0,0 @@ -version: 2 -workers: -- type: router - realms: - - name: realm1 - roles: - - name: public - permissions: - - uri: '' - match: prefix - allow: - call: true - register: true - publish: true - subscribe: true - disclose: - caller: true - publisher: true - cache: true - transports: - - type: web - endpoint: - type: tcp - port: 20408 - paths: - /: - type: static - directory: ../web - ws: - type: websocket - auth: - anonymous: - type: static - role: public -- id: coordinator - type: guest - executable: /path/to/labgrid-venv/bin/python3 - arguments: - - -m - - labgrid.remote.coordinator - options: - workdir: . - env: - vars: - WS: ws://localhost:20408/ws diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 2075bfea8..390dad4cf 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -7,11 +7,11 @@ jobs: runs-on: ubuntu-22.04 continue-on-error: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.11" - name: Install python dependencies diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1a2e2dfbe..5fbfec049 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -5,42 +5,44 @@ on: branches: [ master ] tags: - '*' + release: + types: [ released ] + workflow_dispatch: + +env: + QEMU_PLATFORMS: arm64 + IMAGE_PLATFORMS: linux/amd64,linux/arm64 + IMAGE_PREFIX: ${{ secrets.DOCKERHUB_PREFIX }} jobs: docker: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install system dependencies run: | - sudo apt install -yq python3-pip - python3 -m pip install setuptools_scm + sudo apt install -yq python3-pip python3-setuptools-scm + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: ${QEMU_PLATFORMS} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build docker image + - name: Build amd64 docker image and validate run: | - ./dockerfiles/build.sh - docker-compose -f dockerfiles/staging/docker-compose.yml up --exit-code-from client client - docker-compose -f dockerfiles/staging/docker-compose.yml down + ./dockerfiles/build.sh --load + docker compose -f dockerfiles/staging/docker-compose.yml up --exit-code-from client client + docker compose -f dockerfiles/staging/docker-compose.yml down docker images - - name: Tag latest images - run: | - docker tag labgrid-client ${{ secrets.DOCKERHUB_PREFIX }}client - docker tag labgrid-exporter ${{ secrets.DOCKERHUB_PREFIX }}exporter - docker tag labgrid-coordinator ${{ secrets.DOCKERHUB_PREFIX }}coordinator - - name: Tag release image + - name: Build, tag and push latest image for all platforms + run: ./dockerfiles/build.sh --platform ${IMAGE_PLATFORMS} --push + - name: Tag and push release image for all platforms if: startsWith(github.ref, 'refs/tags') - run: | - docker tag labgrid-client ${{ secrets.DOCKERHUB_PREFIX }}client:${GITHUB_REF_NAME} - docker tag labgrid-exporter ${{ secrets.DOCKERHUB_PREFIX }}exporter:${GITHUB_REF_NAME} - docker tag labgrid-coordinator ${{ secrets.DOCKERHUB_PREFIX }}coordinator:${GITHUB_REF_NAME} - - name: Push to dockerhub - run: | - docker push --all-tags ${{ secrets.DOCKERHUB_PREFIX }}client - docker push --all-tags ${{ secrets.DOCKERHUB_PREFIX }}exporter - docker push --all-tags ${{ secrets.DOCKERHUB_PREFIX }}coordinator - - name: Show images again - run: docker images + env: + IMAGE_TAG: ${{ github.ref_name }} + run: ./dockerfiles/build.sh --platform ${IMAGE_PLATFORMS} --push diff --git a/.github/workflows/push-pr-unit-tests.yml b/.github/workflows/push-pr-unit-tests.yml index 8477cd113..f8dec40f4 100644 --- a/.github/workflows/push-pr-unit-tests.yml +++ b/.github/workflows/push-pr-unit-tests.yml @@ -8,8 +8,10 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.8', '3.9', '3.10', '3.11'] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] uses: ./.github/workflows/reusable-unit-tests.yml + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: python-version: ${{ matrix.python-version }} push-pr-unit-tests-docker: diff --git a/.github/workflows/reusable-unit-tests-docker.yml b/.github/workflows/reusable-unit-tests-docker.yml index 063438aeb..947c413cb 100644 --- a/.github/workflows/reusable-unit-tests-docker.yml +++ b/.github/workflows/reusable-unit-tests-docker.yml @@ -9,20 +9,19 @@ on: jobs: docker: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ inputs.branch }} - name: Install system dependencies run: | - sudo apt install -yq python3-pip - python3 -m pip install setuptools_scm + sudo apt install -yq python3-pip python3-setuptools-scm - name: Build docker images run: | ./dockerfiles/build.sh - docker-compose -f dockerfiles/staging/docker-compose.yml up --exit-code-from client client || (docker-compose -f dockerfiles/staging/docker-compose.yml logs --timestamps && false) - docker-compose -f dockerfiles/staging/docker-compose.yml down + docker compose -f dockerfiles/staging/docker-compose.yml up --exit-code-from client client || (docker compose -f dockerfiles/staging/docker-compose.yml logs --timestamps && false) + docker compose -f dockerfiles/staging/docker-compose.yml down - name: Show docker images run: | docker images diff --git a/.github/workflows/reusable-unit-tests.yml b/.github/workflows/reusable-unit-tests.yml index 18251a3f9..d31731410 100644 --- a/.github/workflows/reusable-unit-tests.yml +++ b/.github/workflows/reusable-unit-tests.yml @@ -9,20 +9,23 @@ on: branch: type: string required: false + secrets: + CODECOV_TOKEN: + required: false jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 continue-on-error: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ inputs.branch }} - name: Set up Python ${{ inputs.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ inputs.python-version }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} @@ -30,7 +33,7 @@ jobs: ${{ runner.os }}-pip- - name: Install system dependencies run: | - sudo apt-get install -yq libow-dev openssh-server openssh-client libsnappy-dev ncurses-term graphviz openocd + sudo apt-get install -yq libow-dev openssh-server openssh-client graphviz openocd sudo mkdir -p /var/cache/labgrid/runner && sudo chown runner /var/cache/labgrid/runner - name: Prepare local SSH run: | @@ -46,17 +49,27 @@ jobs: - name: Install labgrid run: | pip install -e ".[dev]" - - name: Install crossbar in virtualenv - run: | - virtualenv -p python3 crossbar-venv - crossbar-venv/bin/pip install -r crossbar-requirements.txt - name: Lint with pylint run: | pylint --list-msgs-enabled pylint labgrid + - name: Format with ruff + run: | + ruff format --check --diff - name: Test with pytest run: | - TERM=xterm pytest --cov-config .coveragerc --cov=labgrid --local-sshmanager --ssh-username runner --crossbar-venv crossbar-venv -k "not test_docker_with_daemon" + pytest -r a --cov-config .coveragerc --cov=labgrid --junitxml=junit.xml -o junit_family=legacy --local-sshmanager --ssh-username runner -k "not test_docker_with_daemon" + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: ${{ inputs.python-version }} + - name: Upload test results to Codecov + if: ${{ !cancelled() }} + uses: codecov/test-results-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: ${{ inputs.python-version }} - name: Build documentation run: | make -C doc clean @@ -68,4 +81,3 @@ jobs: # check README.rst separately pip install rstcheck rstcheck --ignore-languages=bash --report-level=WARNING README.rst - - uses: codecov/codecov-action@v3 diff --git a/.github/workflows/scheduled-unit-tests.yml b/.github/workflows/scheduled-unit-tests.yml index 4d915d387..a01ee7e89 100644 --- a/.github/workflows/scheduled-unit-tests.yml +++ b/.github/workflows/scheduled-unit-tests.yml @@ -10,9 +10,11 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.8', '3.9', '3.10', '3.11'] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] branch: ['master'] uses: ./.github/workflows/reusable-unit-tests.yml + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: python-version: ${{ matrix.python-version }} branch: ${{ matrix.branch }} diff --git a/.gitignore b/.gitignore index 84192a924..dad0e7c90 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ /dist /.pytest_cache/ /htmlcov/ -/dockerfiles/staging/crossbar/* -!/dockerfiles/staging/crossbar/places_example.yaml +/labgrid/_version.py +/dockerfiles/staging/coordinator/* +!/dockerfiles/staging/coordinator/places_example.yaml /.idea diff --git a/.readthedocs.yaml b/.readthedocs.yaml index f59ca7397..bab308e17 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,5 +1,8 @@ version: 2 +sphinx: + configuration: doc/conf.py + python: install: - method: pip @@ -8,6 +11,6 @@ python: - doc build: - os: "ubuntu-22.04" + os: "ubuntu-24.04" tools: - python: "3.11" + python: "3.13" diff --git a/CHANGES.rst b/CHANGES.rst index c4fe8bc1e..7764eb665 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,65 @@ -Release 23.1 (unreleased) +Release 25.0 (Unreleased) +------------------------- +As announced +`before `_, +this is the first release using gRPC instead of crossbar/autobahn for +communication between client/exporter and coordinator. + +Crossbar/autobahn are unfortunately not very well maintained anymore. The +crossbar component was moved to its own virtualenv to cope with the high number +of dependencies leading to conflicts. Support for Python 3.13 is still not +available in a crossbar release on PyPI. + +That's why labgrid moves to gRPC with this release. gRPC is a well maintained +RPC framework with a lot of users. As a side effect, the message transfer is +more performant and the import times are shorter. + +New Features in 25.0 +~~~~~~~~~~~~~~~~~~~~ +- All components can be installed into the same virtualenv again. +- The `QEMUDriver` now supports setting the ``display`` option to + ``qemu-default``, which will neither set the QEMU ``-display`` option + or pass along ``-nographic``. + +Bug fixes in 25.0 +~~~~~~~~~~~~~~~~~ + +FIXME + +Breaking changes in 25.0 +~~~~~~~~~~~~~~~~~~~~~~~~ +Maintaining support for both crossbar/autobahn as well as gRPC in labgrid would +be a lot of effort due to the different architectures of those frameworks. +Therefore, a hard migration to gRPC is deemed the lesser issue. + +Due to the migration, 25.0 includes the following breaking changes: + +- The labgrid environment config option ``crossbar_url`` was renamed to + ``coordinator_address``. The environment variable ``LG_CROSSBAR`` was renamed + to ``LG_COORDINATOR``. +- The labgrid environment config option ``crossbar_realm`` is now obsolete as + well as the environment variable ``LG_CROSSBAR_REALM``. +- The coordinator is available as ``labgrid-coordinator`` (instead of + ``crossbar start``). No additional configuration file is required. +- The systemd services in ``contrib/systemd/`` were updated. + +Other breaking changes include: + +- Support for Python 3.8 was dropped. +- The siglent power backend is deprecated because it uses the no longer + maintained vxi11 module which again uses the deprecated (and in Python 3.13 + removed) xdrlib. See + `issue #1507 `_. + +Known issues in 25.0 +~~~~~~~~~~~~~~~~~~~~ + +FIXME + +Release 24.0 (Released Aug 12, 2024) ------------------------------------ -New Features in 23.1 +New Features in 24.0 ~~~~~~~~~~~~~~~~~~~~ - When invoking tests with pytest, the ``--log-(cli|file)-(level|format)`` command line arguments and their corresponding pytest.ini configure options @@ -10,28 +68,128 @@ New Features in 23.1 - A new log level called ``CONSOLE`` has been added between the default ``INFO`` and ``DEBUG`` levels. This level will show all reads and writes made to the serial console during testing. - -Bug fixes in 23.1 +- The docker support was extended to support buildx, allowing the build of arm64 + container images. +- The tool lookup function has been extended to return the original name in case + the path can't be found. This makes specification of the qemu binary easier to + use. +- The ``bindings`` base class has been extended, allowing the user to retrieve + all resources used by a driver. +- Support for STLink V2 was added. +- ``UBootStrategy`` was extended with a ``force()`` function. +- labgrid was switched from pysnmp to pysnmp-lexstudio. +- Support for Segger J-Link was added. +- Place tags are now exposed by the RemotePlace. +- The sync-places contrib script has gained support for named matches. +- Remote support for YKush Devices was added. +- Support for sigrok DMMs was added. +- Support for Digital Outputs switched via HTTP was added. +- The ``QEMUDriver`` has a new get_qemu_base_args() function which can be used to + extract the arguments passed to qemu. +- The ``SSHDriver`` has gained support to forward unix sockets. +- The exporter has gained an ``--fqdn`` argument to set the hostname to the + fully qualified domain name instead of the hostname. +- The ``QEMUDriver`` now has an additional ``disk_opts`` property which can be + used to pass additional options for the disk directly to QEMU +- All drivers now inherit a logger from the ``Driver`` base class and many + drivers were changed to use this logger. +- The new ``poe_mib`` backend allows switching of power over Ethernet-capable + ports on switches that use the corresponding SNMP MIB. +- The ``RawNetworkInterfaceDriver`` allows the replay and recording of network + packets on ethernet interfaces. +- The i.MX93 usb loader USB ID has been added to the ``IMXUSBLoader`` resource. +- Support for udev matched GPIOs has been added. +- labgrid-client now has a ``write-files`` subcommand to copy files onto mass + storage devices. +- The ``NetworkPowerPort`` supports a new backend ``ubus``. It controls PoE + switches running OpenWrt using the ubus interface. +- The pyproject.toml gained a config for `ruff `_. +- ``setuptools_scm`` is now used to generate a version file. +- labgrid-client console will fallback to telnet if microcom is not available. +- A power backend for tinycontrol.eu IP Power Socket 6G10A v2 was added. +- Labgrid now publishes arm64 docker images. +- Labgrid's YAML parser will now warn when mapping keys are duplicated and thus + overwritten. +- LC USB Relais are now supported. + + +Bug fixes in 24.0 ~~~~~~~~~~~~~~~~~ - The pypi release now uses the labgrid pyserial fork in the form of the pyserial-labgrid package. This fixes installation with newer versions of pip. - Several tests have gained an importorskip() call to skip them if the module is not available. +- labgrid now uses its own pyserial fork from pypi since installation from + github as an egg is no longer properly supported. - The build-and-release workflow supports building wheels. -- The markers now are restricted to patterns which won't match WARN, - ERROR, INFO and similar log notifiers. - Fix named SSH lookups in conjunction with an environment file in labgrid-client. - -Breaking changes in 23.1 -~~~~~~~~~~~~~~~~~~~~~~~~~ +- The crossbar virtual-environment now needs to be separate from the labgrid + environment, for more information please consult the `current documentation `_. +- The markers now are restricted to patterns which won't match WARN, + ERROR, INFO and similar log notifiers. +- A race inside the ``SSHDriver`` cleanup has been fixed. +- The ``labgrid-client monitor`` command now outputs the full resource identifier. +- Many of the USB loader commands e.g. imx-usb-loader will now print to the + console when logging is not enabled. +- An ``UnboundLocalError`` inside the atomic_replace code which is used inside the + coordinator was fixed. +- Resources of different classes can now have the same name. +- A bug within the pytest logging setup was fixed. +- The ``QemuDriver`` correctly handles the different command lines for virgl + enablement. +- A bug was fixed where resource names were ignored during lookup of the correct + power driver. +- ManagedFile was fixed to work with the stat command on Darwin. +- Instead of using a private member on the pytest config, the labgrid plugin now + uses the pytest config stash. +- The ``ShellDriver`` was fixed to set the correct status attribute. +- The USBNetworkInterface now warns if the interface name is set, as it will be + overwritten by the ResourceManager to assign the correct interface name. +- Fix sftp option issue in SSH driver that caused sftp to only work once per + test run. +- ManagedFile NFS detection heuristic now does symlink resolution on the + local host. +- XModem support within the Shelldriver was fixed by removing the newline from + the marker. +- A typo in the ``NFSProviderDriver`` class was fixed. Documentation was already + correct, however the classname contained an additional P. +- The ``--loop`` argument for labgrid-client console was fixed. +- The password for the ``ShellDriver`` can now be an empty string. +- The default crossbar configuration now enables auto-fragmentation to handle + bigger labs where the payload size can be bigger than 1 megabyte. +- The ``SSHDriver`` redirects ``/dev/null`` to stdin of commands run via SSH. + This prevents unexpected input, especially when using the + ``ManualPowerDriver`` or a REPL. +- The ``ser2net`` version check for YAML configurations in the exporter was + fixed. +- The exporter forces ``ser2net`` TCP connections for versions >=4.2.0. +- The retrieval of the DTR status for ``SerialPortDigitalOutputDriver`` was + fixed. +- The ``SSHDriver`` keepalive is now correctly stopped when using existing + connections. +- The power backend for raritan devices now supports devices with more than 16 + outlets. +- The ``ExternalConsoleDriver`` now correctly sets the bufsize to zero to + prevent buffering. + +Breaking changes in 24.0 +~~~~~~~~~~~~~~~~~~~~~~~~ +- Support for Python 3.7 was dropped. +- Support for the legacy ticket authentication was dropped: If the coordinator + logs ModuleNotFoundError on startup, switch the crossbar config to anonymous + authentication (see ``.crossbar/config-anonymous.yaml`` for an example). - The Debian package (``debian/``) no longer contains crossbar. Use the `coordinator container `_ or - install it into a separate local venv as desribed in the + install it into a separate local venv as described in the `documentation `_. + If you see ``WARNING: Ticket authentication is deprecated. Please update your + coordinator.`` on the client when running an updated coordinator, your + coordinator configuration may set ``ticket`` instead of ``anonymous`` auth. - The `StepReporter` API has been changed. To start step reporting, you must - now call ``StepReporter.start()`` instead of ``StepReporter()`` + now call ``StepReporter.start()`` instead of ``StepReporter()``, and set up + logging via ``labgrid.logging.basicConfig()``. - Logging output when running pytest is no longer sent to stderr by default, since this is both chatty and also unnecessary with the improved logging flexibility. It it recommended to use the ``--log-cli-level=INFO`` command @@ -54,10 +212,100 @@ Breaking changes in 23.1 slightly. ``-vv`` is now an alias for ``--log-cli-level=INFO`` (effectively unchanged), ``-vvv`` is an alias for ``--log-cli-level=CONSOLE``, and ``-vvvv`` is an alias for ``--log-cli-level=DEBUG``. - -Known issues in 23.1 +- The `BareboxDriver` now remembers the log level, sets it to ``0`` on initial + activation/reset and recovers it on ``boot()``. During + ``run()``/``run_check()`` the initially detected log level is used. +- The `NFSProviderDriver` now returns mount and path information on ``stage()`` + instead of the path to be used on the target. The previous return value did + not fit the NFS mount use case. +- The `NFSProvider` and `RemoteNFSProvider` resources no longer expect the + ``internal`` and ``external`` arguments as they do not fit the NFS mount use + case. + +Known issues in 24.0 ~~~~~~~~~~~~~~~~~~~~ +- Some client commands return 0 even if the command failed. + + +Release 23.0.6 (Released Apr 16, 2024) +-------------------------------------- +Bug fixes in 23.0.6 +~~~~~~~~~~~~~~~~~~~ +- In `USBVideoDriver`, use the ``playbin3`` element instead of ``playbin`` to + fix decoding via VA-API for certain webcams on AMD graphic cards. +- Let the `SSHDriver` redirect ``/dev/null`` to stdin on ``run()`` to prevent + unexpected consumption of stdin of the remotely started process. +- Cover more failure scenarios in the exporter and coordinator systemd + services, fix the service startup order, do not buffer journal logs. + +Release 23.0.5 (Released Jan 13, 2024) +-------------------------------------- + +Bug fixes in 23.0.5 +~~~~~~~~~~~~~~~~~~~ +- Fix readthedocs build by specifying Python version and OS. +- Fix several incompatibilities with doc sphinxcontrib-* dependencies having + dropped their explicit Sphinx dependencies, which prevented generation of + labgrid's docs. + +Release 23.0.4 (Released Nov 10, 2023) +-------------------------------------- + +Bug fixes in 23.0.4 +~~~~~~~~~~~~~~~~~~~ +- Fix dockerfiles syntax error that became fatal in a recent docker release. +- Fix ShellDriver's xmodem functionality. +- Pin pylint to prevent incompatibility with pinned pytest-pylint. +- Fix ``labgrid-client console --loop`` on disappearing serial ports (such as + on-board FTDIs). + +Release 23.0.3 (Released Jul 20, 2023) +-------------------------------------- + +Bug fixes in 23.0.3 +~~~~~~~~~~~~~~~~~~~ +- Update to PyYAML 6.0.1 to prevent install errors with Cython>=3.0, see: + https://github.com/yaml/pyyaml/issues/601 + https://github.com/yaml/pyyaml/pull/726#issuecomment-1640397938 + +Release 23.0.2 (Released Jul 04, 2023) +-------------------------------------- + +Bug fixes in 23.0.2 +~~~~~~~~~~~~~~~~~~~ +- Move `SSHDriver`'s control socket tmpdir clean up after the the SSH process + has terminated. Ignore errors on cleanup since it's best effort. +- Add missing class name in ``labgrid-client monitor`` resource output. +- Print USB loader process output if log level does not cover logging it. +- Fix UnboundLocalError in ``atomic_replace()`` used by the coordinator and + ``labgrid-client export`` to write config files. +- Let Config's ``get_tool()`` return the requested tool if it is not found in + the config. Return the resolved path if it exists, otherwise return the value + as is. Also drop the now obsolete tool fallbacks from the drivers and add + tests. +- Fix `USBSDMuxDevice`/`USBSDWireDevice` udev race condition leading to + outdated control/disk paths. +- Fix `SSHDriver`'s ``explicit_sftp_mode`` option to allow calls to ``put()`` + and ``get()`` multiple times. Also make ``scp()`` respect this option. +- Add compatibility with QEMU >= 6.1.0 to `QEMUDriver`'s ``display`` argument + for the ``egl-headless`` option. + +Release 23.0.1 (Released Apr 26, 2023) +-------------------------------------- + +Bug fixes in 23.0.1 +~~~~~~~~~~~~~~~~~~~ +- The pypi release now uses the labgrid pyserial fork in the form of the + pyserial-labgrid package. This fixes installation with newer versions + of pip. +- Several tests have gained an importorskip() call to skip them if the + module is not available. +- The build-and-release workflow supports building wheels. +- The markers now are restricted to patterns which won't match WARN, + ERROR, INFO and similar log notifiers. +- Fix named SSH lookups in conjunction with an environment file in + labgrid-client. Release 23.0 (Released Apr 24, 2023) ------------------------------------ @@ -166,7 +414,7 @@ Bug fixes in 23.0 evaluation. Breaking changes in 23.0 -~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~ - ``Config``'s ``get_option()``/``get_target_option()`` convert non-string options no longer to strings. - `UBootDriver`'s ``boot_expression`` attribute is deprecated, it will no @@ -246,7 +494,7 @@ Breaking changes in 0.4.0 - ``EthernetInterface`` has been renamed to ``NetworkInterface``. Known issues in 0.4.0 -~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~ - Some client commands return 0 even if the command failed. - Currently empty passwords are not well supported by the ShellDriver @@ -343,7 +591,7 @@ Breaking changes in 0.3.0 reasons. Known issues in 0.3.0 -~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~ - There are several reports of ``sshpass`` used within the SSHDriver not working in call cases or only on the first connection. - Some client commands return 0 even if the command failed. diff --git a/README.rst b/README.rst index 7d3935f70..2ae9aafda 100644 --- a/README.rst +++ b/README.rst @@ -10,6 +10,8 @@ Labgrid is an embedded board control python library with a focus on testing, dev and general automation. It includes a remote control layer to control boards connected to other hosts. +* `Getting started <#Getting-started>`_ + * `Purpose and Features <#purpose-and-features>`_ * `Documentation <#documentation>`_ @@ -24,6 +26,15 @@ It includes a remote control layer to control boards connected to other hosts. * `Install Development State <#install-development-state>`_ +Getting started +--------------- +There is a tutorial series on the Pengutronix Youtube channel you can follow to +get started: `Labgrid Tutorial Playlist +`_. + +The other starting point is in the `documentation +`__. + Purpose and Features -------------------- The idea behind labgrid is to create an abstraction of the hardware control diff --git a/contrib/README.rst b/contrib/README.rst new file mode 100644 index 000000000..d770cdebd --- /dev/null +++ b/contrib/README.rst @@ -0,0 +1,43 @@ +labgrid-webapp +============== + +labgrid-webapp implements a browser interface to access some of labgrid's +information. + +Quick Start +----------- + +.. code-block:: bash + + $ cd labgrid/ + $ source venv/bin/activate + venv $ pip install -r contrib/requirements-webapp.txt + venv $ ./contrib/labgrid-webapp --help + usage: labgrid-webapp [-h] [--coordinator ADDRESS] [--port PORT] [--proxy PROXY] + + Labgrid webapp + + options: + -h, --help show this help message and exit + --coordinator ADDRESS, -x ADDRESS + Coordinator address as HOST[:PORT] (default: 127.0.0.1:20408) + --port PORT Port to serve on + --proxy PROXY, -P PROXY + + venv $ ./contrib/labgrid-webapp + INFO: Available routes: + INFO: - /labgrid/graph + INFO: Started server process [2378028] + INFO: Waiting for application startup. + INFO: Application startup complete. + INFO: Uvicorn running on http://0.0.0.0:8800 (Press CTRL+C to quit) + ... + +Please note that the graph feature relies on a valid `graphviz` system +installation. See http://graphviz.org/download/ + +By default the application will start on port 8800. + +To see the graph, go to http://0.0.0.0:8800/labgrid/graph + +See http://0.0.0.0:8800/docs for more information on available endpoints. diff --git a/contrib/completion/labgrid-client.bash b/contrib/completion/labgrid-client.bash index d8bd59abf..81b6883c0 100644 --- a/contrib/completion/labgrid-client.bash +++ b/contrib/completion/labgrid-client.bash @@ -2,14 +2,14 @@ # options top level and subcommands support _labgrid_shared_options="--help" -_labgrid_main_opts_with_value="@(-x|--crossbar|-c|--config|-p|--place|-s|--state|-i|--initial-state|-P|--proxy)" +_labgrid_main_opts_with_value="@(-x|--coordinator|-c|--config|-p|--place|-s|--state|-i|--initial-state|-P|--proxy)" # Parses labgrid-client arguments # Sets arg to subcommand, excluding options and their values. # Sets last_arg_opt_with_value to true if the last argument is an option requiring a value, else # false. # Sets base_cmd to the labgrid-client base command up to subcommand and removes trailing -# option requiring a value - useful to call 'labgrid-client complete' with place/crossbar/proxy set +# option requiring a value - useful to call 'labgrid-client complete' with place/coordinator/proxy set # Before calling this function, make sure arg, base_cmd and last_arg_opt_with_value are local _labgrid_parse_args() { @@ -769,6 +769,40 @@ _labgrid_client_write_image() esac } +_labgrid_client_write_files() +{ + local cur prev words cword + _init_completion || return + + case "$prev" in + -w|--wait) + ;& + -p|--partition) + ;& + -t|--target-directory) + ;& + -T) + ;& + -n|--name) + _labgrid_complete match-names "$cur" + return + ;; + esac + + case "$cur" in + -*) + local options="--wait --partition --target-directory --name $_labgrid_shared_options" + COMPREPLY=( $(compgen -W "$options" -- "$cur") ) + ;; + *) + local args + _labgrid_count_args "@(-w|--wait|-p|--partition|-t|--target-directory|-T|-n|--name)" || return + + _filedir + ;; + esac +} + _labgrid_client_reserve() { _labgrid_client_generic_subcommand "--wait --shell --prio" @@ -833,7 +867,7 @@ _labgrid_client() case "$cur" in --*) # top level args completion - local options="--crossbar \ + local options="--coordinator \ --config \ --place \ --state \ @@ -888,6 +922,7 @@ _labgrid_client() audio \ tmc \ write-image \ + write-files \ reserve \ cancel-reservation \ wait \ diff --git a/contrib/coordinator-statsd.py b/contrib/coordinator-statsd.py index 1d1ec5c44..f8ea0254b 100755 --- a/contrib/coordinator-statsd.py +++ b/contrib/coordinator-statsd.py @@ -42,12 +42,14 @@ import sys import argparse -import statsd import os -import labgrid.remote.client import time import asyncio -import txaio + +from labgrid.remote.client import start_session, Error +from labgrid.remote.generated import labgrid_coordinator_pb2 +from labgrid.remote.common import Reservation +import statsd def inc_gauge(gauges, key): @@ -56,12 +58,13 @@ def inc_gauge(gauges, key): async def report_reservations(session, tags, gauges): - reservations = await session.call("org.labgrid.coordinator.get_reservations") + request = labgrid_coordinator_pb2.GetReservationsRequest() - for token, config in reservations.items(): - state = config["state"] + response = await session.stub.GetReservations(request) + reservations = [Reservation.from_pb2(x) for x in response.reservations] - groups = config.get("filters", {}) + for reservation in reservations: + groups = reservation.filters if not groups: groups = {"": {}} @@ -72,7 +75,7 @@ async def report_reservations(session, tags, gauges): ".".join( ["reservations", group_name] + [group.get(t, "") for t in tags] - + [state] + + [reservation.state.name] ), ) @@ -94,10 +97,10 @@ def main(): ) parser.add_argument( "-x", - "--crossbar", - metavar="URL", - help="Crossbar URL for the coordinator", - default=os.environ.get("LG_CROSSBAR", "ws://127.0.0.1:20408/ws"), + "--coordinator", + metavar="ADDRESS", + help="Coordinator address as HOST[:PORT]. Default is %(default)s", + default=os.environ.get("LG_COORDINATOR", "127.0.0.1:20408"), ) parser.add_argument( "--period", @@ -142,8 +145,8 @@ def main(): args = parser.parse_args() - txaio.use_asyncio() - txaio.config.loop = asyncio.get_event_loop() + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) statsd_client = None gauges = {} @@ -175,20 +178,18 @@ def main(): next_time = time.monotonic() + args.period try: - extra = {} - session = labgrid.remote.client.start_session( - args.crossbar, - os.environ.get("LG_CROSSBAR_REALM", "realm1"), - extra, - ) - - session.loop.run_until_complete( - asyncio.gather( - report_places(session, args.tags, gauges), - report_reservations(session, args.tags, gauges), + session = start_session(args.coordinator, loop=loop) + try: + loop.run_until_complete( + asyncio.gather( + report_places(session, args.tags, gauges), + report_reservations(session, args.tags, gauges), + ) ) - ) - except labgrid.remote.client.Error as e: + finally: + loop.run_until_complete(session.stop()) + loop.run_until_complete(session.close()) + except Error as e: print(f"Error communicating with labgrid: {e}") continue diff --git a/contrib/labgrid-webapp b/contrib/labgrid-webapp new file mode 100755 index 000000000..e78976ad1 --- /dev/null +++ b/contrib/labgrid-webapp @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +import argparse +import asyncio +import logging +import os +import sys +from typing import Dict + +import graphviz +import uvicorn +from fastapi import FastAPI +from fastapi.responses import Response + +from labgrid.remote.client import ClientSession, start_session +from labgrid.remote.common import Place +from labgrid.resource import Resource +from labgrid.util.proxy import proxymanager + + +async def do_graph(session: ClientSession) -> bytes: + '''Generate a graphviz graph of the current configuration. + + Graph displays: + - all resources, grouped by groupname and exporter. + - all places, with a list of tags + - solid edges between places and acquired resources + - dotted edges between places and unacquired resources + - edges between resources and places carry the match name if any. + ''' + def res_node_attr(name: str, resource: Resource) -> Dict[str, str]: + return { + 'shape': 'plaintext', + 'label': f'''< + + + + + + + + +
Resource
{resource.cls}{name}
>''', + } + + def place_node_attr(name: str, place: Place) -> Dict[str, str]: + acquired = '' + bgcolor = 'lightblue' + if place.acquired: + bgcolor = 'cornflowerblue' + acquired = f'{place.acquired}' + + tags = 'Tags' if place.tags else '' + for k, v in place.tags.items(): + tags += f'{k}={v}' + + return { + 'shape': 'plaintext', + 'label': f'''< + + + + {acquired} + + + + + {tags} +
Place
{name}
>''', + } + + g = graphviz.Digraph('G') + g.attr(rankdir='LR') + + paths = {} + for exporter, groups in session.resources.items(): + g_exporter = graphviz.Digraph(f'cluster_{exporter}') + g_exporter.attr(label=exporter) + + for group, resources in groups.items(): + g_group = graphviz.Digraph(f'cluster_{group}') + g_group.attr(label=group) + + for r_name, entry in resources.items(): + res_node = f'{exporter}/{group}/{entry.cls}/{r_name}'.replace(':', '_') + paths[res_node] = [exporter, group, entry.cls, r_name] + g_group.node(res_node, **res_node_attr(r_name, entry)) + + g_exporter.subgraph(g_group) + + g.subgraph(g_exporter) + + for p_node, place in session.places.items(): + g.node(p_node, **place_node_attr(p_node, place)) + + for m in place.matches: + for node, p in paths.items(): + if m.ismatch(p): + g.edge( + f'{node}:name', p_node, + style='solid' if place.acquired else 'dotted', + label=m.rename if m.rename else None, + ) + + return g.pipe(format='svg') + + +def main(): + app = FastAPI() + logger = logging.getLogger('uvicorn') + + @app.get('/labgrid/graph') + async def get_graph() -> str: + '''Show a graph of the current infrastructure.''' + svg = await do_graph(session) + return Response(content=svg, media_type='image/svg+xml') + + parser = argparse.ArgumentParser( + description='Labgrid webapp', + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument( + '--coordinator', + '-x', + metavar='URL', + default=os.environ.get('LG_COORDINATOR', '127.0.0.1:20408'), + help='Coordinator address as HOST[:PORT] (default: %(default)s)', + ) + parser.add_argument('--port', type=int, default=8800, help='Port to serve on') + parser.add_argument('--proxy', '-P', help='Proxy connections via given ssh host') + + args = parser.parse_args() + + if args.proxy: + proxymanager.force_proxy(args.proxy) + + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + try: + session = start_session( + args.coordinator, + loop=loop, + ) + except ConnectionRefusedError: + logger.fatal('Unable to connect to labgrid coordinator') + return + + server = uvicorn.Server(config=uvicorn.Config( + loop=loop, + host='0.0.0.0', + port=args.port, + app=app, + )) + + logger.info('Available routes:') + for route in app.routes: + reserved_routes = ['/openapi.json', '/docs', '/docs/oauth2-redirect', '/redoc'] + if route.path not in reserved_routes: + logger.info(f' - {route.path}') + + try: + loop.run_until_complete(server.serve()) + finally: + loop.run_until_complete(session.stop()) + loop.run_until_complete(session.close()) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/contrib/requirements-webapp.txt b/contrib/requirements-webapp.txt new file mode 100644 index 000000000..539b76d5e --- /dev/null +++ b/contrib/requirements-webapp.txt @@ -0,0 +1,3 @@ +fastapi +graphviz +uvicorn diff --git a/contrib/sync-places.py b/contrib/sync-places.py index 2406bd4f9..94ea15734 100755 --- a/contrib/sync-places.py +++ b/contrib/sync-places.py @@ -15,8 +15,10 @@ # limitations under the License. import argparse +import asyncio from contextlib import contextmanager from labgrid.remote.client import start_session +from labgrid.remote.generated import labgrid_coordinator_pb2 from labgrid.util.proxy import proxymanager import os import sys @@ -51,25 +53,37 @@ async def do_sync(session, args): for name in remove_places: print(f"Removing place {name}") if not args.dry_run: - await session.call("org.labgrid.coordinator.del_place", name) + request = labgrid_coordinator_pb2.DeletePlaceRequest(name=name) + await session.stub.DeletePlace(request) + await session.sync_with_coordinator() + changed = True for name in config["places"]: if not name in seen_places: print(f"Adding place {name}") if not args.dry_run: - await session.call("org.labgrid.coordinator.add_place", name) + request = labgrid_coordinator_pb2.AddPlaceRequest(name=name) + await session.stub.AddPlace(request) + await session.sync_with_coordinator() + changed = True for name in config["places"]: - matches = config["places"][name].get("matches", []) + matches = [] + for m in config["places"][name].get("matches", []): + if isinstance(m, dict): + match = list(m.keys())[0] + matches.append((match, m[match])) + else: + matches.append((m, None)) + seen_matches = set() remove_matches = set() place_tags = {} if name in seen_places: place = session.places[name] - for m in place.matches: - m = repr(m) + for m in [(repr(x), x.rename) for x in place.matches]: if m in matches: seen_matches.add(m) else: @@ -77,23 +91,38 @@ async def do_sync(session, args): place_tags = place.tags for m in remove_matches: - print(f"Deleting match '{m}' for place {name}") + match, rename = m + if rename: + print(f"Deleting named match '{match} -> {rename}' for place {name}") + else: + print(f"Deleting match '{match}' for place {name}") if not args.dry_run: - await session.call( - "org.labgrid.coordinator.del_place_match", name, m - ) + request = labgrid_coordinator_pb2.DeletePlaceMatchRequest(placename=name, pattern=match) + await session.stub.DeletePlaceMatch(request) + await session.sync_with_coordinator() + changed = True for m in matches: if not m in seen_matches: - print(f"Adding match '{m}' for place {name}") + match, rename = m + if rename: + print(f"Adding named match '{match} -> {rename}' for place {name}") + else: + print(f"Adding match '{match}' for place {name}") + if not args.dry_run: - await session.call( - "org.labgrid.coordinator.add_place_match", name, m - ) + request = labgrid_coordinator_pb2.AddPlaceMatchRequest(placename=name, pattern=match, rename=rename) + await session.stub.AddPlaceMatch(request) + await session.sync_with_coordinator() changed = True tags = config["places"][name].get("tags", {}).copy() + for k, v in tags.items(): + if not isinstance(k, str) or not isinstance(v, str): + del(tags[k]) + tags[str(k)] = str(v) + if place_tags != tags: print( "Setting tags for place %s to %s" @@ -111,16 +140,20 @@ async def do_sync(session, args): tags[k] = "" if not args.dry_run: - await session.call( - "org.labgrid.coordinator.set_place_tags", name, tags - ) + request = labgrid_coordinator_pb2.SetPlaceTagsRequest(placename=name, tags=tags) + await session.stub.SetPlaceTags(request) + await session.sync_with_coordinator() + changed = True async def do_dump(session, args): config = {"places": {}} for name, place in session.places.items(): config["places"][name] = { - "matches": [repr(m) for m in place.matches], + "matches": [ + {repr(m): m.rename} if m.rename else repr(m) + for m in place.matches + ], "tags": {k: v for k, v in place.tags.items()}, } @@ -139,6 +172,7 @@ async def do_dump(session, args): my-place1: # Replace with your place matches: # A list of match patterns. Replace with your match patterns - "*/my-place1/*" + - "exporter/my-place1/resource": name # named matches supported tags: # A dictionary of key/value tags. Replace with your tags board: awesomesauce bar: baz @@ -150,11 +184,11 @@ async def do_dump(session, args): formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument( - "--crossbar", + "--coordinator", "-x", - metavar="URL", - default=os.environ.get("LG_CROSSBAR", "ws://127.0.0.1:20408/ws"), - help="Crossbar websocket URL (default: %(default)s)", + metavar="ADDRESS", + default=os.environ.get("LG_COORDINATOR", "127.0.0.1:20408"), + help="Coordinator address as HOST[:PORT] (default: %(default)s)", ) parser.add_argument("--proxy", "-P", help="Proxy connections via given ssh host") @@ -195,11 +229,19 @@ async def do_dump(session, args): if args.proxy: proxymanager.force_proxy(args.proxy) + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + session = start_session( - args.crossbar, os.environ.get("LG_CROSSBAR_REALM", "realm1"), {} + args.coordinator, + loop=loop, ) - return session.loop.run_until_complete(args.func(session, args)) + try: + return loop.run_until_complete(args.func(session, args)) + finally: + loop.run_until_complete(session.stop()) + loop.run_until_complete(session.close()) if __name__ == "__main__": diff --git a/contrib/systemd/labgrid-coordinator.service b/contrib/systemd/labgrid-coordinator.service index 2dc5d117e..c701038c6 100644 --- a/contrib/systemd/labgrid-coordinator.service +++ b/contrib/systemd/labgrid-coordinator.service @@ -4,12 +4,12 @@ After=network.target [Service] Environment="PYTHONUNBUFFERED=1" -# labgrid's .crossbar/config-anonymous.yaml serves as an example -ExecStart=/path/to/labgrid-coordinator/venv/bin/crossbar start --logformat=syslogd --cbdir /var/lib/labgrid-coordinator --config /etc/labgrid/coordinator.yaml -ExecStop=/usr/bin/labgrid-coordinator stop --cbdir /var/lib/labgrid-coordinator +ExecStart=/path/to/labgrid/venv/bin/labgrid-coordinator Restart=on-failure DynamicUser=yes StateDirectory=labgrid-coordinator +# Set WorkingDirectory to StateDirectory, this works in DynamicUser mode since symlinks are created +WorkingDirectory=%S/labgrid-coordinator [Install] WantedBy=multi-user.target diff --git a/contrib/systemd/labgrid-exporter.service b/contrib/systemd/labgrid-exporter.service index a896aeeae..10cbfff26 100644 --- a/contrib/systemd/labgrid-exporter.service +++ b/contrib/systemd/labgrid-exporter.service @@ -5,7 +5,7 @@ Wants=network-online.target [Service] Environment="PYTHONUNBUFFERED=1" -# Should contain LG_CROSSBAR configuration +# Should contain LG_COORDINATOR configuration EnvironmentFile=-/etc/environment ExecStart=/path/to/labgrid/venv/bin/labgrid-exporter /etc/labgrid/exporter.yaml Restart=on-failure diff --git a/crossbar-requirements.txt b/crossbar-requirements.txt deleted file mode 100644 index d361d83d9..000000000 --- a/crossbar-requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -crossbar==21.3.1 -autobahn<=22.4.1 diff --git a/debian/changelog b/debian/changelog index 4b9ee3cb4..092c58225 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,14 @@ -labgrid (23.1.0) UNRELEASED; urgency=low +labgrid (25.0.0) UNRELEASED; urgency=low * See https://github.com/labgrid-project/labgrid/blob/master/CHANGES.rst - -- Bastian Krause Wed, 26 Apr 2023 16:06:25 +0200 + -- Bastian Krause Fri, 21 Jan 2024 10:43:45 +0100 + +labgrid (24.0.0) UNRELEASED; urgency=low + + * See https://github.com/labgrid-project/labgrid/blob/master/CHANGES.rst + + -- Bastian Krause Wed, 01 Jan 2024 14:06:25 +0100 labgrid (23.0.0) UNRELEASED; urgency=low diff --git a/debian/copyright b/debian/copyright index e921d88cb..7583beffb 100644 --- a/debian/copyright +++ b/debian/copyright @@ -3,12 +3,12 @@ Upstream-Name: labgrid Source: https://github.com/labgrid-project/labgrid Files: * -Copyright: Copyright (C) 2016-2023 Pengutronix, Jan Luebbe - Copyright (C) 2016-2023 Pengutronix, Rouven Czerwinski +Copyright: Copyright (C) 2016-2025 Pengutronix, Jan Luebbe + Copyright (C) 2016-2025 Pengutronix, Rouven Czerwinski License: LGPL-2.1+ Files: man/* -Copyright: Copyright (C) 2016-2023 Pengutronix +Copyright: Copyright (C) 2016-2025 Pengutronix License: LGPL-2.1+ License: LGPL-2.1+ diff --git a/debian/labgrid-coordinator b/debian/labgrid-coordinator new file mode 100755 index 000000000..54a34440b --- /dev/null +++ b/debian/labgrid-coordinator @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /opt/venvs/labgrid/bin/labgrid-coordinator "$@" diff --git a/debian/labgrid.install b/debian/labgrid.install index 87e162fa8..2a1d9725d 100755 --- a/debian/labgrid.install +++ b/debian/labgrid.install @@ -1,8 +1,10 @@ #!/usr/bin/dh-exec debian/labgrid.yaml /etc debian/labgrid-client /usr/bin +debian/labgrid-coordinator /usr/bin debian/labgrid-exporter /usr/bin debian/labgrid-pytest /usr/bin debian/labgrid-suggest /usr/bin helpers/labgrid-bound-connect /usr/sbin +helpers/labgrid-raw-interface /usr/sbin contrib/completion/labgrid-client.bash => /usr/share/bash-completion/completions/labgrid-client diff --git a/debian/labgrid.manpages b/debian/labgrid.manpages index eb6e10245..b72c77540 100644 --- a/debian/labgrid.manpages +++ b/debian/labgrid.manpages @@ -1,4 +1,5 @@ man/labgrid-client.1 +man/labgrid-coordinator.1 man/labgrid-exporter.1 man/labgrid-suggest.1 man/labgrid-device-config.5 diff --git a/debian/rules b/debian/rules index 7c1532c6c..cd09b0c5d 100755 --- a/debian/rules +++ b/debian/rules @@ -13,4 +13,4 @@ override_dh_virtualenv: --upgrade-pip \ --extras deb \ --extra-pip-arg='--no-binary' \ - --extra-pip-arg='cffi,numpy' + --extra-pip-arg='cffi' diff --git a/doc/RELEASE.rst b/doc/RELEASE.rst index da1b2f41d..2bf33fcc9 100644 --- a/doc/RELEASE.rst +++ b/doc/RELEASE.rst @@ -1,101 +1,65 @@ Step by step guide to releasing a new labgrid version. +labgrid follows the `calver `_ versioning scheme +``YY.MINOR[.MICRO]``. +The ``MINOR`` number starts at 0. +The ``MICRO`` number for stable releases starts at 1. -0. Preparations -=============== -Clean the `dist/` directory: - -.. code-block:: bash - - rm dist/* - -Check your commit mail and name: - -.. code-block:: bash +1. Check for relevant PRs that need a merge +=========================================== - git config --get user.name - git config --get user.email +- `Milestones `_ +- `Fixes `_ +- `Fixes for stable `_ -1. Update CHANGES.rst +2. Update CHANGES.rst ===================== Update the `CHANGES.rst` file. -Ensure that no incompatiblities are unlisted and that all major features are +Ensure that no incompatibilities are unlisted and that all major features are described in a separate section. It's best to compare against the git log. -2. Bump Version Number -====================== +Add new sections including the version number for the release in `CHANGES.rst` +(if not already done). +Set the release date. -Bump the version number in `CHANGES.rst`. +If you are bumping the ``MINOR`` number, import the changes from the latest stable +branch and add a new (unreleased) section for the next release. +Also add a new section into ``debian/changelog``. -3. Create a signed Tag -====================== - -Create a signed tag of the new release. -Your PGP-key has to be available on the computer. - -.. code-block:: bash - - git tag -s - -4. Create sdist +3. Create a tag =============== -Run the following command: - -:: +Wait for the CI to succeed on the commit you are about to tag. - pip install build - python -m build --sdist +Now create a (signed) tag of the new release. +If it should be signed (``-s``), your PGP-key has to be available on the +computer. +The release tag should start with a lower case ``v``, e.g. ``v24.0`` or +``v24.0.1``. -The sdist file will be available in the `dist/` directory. - -5. Test upload to pypi dev -========================== - -Test the upload by using twine to upload to pypi test service - -:: - - twine upload --repository-url https://test.pypi.org/legacy/ dist/* - -6. Test download from pypi dev -============================== - -Test the upload by using pypi dev as a download source - -:: - - virtualenv -p python3 labgrid-crossbar-release- - labgrid-crossbar-release-/bin/pip install --upgrade pip - labgrid-crossbar-release-/bin/pip install -r crossbar-requirements.txt - - virtualenv -p python3 labgrid-release- - source labgrid-release-/bin/activate - pip install --upgrade pip setuptools wheel - pip install --index-url https://test.pypi.org/simple/ labgrid - -And optionally run the tests: - -:: +.. code-block:: bash - pip install ".[dev]" - pytest tests --crossbar-venv labgrid-crossbar-release- + git tag -s $VERSION -7. Upload to pypi -================= +If you're happy with it, push it: -Upload the tested dist file to pypi. +.. code-block:: bash -:: + git push upstream $VERSION - twine upload dist/* +The CI should take care of the rest. +Make sure it succeeds and the new release is available on PyPi. -8. Upload the signed tag -======================== +4. Draft a release +================== -Upload the signed tag to the upstream repository +On GitHub, draft a new release, add the changes in Markdown format and create a +discussion for the release: +https://github.com/labgrid-project/labgrid/releases/new -:: +5. Create new stable branch +=========================== - git push upstream +If you are bumping the ``MINOR`` number, push a new stable branch +``stable-YY.MINOR`` based on the release tag. diff --git a/doc/conf.py b/doc/conf.py index 5061f4333..0d8b8aca7 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -57,7 +57,7 @@ # General information about the project. project = 'labgrid' -copyright = '2016-2023 Pengutronix, Jan Luebbe and Rouven Czerwinski' +copyright = '2016-2025 Pengutronix, Jan Luebbe and Rouven Czerwinski' author = 'Jan Luebbe, Rouven Czerwinski' # The version info for the project you're documenting, acts as replacement for @@ -95,9 +95,6 @@ # html_theme = 'sphinx_rtd_theme' -# Set correct html_path for rtd theme: -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. @@ -182,18 +179,9 @@ 'special-members': True, } autodoc_mock_imports = ['onewire', - 'txaio', - 'autobahn', - 'autobahn.asyncio', - 'autobahn.asyncio.wamp', - 'autobahn.wamp', - 'autobahn.wamp.types', - 'autobahn.twisted', - 'autobahn.twisted.wamp', - 'autobahn.wamp.exception', - 'twisted.internet.defer', 'gi', - 'gi.repository',] + 'gi.repository', + 'vxi11'] # -- Options for autosection ---------------------------------------------- autosectionlabel_prefix_document = True diff --git a/doc/configuration.rst b/doc/configuration.rst index f859dbec8..bafd332bd 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -8,8 +8,8 @@ have no dependencies. .. image:: res/config_graph.svg :width: 50% -Here the resource `RawSerialPort` provides the information for the -`SerialDriver`, which in turn is needed by the `ShellDriver`. +Here the resource `RawSerialPort`_ provides the information for the +`SerialDriver`_, which in turn is needed by the `ShellDriver`_. Driver dependency resolution is done by searching for the driver which implements the dependent protocol, all drivers implement one or more protocols. @@ -21,19 +21,19 @@ Serial Ports RawSerialPort +++++++++++++ -A RawSerialPort is a serial port which is identified via the device path on the -local computer. +A :any:`RawSerialPort` is a serial port which is identified via the device path +on the local computer. Take note that re-plugging USB serial converters can result in a different enumeration order. .. code-block:: yaml RawSerialPort: - port: /dev/ttyUSB0 + port: '/dev/ttyUSB0' speed: 115200 -The example would access the serial port /dev/ttyUSB0 on the local computer with -a baud rate of 115200. +The example would access the serial port ``/dev/ttyUSB0`` on the local computer +with a baud rate of ``115200``. Arguments: - port (str): path to the serial device @@ -44,18 +44,20 @@ Used by: NetworkSerialPort +++++++++++++++++ -A NetworkSerialPort describes a serial port which is exported over the network, -usually using RFC2217 or raw tcp. +A :any:`NetworkSerialPort` describes a serial port which is exported over the +network, usually using `RFC2217 `_ +or raw TCP. .. code-block:: yaml NetworkSerialPort: - host: remote.example.computer + host: 'remote.example.computer' port: 53867 speed: 115200 -The example would access the serial port on computer remote.example.computer via -port 53867 and use a baud rate of 115200 with the RFC2217 protocol. +The example would access the serial port on computer +``remote.example.computer`` via port ``53867`` and use a baud rate of +``115200`` with the RFC2217 protocol. Arguments: - host (str): hostname of the remote host @@ -69,7 +71,7 @@ Used by: ModbusRTU +++++++++ -Describes the resource required to use the ModbusRTU driver. +A :any:`ModbusRTU` resource is required to use the `ModbusRTUDriver`_. `Modbus RTU `_ is a communication protocol used to control many different kinds of electronic systems, such as thermostats, power plants, etc. @@ -77,49 +79,45 @@ Modbus is normally implemented on top of RS-485, though this is not strictly necessary, as long as the Modbus network only has one master (and up to 256 slaves). -The labgrid driver is implemented using the minimalmodbus Python library. -The implementation only supports that labgrid will be the master on the Modbus -network. -For more information, see `minimalmodbus -`_. - -This resource and driver only supports local usage and will not work with an -exporter. +This resource only supports local usage and will not work with an exporter. .. code-block:: yaml ModbusRTU: - port: "/dev/ttyUSB0" + port: '/dev/ttyUSB0' address: 16 speed: 115200 timeout: 0.25 Arguments: - - port (str): tty the instrument is connected to, e.g. '/dev/ttyUSB0' + - port (str): tty the instrument is connected to, e.g. ``/dev/ttyUSB0`` - address (int): slave address on the modbus, e.g. 16 - speed (int, default=115200): baud rate of the serial port - - timeout (float, default=0.25): optional, timeout in seconds + - timeout (float, default=0.25): timeout in seconds Used by: - `ModbusRTUDriver`_ USBSerialPort +++++++++++++ -A USBSerialPort describes a serial port which is connected via USB and is -identified by matching udev properties. +A :any:`USBSerialPort` describes a serial port which is connected via USB and +is identified by matching udev properties. This allows identification through hot-plugging or rebooting. .. code-block:: yaml USBSerialPort: match: - ID_SERIAL_SHORT: P-00-00682 + ID_SERIAL_SHORT: 'P-00-00682' + ID_USB_INTERFACE_NUM: '00' speed: 115200 -The example would search for a USB serial converter with the key -`ID_SERIAL_SHORT` and the value `P-00-00682` and use it with a baud rate -of 115200. -The `ID_SERIAL_SHORT` property is set by the usb_id builtin helper program. +The example would search for a USB serial converter with a given serial number +(``ID_SERIAL_SHORT`` = ``P-00-00682``) and use first interface +(``ID_USB_INTERFACE_NUM`` = ``00``) with a baud rate of 115200. + +The ``ID_SERIAL_SHORT`` and ``ID_USB_INTERFACE_NUM`` properties are set by the +``usb_id`` builtin helper program. Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ @@ -133,70 +131,85 @@ Power Ports NetworkPowerPort ++++++++++++++++ -A NetworkPowerPort describes a remotely switchable power port. +A :any:`NetworkPowerPort` describes a remotely switchable power port. .. code-block:: yaml NetworkPowerPort: - model: gude - host: powerswitch.example.computer + model: 'gude' + host: 'powerswitch.example.computer' index: 0 The example describes port 0 on the remote power switch -`powerswitch.example.computer`, which is a `gude` model. +``powerswitch.example.computer``, which is a ``gude`` model. Arguments: - model (str): model of the power switch - host (str): hostname of the power switch - index (int): number of the port to switch -The `model` property selects one of several `backend implementations +The ``model`` property selects one of several `backend implementations `_. Currently available are: ``apc`` - Controls an APU PDU via SNMP. + Controls *APU PDUs* via SNMP. ``digipower`` - Controls a DigiPower PDU via a simple HTTP API. + Controls *DigiPower PDUs* via a simple HTTP API. ``digitalloggers_http`` - Control a Digital Loggers PDU that use the legacy HTTP API. Note that + Controls *Digital Loggers PDUs* that use the legacy HTTP API. Note that host argument must include the protocol, such as ``http://192.168.0.3`` or ``http://admin:pass@192.168.0.4``. +``digitalloggers_restapi`` + Controls *Digital Loggers PDUs* that use the REST API. Note that + host argument must include the protocol, such as + ``http://192.168.0.3`` or ``https://admin:pass@192.168.0.4``. + By default, only authenticated users may access the REST API. + HTTPS queries intentially ignore ssl certificate validation, since + the as-shipped certificate is self-signed. + ``eaton`` - Controls Eaton ePDUs via SNMP. + Controls *Eaton ePDUs* via SNMP. ``eg_pms2_network`` - Controls the EG_PMS2_LAN & EG_PMS2_WLAN devices, through simple HTTP POST and - GET requests. The device requires a password for logging into the control - interface, this module deliberately uses the standard password '1' and is - not compatible with a different password. + Controls *EG_PMS2_LAN* and *EG_PMS2_WLAN* devices, through simple HTTP POST + and GET requests. The device requires a password for logging into the + control interface, this module deliberately uses the standard password ``1`` + and is not compatible with a different password. + +``eth008`` + Controls *Robot-Electronics eth008* via a simple HTTP API. ``gude`` - Controls a Gude PDU via a simple HTTP API. + Controls *Gude PDUs* via a simple HTTP API. ``gude24`` - Controls a Gude Expert Power Control 8008 PDU via a simple HTTP API. + Controls *Gude Expert Power Control 8008 PDUs* via a simple HTTP API. ``gude8031`` - Controls a Gude Expert Power Control 8031 PDU via a simple HTTP API. + Controls *Gude Expert Power Control 8031 PDUs* and *Gude Expert Power Control 87-1210-18 PDUs* via a simple HTTP API. ``gude8225`` - Controls a Gude Expert Power Control 8225 PDU via a simple HTTP API. + Controls *Gude Expert Power Control 8225 PDUs* via a simple HTTP API. ``gude8316`` - Controls a Gude Expert Power Control 8316 PDU via a simple HTTP API. + Controls *Gude Expert Power Control 8316 PDUs* via a simple HTTP API. + +``mfi_mpower`` + Controls the *Ubiquity mFi mPower* Power Strip with Ethernet and Wi-Fi connectivity via HTTP. + Tested on a mFi mPower Pro EU device. ``netio`` - Controls a NETIO 4-Port PDU via a simple HTTP API. + Controls *NETIO 4-Port PDUs* via a simple HTTP API. ``netio_kshell`` - Controls a NETIO 4C PDU via a Telnet interface. + Controls *NETIO 4C PDUs* via a Telnet interface. ``raritan`` - Controls Raritan PDUs via SNMP. + Controls *Raritan PDUs* via SNMP. ``rest`` This is a generic backend for PDU implementations which can be controlled via @@ -206,17 +219,17 @@ Currently available are: for details. ``sentry`` - Controls a Sentry PDU via SNMP using Sentry3-MIB. - It was tested on CW-24VDD and 4805-XLS-16. + Controls *Sentry PDUs* via SNMP using Sentry3-MIB. + It was tested on *CW-24VDD* and *4805-XLS-16*. ``shelly_gen1`` - Controls relays of Shelly devices using the Gen 1 Device API. + Controls relays of *Shelly* devices using the Gen 1 Device API. See the `docstring in the module `__ for details. ``siglent`` - Controls Siglent SPD3000X series modules via the `vxi11 Python module + Controls *Siglent SPD3000X* series modules via the `vxi11 Python module `_. ``simplerest`` @@ -227,15 +240,27 @@ Currently available are: for details. ``tplink`` - Controls TP-Link power strips via `python-kasa + Controls *TP-Link power strips* via `python-kasa `_. +``tinycontrol`` + Controls a tinycontrol.eu IP Power Socket via HTTP. + It was tested on the *6G10A v2* model. + `Manual `__ + +``poe_mib`` + Controls PoE switches using the PoE SNMP administration MiBs. + +``ubus`` + Controls *PoE switches* running OpenWrt using the *ubus* interface. + Further information available at + Used by: - `NetworkPowerDriver`_ PDUDaemonPort +++++++++++++ -A PDUDaemonPort describes a PDU port accessible via `PDUDaemon +A :any:`PDUDaemonPort` describes a PDU port accessible via `PDUDaemon `_. As one PDUDaemon instance can control many PDUs, the instance name from the PDUDaemon configuration file needs to be specified. @@ -243,12 +268,12 @@ PDUDaemon configuration file needs to be specified. .. code-block:: yaml PDUDaemonPort: - host: pduserver - pdu: apc-snmpv3-noauth + host: 'pduserver' + pdu: 'apc-snmpv3-noauth' index: 1 -The example describes port 1 on the PDU configured as `apc-snmpv3-noauth`, with -PDUDaemon running on the host `pduserver`. +The example describes port ``1`` on the PDU configured as +``apc-snmpv3-noauth``, with PDUDaemon running on the host ``pduserver``. Arguments: - host (str): name of the host running the PDUDaemon @@ -260,17 +285,18 @@ Used by: YKUSHPowerPort ++++++++++++++ -A YKUSHPowerPort describes a YEPKIT YKUSH USB (HID) switchable USB hub. +A :any:`YKUSHPowerPort` describes a *YEPKIT YKUSH* USB (HID) switchable USB +hub. .. code-block:: yaml YKUSHPowerPort: - serial: YK12345 + serial: 'YK12345' index: 1 The example describes port 1 on the YKUSH USB hub with the -serial "YK12345". -(use "pykush -l" to get your serial...) +serial ``YK12345``. +Use ``ykushcmd -l`` to get your serial number. Arguments: - serial (str): serial number of the YKUSH hub @@ -279,20 +305,25 @@ Arguments: Used by: - `YKUSHPowerDriver`_ +NetworkYKUSHPowerPort ++++++++++++++++++++++ +A :any:`NetworkYKUSHPowerPort` describes a `YKUSHPowerPort`_ available on a +remote computer. + USBPowerPort ++++++++++++ -A USBPowerPort describes a generic switchable USB hub as supported by +A :any:`USBPowerPort` describes a generic switchable USB hub as supported by `uhubctl `_. .. code-block:: yaml USBPowerPort: match: - ID_PATH: pci-0000:00:14.0-usb-0:2:1.0 + ID_PATH: 'pci-0000:00:14.0-usb-0:2:1.0' index: 1 The example describes port 1 on the hub with the ID_PATH -"pci-0000:00:14.0-usb-0:2:1.0". +``pci-0000:00:14.0-usb-0:2:1.0``. (use ``udevadm info /sys/bus/usb/devices/...`` to find the ID_PATH value) Arguments: @@ -303,7 +334,7 @@ Used by: - `USBPowerDriver`_ .. note:: - Labgrid requires that the interface is contained in the ID_PATH. + labgrid requires that the interface is contained in the ID_PATH. This usually means that the ID_PATH should end with ``:1.0``. Only this first interface is registered with the ``hub`` driver labgrid is looking for, paths without the interface will fail to match since they use @@ -311,18 +342,18 @@ Used by: SiSPMPowerPort ++++++++++++++ -A SiSPMPowerPort describes a GEMBIRD SiS-PM as supported by +A :any:`SiSPMPowerPort` describes a *GEMBIRD SiS-PM* as supported by `sispmctl `_. .. code-block:: yaml SiSPMPowerPort: match: - ID_PATH: platform-1c1a400.usb-usb-0:2 + ID_PATH: 'platform-1c1a400.usb-usb-0:2' index: 1 The example describes port 1 on the hub with the ID_PATH -"platform-1c1a400.usb-usb-0:2". +``platform-1c1a400.usb-usb-0:2``. Arguments: - index (int): number of the port to switch @@ -333,19 +364,19 @@ Used by: TasmotaPowerPort ++++++++++++++++ -A :any:`TasmotaPowerPort` resource describes a switchable Tasmota power outlet -accessed over MQTT. +A :any:`TasmotaPowerPort` resource describes a switchable `Tasmota +`_ power outlet accessed over *MQTT*. .. code-block:: yaml TasmotaPowerPort: - host: this.is.an.example.host.com - status_topic: stat/tasmota_575A2B/POWER - power_topic: cmnd/tasmota_575A2B/POWER - avail_topic: tele/tasmota_575A2B/LWT + host: 'this.is.an.example.host.com' + status_topic: 'stat/tasmota_575A2B/POWER' + power_topic: 'cmnd/tasmota_575A2B/POWER' + avail_topic: 'tele/tasmota_575A2B/LWT' -The example uses a mosquitto server at "this.is.an.example.host.com" and has the -topics setup for a tasmota power port that has the ID 575A2B. +The example uses a *Mosquitto* server at ``this.is.an.example.host.com`` and +has the topics setup for a Tasmota power port that has the ID ``575A2B``. Arguments: - host (str): hostname of the MQTT server @@ -363,20 +394,20 @@ Digital Outputs ModbusTCPCoil +++++++++++++ -A ModbusTCPCoil describes a coil accessible via ModbusTCP. +A :any:`ModbusTCPCoil` describes a coil accessible via *Modbus TCP*. .. code-block:: yaml ModbusTCPCoil: - host: "192.168.23.42" + host: '192.168.23.42' coil: 1 -The example describes the coil with the address 1 on the ModbusTCP device -`192.168.23.42`. +The example describes the coil with the address ``1`` on the Modbus TCP device +``192.168.23.42``. Arguments: - - host (str): hostname of the Modbus TCP server e.g. "192.168.23.42:502" - - coil (int): index of the coil e.g. 3 + - host (str): hostname of the Modbus TCP server e.g. ``192.168.23.42:502`` + - coil (int): index of the coil, e.g. ``3`` - invert (bool, default=False): whether the logic level is inverted (active-low) - write_multiple_coils (bool, default=False): whether to perform write @@ -387,7 +418,7 @@ Used by: DeditecRelais8 ++++++++++++++ -A DeditecRelais8 describes a Deditec USB GPO module with 8 relays. +A :any:`DeditecRelais8` describes a *Deditec USB GPO module* with 8 relays. .. code-block:: yaml @@ -395,7 +426,7 @@ A DeditecRelais8 describes a Deditec USB GPO module with 8 relays. index: 1 invert: false match: - ID_PATH: pci-0000:00:14.0-usb-0:2:1.0 + ID_PATH: 'pci-0000:00:14.0-usb-0:2:1.0' Arguments: - index (int): number of the relay to use @@ -408,20 +439,20 @@ Used by: OneWirePIO ++++++++++ -A OneWirePIO describes a onewire programmable I/O pin. +A :any:`OneWirePIO` describes a *1-Wire* programmable I/O pin. .. code-block:: yaml OneWirePIO: - host: example.computer - path: /29.7D6913000000/PIO.0 + host: 'example.computer' + path: '/29.7D6913000000/PIO.0' invert: false -The example describes a `PIO.0` at device address `29.7D6913000000` via the onewire -server on `example.computer`. +The example describes a ``PIO.0`` at device address ``29.7D6913000000`` via the +1-Wire server on ``example.computer``. Arguments: - - host (str): hostname of the remote system running the onewire server + - host (str): hostname of the remote system running the 1-Wire server - path (str): path on the server to the programmable I/O pin - invert (bool, default=False): whether the logic level is inverted (active-low) @@ -436,13 +467,13 @@ An :any:`LXAIOBusPIO` resource describes a single PIO pin on an LXAIOBusNode. .. code-block:: yaml LXAIOBusPIO: - host: localhost:8080 - node: IOMux-00000003 - pin: OUT0 - invert: False + host: 'localhost:8080' + node: 'IOMux-00000003' + pin: 'OUT0' + invert: false -The example uses an lxa-iobus-server running on localhost:8080, with node -IOMux-00000003 and pin OUT0. +The example uses an lxa-iobus-server running on ``localhost:8080``, with node +``IOMux-00000003`` and pin ``OUT0``. Arguments: - host (str): hostname with port of the lxa-io-bus server @@ -455,21 +486,21 @@ Used by: NetworkLXAIOBusPIO ++++++++++++++++++ -A NetworkLXAIOBusPIO describes an `LXAIOBusPIO`_ exported over the network. +A :any:`NetworkLXAIOBusPIO` describes an `LXAIOBusPIO`_ exported over the network. HIDRelay ++++++++ -An :any:`HIDRelay` resource describes a single output of a HID protocol based +An :any:`HIDRelay` resource describes a single output of an HID protocol based USB relays. -It currently supports the widely used "dcttech USBRelay". +It currently supports the widely used *dcttech USBRelay* and *lctech LCUS* .. code-block:: yaml HIDRelay: index: 2 - invert: False + invert: false match: - ID_PATH: pci-0000:00:14.0-usb-0:2:1.0 + ID_PATH: 'pci-0000:00:14.0-usb-0:2:1.0' Arguments: - index (int, default=1): number of the relay to use @@ -479,22 +510,97 @@ Arguments: Used by: - `HIDRelayDriver`_ +HttpDigitalOutput ++++++++++++++++++ +An :any:`HttpDigitalOutput` resource describes a generic digital output that +can be controlled via HTTP. + +.. code-block:: yaml + + HttpDigitalOutput: + url: 'http://host.example/some/endpoint' + body_asserted: 'On' + body_deasserted: 'Off' + +The example assumes a simple scenario where the same URL is used for PUT +requests that set the output state and GET requests to get the current state. +It also assumes that the returned state matches either "On" or "Off" exactly. + +The `HttpDigitalOutputDriver`_ also supports more advanced use cases where the +current state is fetched from another URL and is interpreted using regular +expressions. + +Arguments: + - url (str): URL to use for setting a new state + - body_asserted (str): request body to send to assert the output + - body_deasserted (str): request body to send to de-assert the output + - method (str, default="PUT"): HTTP method to set a new state + + - url_get (str): optional, URL to use instead of ``url`` for getting the state + - body_get_asserted (str): optional, regular expression that matches an asserted response body + - body_get_deasserted (str): optional, regular expression that matches a de-asserted response body + +Used by: + - `HttpDigitalOutputDriver`_ + NetworkHIDRelay +++++++++++++++ -A NetworkHIDRelay describes an `HIDRelay`_ exported over the network. +A :any:`NetworkHIDRelay` describes an `HIDRelay`_ exported over the network. + +SysfsGPIO ++++++++++ + +A :any:`SysfsGPIO` resource describes a GPIO line. + +.. code-block:: yaml + + SysfsGPIO: + index: 12 + +Arguments: + - index (int): index of the GPIO line + +Used by: + - `GpioDigitalOutputDriver`_ + +MatchedSysfsGPIO +++++++++++++++++ +A :any:`MatchedSysfsGPIO` describes a GPIO line, like a `SysfsGPIO`_. +The gpiochip is identified by matching udev properties. This allows +identification through hot-plugging or rebooting for controllers like +USB based gpiochips. + +.. code-block:: yaml + + MatchedSysfsGPIO: + match: + '@SUBSYSTEM': 'usb' + '@ID_SERIAL_SHORT': 'D38EJ8LF' + pin: 0 + +The example would search for a USB gpiochip with the key `ID_SERIAL_SHORT` +and the value `D38EJ8LF` and use the pin 0 of this device. +The `ID_SERIAL_SHORT` property is set by the usb_id builtin helper program. + +Arguments: + - match (dict): key and value pairs for a udev match, see `udev Matching`_ + - pin (int): gpio pin number within the matched gpiochip. + +Used by: + - `GpioDigitalOutputDriver`_ NetworkService ~~~~~~~~~~~~~~ -A NetworkService describes a remote SSH connection. +A :any:`NetworkService` describes a remote SSH connection. .. code-block:: yaml NetworkService: - address: example.computer - username: root + address: 'example.computer' + username: 'root' -The example describes a remote SSH connection to the computer `example.computer` -with the username `root`. +The example describes a remote SSH connection to the computer +``example.computer`` with the username ``root``. Set the optional password password property to make SSH login with a password instead of the key file. @@ -516,13 +622,22 @@ Used by: USBMassStorage ~~~~~~~~~~~~~~ -A USBMassStorage resource describes a USB memory stick or similar device. +A :any:`USBMassStorage` resource describes a USB memory stick or similar +device. .. code-block:: yaml USBMassStorage: match: - ID_PATH: pci-0000:06:00.0-usb-0:1.3.2:1.0-scsi-0:0:0:3 + ID_PATH: 'pci-0000:06:00.0-usb-0:1.3.2:1.0-scsi-0:0:0:3' + +Writing images to disk requires installation of ``dd`` or optionally +``bmaptool`` on the same system as the block device. + +For mounting the file system and writing into it, +`PyGObject `_ must be installed. +For Debian, the necessary packages are `python3-gi` and `gir1.2-udisks-2.0`. +This is not required for writing images to disks. Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ @@ -532,43 +647,43 @@ Used by: NetworkUSBMassStorage ~~~~~~~~~~~~~~~~~~~~~ -A NetworkUSBMassStorage resource describes a USB memory stick or similar +A :any:`NetworkUSBMassStorage` resource describes a USB memory stick or similar device available on a remote computer. -Used by: - - `USBStorageDriver`_ - The NetworkUSBMassStorage can be used in test cases by calling the -`write_image()`, and `get_size()` functions. +``write_files()``, ``write_image()``, and ``get_size()`` functions. SigrokDevice ~~~~~~~~~~~~ -A SigrokDevice resource describes a sigrok device. To select a specific device -from all connected supported devices use the `SigrokUSBDevice`_. +A :any:`SigrokDevice` resource describes a *Sigrok* device. To select a +specific device from all connected supported devices use the +`SigrokUSBDevice`_. .. code-block:: yaml SigrokDevice: - driver: fx2lafw - channels: "D0=CLK,D1=DATA" + driver: 'fx2lafw' + channels: 'D0=CLK,D1=DATA' Arguments: - driver (str): name of the sigrok driver to use - - channels (str): optional, channel mapping as described in the sigrok-cli - man page + - channels (str): optional, channel mapping as described in the + ``sigrok-cli`` man page + - channel_group (str): optional, channel group as described in the + ``sigrok-cli`` man page Used by: - `SigrokDriver`_ IMXUSBLoader ~~~~~~~~~~~~ -An IMXUSBLoader resource describes a USB device in the imx loader state. +An :any:`IMXUSBLoader` resource describes a USB device in the imx loader state. .. code-block:: yaml IMXUSBLoader: match: - ID_PATH: pci-0000:06:00.0-usb-0:1.3.2:1.0 + ID_PATH: 'pci-0000:06:00.0-usb-0:1.3.2:1.0' Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ @@ -576,33 +691,37 @@ Arguments: Used by: - `IMXUSBDriver`_ - `UUUDriver`_ + - `BDIMXUSBDriver`_ MXSUSBLoader ~~~~~~~~~~~~ -An MXSUSBLoader resource describes a USB device in the mxs loader state. +An :any:`MXSUSBLoader` resource describes a USB device in the *MXS loader +state*. .. code-block:: yaml MXSUSBLoader: match: - ID_PATH: pci-0000:06:00.0-usb-0:1.3.2:1.0 + ID_PATH: 'pci-0000:06:00.0-usb-0:1.3.2:1.0' Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ Used by: - `MXSUSBDriver`_ + - `IMXUSBDriver`_ - `UUUDriver`_ RKUSBLoader -~~~~~~~~~~~~ -An RKUSBLoader resource describes a USB device in the rockchip loader state. +~~~~~~~~~~~ +An :any:`RKUSBLoader` resource describes a USB device in the *Rockchip loader +state*. .. code-block:: yaml RKUSBLoader: match: - ID_PATH: pci-0000:06:00.0-usb-0:1.3.2:1.0 + ID_PATH: 'pci-0000:06:00.0-usb-0:1.3.2:1.0' Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ @@ -612,19 +731,23 @@ Used by: NetworkMXSUSBLoader ~~~~~~~~~~~~~~~~~~~ -A NetworkMXSUSBLoader describes an `MXSUSBLoader`_ available on a remote computer. +A :any:`NetworkMXSUSBLoader` describes an `MXSUSBLoader`_ available on a remote +computer. NetworkIMXUSBLoader ~~~~~~~~~~~~~~~~~~~ -A NetworkIMXUSBLoader describes an `IMXUSBLoader`_ available on a remote computer. +A :any:`NetworkIMXUSBLoader` describes an `IMXUSBLoader`_ available on a remote +computer. NetworkRKUSBLoader -~~~~~~~~~~~~~~~~~~~ -A NetworkRKUSBLoader describes an `RKUSBLoader`_ available on a remote computer. +~~~~~~~~~~~~~~~~~~ +A :any:`NetworkRKUSBLoader` describes an `RKUSBLoader`_ available on a remote +computer. AndroidUSBFastboot ~~~~~~~~~~~~~~~~~~ -An AndroidUSBFastboot resource describes a USB device in the fastboot state. +An :any:`AndroidUSBFastboot` resource describes a USB device in the *Fastboot +state*. Previously, this resource was named AndroidFastboot and this name still supported for backwards compatibility. @@ -632,7 +755,7 @@ supported for backwards compatibility. AndroidUSBFastboot: match: - ID_PATH: pci-0000:06:00.0-usb-0:1.3.2:1.0 + ID_PATH: 'pci-0000:06:00.0-usb-0:1.3.2:1.0' Arguments: - usb_vendor_id (str, default="1d6b"): USB vendor ID to be compared with the @@ -646,12 +769,13 @@ Used by: AndroidNetFastboot ~~~~~~~~~~~~~~~~~~ -An AndroidNetFastboot resource describes a network device in fastboot state. +An :any:`AndroidNetFastboot` resource describes a network device in *Fastboot +state*. .. code-block:: yaml AndroidNetFastboot: - address: "192.168.23.42" + address: '192.168.23.42' Arguments: - address (str): ip address of the fastboot device @@ -665,14 +789,14 @@ Used by: DFUDevice ~~~~~~~~~ -A DFUDevice resource describes a USB device in DFU (Device Firmware Upgrade) -mode. +A :any:`DFUDevice` resource describes a USB device in DFU (Device Firmware +Upgrade) mode. .. code-block:: yaml DFUDevice: match: - ID_PATH: pci-0000:06:00.0-usb-0:1.3.2:1.0 + ID_PATH: 'pci-0000:06:00.0-usb-0:1.3.2:1.0' Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ @@ -682,45 +806,53 @@ Used by: NetworkInterface ~~~~~~~~~~~~~~~~ -A NetworkInterface resource describes a network adapter (such as Ethernet or -WiFi) +A :any:`NetworkInterface` resource describes a network adapter (such as +Ethernet or WiFi) .. code-block:: yaml NetworkInterface: - ifname: eth0 + ifname: 'eth0' Arguments: - ifname (str): name of the interface +Used by: + - `NetworkInterfaceDriver`_ + - `RawNetworkInterfaceDriver`_ + USBNetworkInterface -~~~~~~~~~~~~~~~~~~~~ -A USBNetworkInterface resource describes a USB network adapter (such as +~~~~~~~~~~~~~~~~~~~ +A :any:`USBNetworkInterface` resource describes a USB network adapter (such as Ethernet or WiFi) .. code-block:: yaml USBNetworkInterface: match: - ID_PATH: pci-0000:06:00.0-usb-0:1.3.2:1.0 + ID_PATH: 'pci-0000:06:00.0-usb-0:1.3.2:1.0' Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ +Used by: + - `NetworkInterfaceDriver`_ + - `RawNetworkInterfaceDriver`_ + RemoteNetworkInterface ~~~~~~~~~~~~~~~~~~~~~~ -A :any:`RemoteNetworkInterface` resource describes a :any:`NetworkInterface` or -:any:`USBNetworkInterface` resource available on a remote computer. +A :any:`RemoteNetworkInterface` resource describes a `NetworkInterface`_ or +`USBNetworkInterface`_ resource available on a remote computer. AlteraUSBBlaster ~~~~~~~~~~~~~~~~ -An AlteraUSBBlaster resource describes an Altera USB blaster. +An :any:`AlteraUSBBlaster` resource describes an Altera USB blaster. .. code-block:: yaml AlteraUSBBlaster: match: - ID_PATH: pci-0000:06:00.0-usb-0:1.3.2:1.0 + ID_PATH: 'pci-0000:06:00.0-usb-0:1.3.2:1.0' Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ @@ -731,14 +863,14 @@ Used by: USBDebugger ~~~~~~~~~~~ -An USBDebugger resource describes a JTAG USB adapter (for example an FTDI -FT2232H). +A :any:`USBDebugger` resource describes a JTAG USB adapter (for example an +*FTDI FT2232H*). .. code-block:: yaml USBDebugger: match: - ID_PATH: pci-0000:00:10.0-usb-0:1.4 + ID_PATH: 'pci-0000:00:10.0-usb-0:1.4' Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ @@ -748,35 +880,40 @@ Used by: SNMPEthernetPort ~~~~~~~~~~~~~~~~ -A SNMPEthernetPort resource describes a port on an Ethernet switch, which is -accessible via SNMP. +A :any:`SNMPEthernetPort` resource describes a port on an Ethernet switch, +which is accessible via SNMP. .. code-block:: yaml SNMPEthernetPort: - switch: "switch-012" - interface: "17" + switch: 'switch-012' + interface: '17' Arguments: - switch (str): host name of the Ethernet switch - interface (str): interface name +Used by: + - None + SigrokUSBDevice ~~~~~~~~~~~~~~~ -A SigrokUSBDevice resource describes a sigrok USB device. +A :any:`SigrokUSBDevice` resource describes a *Sigrok* USB device. .. code-block:: yaml SigrokUSBDevice: - driver: fx2lafw - channels: "D0=CLK,D1=DATA" + driver: 'fx2lafw' + channels: 'D0=CLK,D1=DATA' match: - ID_PATH: pci-0000:06:00.0-usb-0:1.3.2:1.0 + ID_PATH: 'pci-0000:06:00.0-usb-0:1.3.2:1.0' Arguments: - driver (str): name of the sigrok driver to use - - channels (str): optional, channel mapping as described in the sigrok-cli - man page + - channels (str): optional, channel mapping as described in the + ``sigrok-cli`` man page + - channel_group (str): optional, channel group as described in the + ``sigrok-cli`` man page - match (dict): key and value pairs for a udev match, see `udev Matching`_ Used by: @@ -784,30 +921,34 @@ Used by: NetworkSigrokUSBDevice ~~~~~~~~~~~~~~~~~~~~~~ -A NetworkSigrokUSBDevice resource describes a sigrok USB device connected to a -host which is exported over the network. The SigrokDriver will access it via SSH. +A :any:`NetworkSigrokUSBDevice` resource describes a *Sigrok* USB device +connected to a host which is exported over the network. +The `SigrokDriver`_ will access it via SSH. SigrokUSBSerialDevice ~~~~~~~~~~~~~~~~~~~~~ -A SigrokUSBSerialDevice resource describes a sigrok device which communicates -of a USB serial port instead of being a USB device itself (see -`SigrokUSBDevice` for that case). +A :any:`SigrokUSBSerialDevice` resource describes a *Sigrok* device which +communicates over a USB serial port instead of being a USB device itself (see +`SigrokUSBDevice`_ for that case). .. code-block:: yaml SigrokUSBSerialDevice: - driver: manson-hcs-3xxx + driver: 'manson-hcs-3xxx' match: - '@ID_SERIAL_SHORT': P-00-02389 + '@ID_SERIAL_SHORT': 'P-00-02389' Arguments: - driver (str): name of the sigrok driver to use - - channels (str): optional, channel mapping as described in the sigrok-cli - man page + - channels (str): optional, channel mapping as described in the + ``sigrok-cli`` man page + - channel_group (str): optional, channel group as described in the + ``sigrok-cli`` man page - match (dict): key and value pairs for a udev match, see `udev Matching`_ Used by: - `SigrokPowerDriver`_ + - `SigrokDmmDriver`_ USBSDMuxDevice ~~~~~~~~~~~~~~ @@ -819,29 +960,29 @@ device. USBSDMuxDevice: match: - '@ID_PATH': pci-0000:00:14.0-usb-0:1.2 + '@ID_PATH': 'pci-0000:00:14.0-usb-0:1.2' Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ Used by: - - `USBSDMUXDriver`_ + - `USBSDMuxDriver`_ + - `USBStorageDriver`_ NetworkUSBSDMuxDevice ~~~~~~~~~~~~~~~~~~~~~ - A :any:`NetworkUSBSDMuxDevice` resource describes a `USBSDMuxDevice`_ available on a remote computer. LXAUSBMux ~~~~~~~~~ -A :any:`LXAUSBMux` resource describes a Linux Automation GmbH USB-Mux device. +An :any:`LXAUSBMux` resource describes a *Linux Automation GmbH USB-Mux* device. .. code-block:: yaml LXAUSBMux: match: - '@ID_PATH': pci-0000:00:14.0-usb-0:1.2 + '@ID_PATH': 'pci-0000:00:14.0-usb-0:1.2' Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ @@ -851,45 +992,42 @@ Used by: NetworkLXAUSBMux ~~~~~~~~~~~~~~~~ - -A :any:`NetworkLXAUSBMux` resource describes a `LXAUSBMux`_ available on a +A :any:`NetworkLXAUSBMux` resource describes an `LXAUSBMux`_ available on a remote computer. USBSDWireDevice ~~~~~~~~~~~~~~~ A :any:`USBSDWireDevice` resource describes a Tizen -`SD Wire device `_ -device. +`SD Wire device `_. .. code-block:: yaml USBSDWireDevice: match: - '@ID_PATH': pci-0000:00:14.0-usb-0:1.2 + '@ID_PATH': 'pci-0000:00:14.0-usb-0:1.2' Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ Used by: - `USBSDWireDriver`_ + - `USBStorageDriver`_ NetworkUSBSDWireDevice ~~~~~~~~~~~~~~~~~~~~~~ - A :any:`NetworkUSBSDWireDevice` resource describes a `USBSDWireDevice`_ available on a remote computer. USBVideo ~~~~~~~~ - A :any:`USBVideo` resource describes a USB video camera which is supported by a -Video4Linux2 kernel driver. +Video4Linux2 (v4l2) kernel driver. .. code-block:: yaml USBVideo: match: - '@ID_PATH': pci-0000:00:14.0-usb-0:1.2 + '@ID_PATH': 'pci-0000:00:14.0-usb-0:1.2' Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ @@ -897,31 +1035,13 @@ Arguments: Used by: - `USBVideoDriver`_ -SysfsGPIO -~~~~~~~~~ - -A :any:`SysfsGPIO` resource describes a GPIO line. - -.. code-block:: yaml - - SysfsGPIO: - index: 12 - -Arguments: - - index (int): index of the GPIO line - -Used by: - - `GpioDigitalOutputDriver`_ - NetworkUSBVideo ~~~~~~~~~~~~~~~ - -A :any:`NetworkUSBVideo` resource describes a :any:`USBVideo` resource available +A :any:`NetworkUSBVideo` resource describes a `USBVideo`_ resource available on a remote computer. USBAudioInput ~~~~~~~~~~~~~ - A :any:`USBAudioInput` resource describes a USB audio input which is supported by an ALSA kernel driver. @@ -929,11 +1049,11 @@ by an ALSA kernel driver. USBAudioInput: match: - ID_PATH: pci-0000:00:14.0-usb-0:3:1.0 + ID_PATH: 'pci-0000:00:14.0-usb-0:3:1.0' Arguments: - index (int, default=0): ALSA PCM device number (as in - `hw:CARD=,DEV=`) + ``hw:CARD=,DEV=``) - match (dict): key and value pairs for a udev match, see `udev Matching`_ Used by: @@ -941,23 +1061,20 @@ Used by: NetworkUSBAudioInput ~~~~~~~~~~~~~~~~~~~~ - -A :any:`NetworkUSBAudioInput` resource describes a :any:`USBAudioInput` resource +A :any:`NetworkUSBAudioInput` resource describes a `USBAudioInput`_ resource available on a remote computer. USBTMC ~~~~~~ - -A :any:`USBTMC` resource describes an oscilloscope connected via the USB TMC -protocol. -The low-level communication is handled by the ``usbtmc`` kernel driver. - +A :any:`USBTMC` resource describes an oscilloscope connected via the *USB TMC +protocol*. +The low-level communication is handled by the "usbtmc" kernel driver. .. code-block:: yaml USBTMC: match: - '@ID_PATH': pci-0000:00:14.0-usb-0:1.2 + '@ID_PATH': 'pci-0000:00:14.0-usb-0:1.2' Arguments: - match (dict): key and value pairs for a udev match, see `udev Matching`_ @@ -973,14 +1090,15 @@ Used by: NetworkUSBTMC ~~~~~~~~~~~~~ - -A :any:`NetworkUSBTMC` resource describes a :any:`USBTMC` resource available +A :any:`NetworkUSBTMC` resource describes a `USBTMC`_ resource available on a remote computer. Flashrom ~~~~~~~~ -A Flashrom resource is used to configure the parameters to a local installed flashrom instance. -It is assumed that flashrom is installed on the host and the executable is configured in: +A :any:`Flashrom` resource is used to configure the parameters to a local +installed flashrom instance. +It is assumed that flashrom is installed on the host and the executable is +configured in: .. code-block:: yaml @@ -988,12 +1106,13 @@ It is assumed that flashrom is installed on the host and the executable is confi flashrom: '/usr/sbin/flashrom' Arguments: - - programmer (str): programmer device as described in `-p, --programmer` in - `man 8 flashrom` + - programmer (str): programmer device as described in ``-p, --programmer`` in + ``man 8 flashrom`` -The resource must configure which programmer to use and the parameters to the programmer. -The programmer parameter is passed directly to the flashrom bin hence man(8) flashrom -can be used for reference. +The resource must configure which programmer to use and the parameters to the +programmer. +The programmer parameter is passed directly to the flashrom bin hence +``man 8 flashrom`` can be used for reference. Below an example where the local spidev is used. .. code-block:: yaml @@ -1004,15 +1123,16 @@ Below an example where the local spidev is used. Used by: - `FlashromDriver`_ -NetworkFlashRom +NetworkFlashrom ~~~~~~~~~~~~~~~ -A NetworkFlashrom describes a `Flashrom`_ available on a remote computer. +A :any:`NetworkFlashrom` describes a `Flashrom`_ available on a remote computer. USBFlashableDevice ~~~~~~~~~~~~~~~~~~ -Represents an "opaque" USB device used by custom flashing programs. There is -usually not anything useful that can be done with the interface other than -running a flashing program with `FlashScriptDriver`_. +A :any:`USBFlashableDevice` represents an "opaque" USB device used by custom +flashing programs. +There is usually not anything useful that can be done with the interface other +than running a flashing program with `FlashScriptDriver`_. .. note:: This resource is only intended to be used as a last resort when it is @@ -1022,7 +1142,7 @@ running a flashing program with `FlashScriptDriver`_. USBFlashableDevice: match: - SUBSYSTEM: usb + SUBSYSTEM: 'usb' ID_SERIAL: '1234' Arguments: @@ -1033,17 +1153,15 @@ Used by: NetworkUSBFlashableDevice ~~~~~~~~~~~~~~~~~~~~~~~~~ -A :any:`NetworkUSBFlashableDevice` resource describes a :any:`USBFlashableDevice` +A :any:`NetworkUSBFlashableDevice` resource describes a `USBFlashableDevice`_ resource available on a remote computer -Used by: - - `FlashScriptDriver`_ - DediprogFlasher ~~~~~~~~~~~~~~~ -A DediprogFlasher resource is used to configure the parameters to a locally installed -dpmcd instance. It is assumed that dpcmd is installed on the host and the -executable can be configured via: +A :any:`DediprogFlasher` resource is used to configure the parameters to a +locally installed dpmcd instance. +It is assumed that dpcmd is installed on the host and the executable can be +configured via: .. code-block:: yaml @@ -1051,32 +1169,33 @@ executable can be configured via: dpcmd: '/usr/sbin/dpcmd' Arguments: - - vcc (str): '3.5V', '2.5V' or '1.8V'. + - vcc (str): ``3.5V``, ``2.5V`` or ``1.8V``. -For instance, to flash using 3.5V vcc: +For instance, to flash using 3.5 V VCC: .. code-block:: yaml DediprogFlasher: vcc: '3.5V' - Used by: - `DediprogFlashDriver`_ NetworkDediprogFlasher ~~~~~~~~~~~~~~~~~~~~~~ -A NetworkDediprogFlasher describes a `DediprogFlasher`_ available on a remote computer. +A :any:`NetworkDediprogFlasher` describes a `DediprogFlasher`_ available on a +remote computer. XenaManager ~~~~~~~~~~~ -A XenaManager resource describes a Xena Manager instance which is the instance the -`XenaDriver`_ must connect to in order to configure a Xena chassis. +A :any:`XenaManager` resource describes a *Xena Manager* instance which is the +instance the `XenaDriver`_ must connect to in order to configure a Xena +chassis. .. code-block:: yaml XenaManager: - hostname: "example.computer" + hostname: 'example.computer' Arguments: - hostname (str): hostname or IP of the management address of the Xena tester @@ -1086,33 +1205,34 @@ Used by: PyVISADevice ~~~~~~~~~~~~ -A PyVISADevice resource describes a test stimuli device controlled by PyVISA. +A :any:`PyVISADevice` resource describes a test stimuli device controlled by +PyVISA. Such device could be a signal generator. .. code-block:: yaml PyVISADevice: - type: "TCPIP" - url: "192.168.110.11" + type: 'TCPIP' + url: '192.168.110.11' Arguments: - - type (str): device resource type following the pyVISA resource syntax, e.g. + - type (str): device resource type following the PyVISA resource syntax, e.g. ASRL, TCPIP... - url (str): device identifier on selected resource, e.g. for TCPIP resource + - backend (str): optional, Visa library backend, e.g. '@sim' for pyvisa-sim backend Used by: - `PyVISADriver`_ HTTPVideoStream ~~~~~~~~~~~~~~~ - -A :any:`HTTPVideoStream` resource describes a IP video stream over HTTP or HTTPS. +An :any:`HTTPVideoStream` resource describes an IP video stream over HTTP or HTTPS. .. code-block:: yaml HTTPVideoStream: - url: http://192.168.110.11/0.ts + url: 'http://192.168.110.11/0.ts' Arguments: - url (str): URI of the IP video stream @@ -1120,6 +1240,27 @@ Arguments: Used by: - `HTTPVideoDriver`_ +USBHub +~~~~~~ + +A :any:`USBHub` resource describes an USB hub. +There is no corresponding driver, as this resource is only useful to monitor +whether the expected USB hubs are detected by an exporter. +To control individual ports, use `USBPowerPort`_. + +.. code-block:: yaml + + USBHub: + match: + ID_PATH: 'pci-0000:02:00.0-usb-0:4:1.0' + + +Arguments: + - match (dict): key and value pairs for a udev match, see `udev Matching`_ + +Used by: + - none + Providers ~~~~~~~~~ Providers describe directories that are accessible by the target over a @@ -1127,65 +1268,133 @@ specific protocol. This is useful for software installation in the bootloader (via TFTP) or downloading update artifacts under Linux (via HTTP). -They are used with the ManagedFile helper, which ensures that the file is -available on the server and then creates a symlink from the internal directory -to the uploaded file. +They are used with the :any:`ManagedFile` helper, which ensures that the file +is available on the server. For HTTP and TFTP, a symlink from the internal +directory to the uploaded file is created. The path for the target is generated by replacing the internal prefix with the external prefix. +For NFS, it is assumed that ``/var/cache/labgrid`` is exported. +The information required for mounting and accessing staged files are returned, +see below. For now, the TFTP/NFS/HTTP server needs to be configured before using it from labgrid. -.. _TFTPProvider: -.. _NFSProvider: -.. _HTTPProvider: - -TFTPProvider / NFSProvider / HTTPProvider -+++++++++++++++++++++++++++++++++++++++++ +TFTPProvider +++++++++++++ +A :any:`TFTPProvider` resource describes a TFTP server. .. code-block:: yaml TFTPProvider: - internal: "/srv/tftp/board-23/" - external: "board-23/" + internal: '/srv/tftp/board-23/' + external: 'board-23/' + +Arguments: + - internal (str): path prefix to the local directory accessible by the target + - external (str): corresponding path prefix for use by the target + +Used by: + - `TFTPProviderDriver`_ + +HTTPProvider +++++++++++++ +An :any:`HTTPProvider` resource describes an HTTP server. + +.. code-block:: yaml HTTPProvider: - internal: "/srv/www/board-23/" - external: "http://192.168.1.1/board-23/" + internal: '/srv/www/board-23/' + external: 'http://192.168.1.1/board-23/' Arguments: - internal (str): path prefix to the local directory accessible by the target - external (str): corresponding path prefix for use by the target Used by: - - `TFTPProviderDriver`_ - - `NFSProviderDriver`_ - `HTTPProviderDriver`_ -.. _RemoteTFTPProvider: -.. _RemoteNFSProvider: -.. _RemoteHTTPProvider: +NFSProvider ++++++++++++ +An :any:`NFSProvider` resource describes an NFS server. + +.. code-block:: yaml -RemoteTFTPProvider / RemoteNFSProvider / RemoteHTTPProvider -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -These describe a `TFTPProvider`_, `NFSProvider`_ or `HTTPProvider`_ resource -available on a remote computer + NFSProvider: {} + +Arguments: + - None Used by: - - `TFTPProviderDriver`_ - `NFSProviderDriver`_ + +RemoteTFTPProvider +++++++++++++++++++ +A :any:`RemoteTFTPProvider` describes a `TFTPProvider`_ resource available on +a remote computer. + +.. code-block:: yaml + + RemoteTFTPProvider + host: 'tftphost' + internal: '/srv/tftp/board-23/' + external: 'board-23/' + +Arguments: + - host (str): hostname of the remote host + - internal (str): path prefix to the TFTP root directory on ``host`` + - external (str): corresponding path prefix for use by the target + +Used by: + - `TFTPProviderDriver`_ + +RemoteHTTPProvider +++++++++++++++++++ +A :any:`RemoteHTTPProvider` describes an `HTTPProvider`_ resource available on +a remote computer. + +.. code-block:: yaml + + RemoteHTTPProvider: + host: 'httphost' + internal: '/srv/www/board-23/' + external: 'http://192.168.1.1/board-23/' + +Arguments: + - host (str): hostname of the remote host + - internal (str): path prefix to the HTTP root directory on ``host`` + - external (str): corresponding path prefix for use by the target + +Used by: - `HTTPProviderDriver`_ +RemoteNFSProvider ++++++++++++++++++ +A :any:`RemoteNFSProvider` describes an `NFSProvider`_ resource available on a +remote computer. + +.. code-block:: yaml + + RemoteNFSProvider: + host: 'nfshost' + +Arguments: + - host (str): hostname of the remote host + +Used by: + - `NFSProviderDriver`_ + RemotePlace ~~~~~~~~~~~ -A RemotePlace describes a set of resources attached to a labgrid remote place. +A :any:`RemotePlace` describes a set of resources attached to a labgrid remote +place. .. code-block:: yaml RemotePlace: - name: example-place + name: 'example-place' -The example describes the remote place `example-place`. It will connect to the +The example describes the remote place ``example-place``. It will connect to the labgrid remote coordinator, wait until the resources become available and expose them to the internal environment. @@ -1197,32 +1406,32 @@ Used by: DockerDaemon ~~~~~~~~~~~~ -A DockerDaemon describes where to contact a docker daemon process. -DockerDaemon also participates in managing `NetworkService` instances +A :any:`DockerDaemon` describes where to contact a *docker daemon* process. +DockerDaemon also participates in managing `NetworkService`_ instances created through interaction with that daemon. .. code-block:: yaml DockerDaemon: - docker_daemon_url: unix://var/run/docker.sock + docker_daemon_url: 'unix://var/run/docker.sock' The example describes a docker daemon accessible via the -'/var/run/docker.sock' unix socket. When used by a `DockerDriver`, the -`DockerDriver` will first create a docker container which the +``/var/run/docker.sock`` unix socket. When used by a `DockerDriver`_, the +`DockerDriver`_ will first create a docker container which the DockerDaemon resource will subsequently use to create one/more -`NetworkService` instances - as specified by `DockerDriver` configuration. -Each `NetworkService` instance corresponds to a network service running inside +`NetworkService`_ instances - as specified by `DockerDriver`_ configuration. +Each `NetworkService`_ instance corresponds to a network service running inside the container. Moreover, DockerDaemon will remove any hanging containers if DockerDaemon is used several times in a row - as is the case when -executing test suites. Normally `DockerDriver` - when deactivated - +executing test suites. Normally `DockerDriver`_ - when deactivated - cleans up the created docker container; programming errors, keyboard interrupts or unix kill signals may lead to hanging containers, however; therefore auto-cleanup is important. Arguments: - - docker_daemon_url (str): The url of the daemon to use for this target + - docker_daemon_url (str): url of the daemon to use for this target Used by: - `DockerDriver`_ @@ -1231,7 +1440,6 @@ Used by: udev Matching ~~~~~~~~~~~~~ - labgrid allows the exporter (or the client-side environment) to match resources via udev rules. Any udev property key and value can be used, path matching USB devices is @@ -1287,26 +1495,24 @@ use-cases. Matching a USB Serial Converter on a Hub Port +++++++++++++++++++++++++++++++++++++++++++++ - This will match any USB serial converter connected below the hub port 1.2.5.5 on bus 1. -The `ID_PATH` value corresponds to the hierarchy of buses and ports as shown +The ``ID_PATH`` value corresponds to the hierarchy of buses and ports as shown with ``udevadm info /dev/ttyUSB0``. .. code-block:: yaml USBSerialPort: match: - '@ID_PATH': pci-0000:05:00.0-usb-0:1.2.5.5 + '@ID_PATH': 'pci-0000:05:00.0-usb-0:1.2.5.5' Note the ``@`` in the ``@ID_PATH`` match, which applies this match to the device's parents instead of directly to itself. -This is necessary for the `USBSerialPort` because we actually want to find the +This is necessary for the `USBSerialPort`_ because we actually want to find the ``ttyUSB?`` device below the USB serial converter device. Matching an Android USB Fastboot Device +++++++++++++++++++++++++++++++++++++++ - In this case, we want to match the USB device on that port directly, so we don't use a parent match. @@ -1314,14 +1520,13 @@ don't use a parent match. AndroidUSBFastboot: match: - ID_PATH: pci-0000:05:00.0-usb-0:1.2.3 + ID_PATH: 'pci-0000:05:00.0-usb-0:1.2.3' Matching a Specific UART in a Dual-Port Adapter +++++++++++++++++++++++++++++++++++++++++++++++ - On this board, the serial console is connected to the second port of an on-board dual-port USB-UART. -The board itself is connected to the bus 3 and port path 10.2.2.2. +The board itself is connected to the bus 3 and port path ``10.2.2.2``. The correct value can be shown by running ``udevadm info /dev/ttyUSB9`` in our case: @@ -1365,12 +1570,11 @@ We use the ``ID_USB_INTERFACE_NUM`` to distinguish between the two ports: USBSerialPort: match: - '@ID_PATH': pci-0000:05:00.0-usb-2:10.2.2.2' + '@ID_PATH': 'pci-0000:05:00.0-usb-2:10.2.2.2' ID_USB_INTERFACE_NUM: '01' Matching a USB UART by Serial Number ++++++++++++++++++++++++++++++++++++ - Most of the USB serial converters in our lab have been programmed with unique serial numbers. This makes it easy to always match the same one even if the USB topology @@ -1380,7 +1584,7 @@ changes or a board has been moved between host systems. USBSerialPort: match: - ID_SERIAL_SHORT: P-00-03564 + ID_SERIAL_SHORT: 'P-00-03564' To check if your device has a serial number, you can use ``udevadm info``: @@ -1419,8 +1623,8 @@ Drivers SerialDriver ~~~~~~~~~~~~ -A SerialDriver connects to a serial port. It requires one of the serial port -resources. +A :any:`SerialDriver` connects to a serial port. It requires one of the serial +port resources. Binds to: port: @@ -1435,6 +1639,7 @@ Binds to: Implements: - :any:`ConsoleProtocol` + - :any:`ResetProtocol` Arguments: - txdelay (float, default=0.0): time in seconds to wait before sending each byte @@ -1443,21 +1648,33 @@ Arguments: ModbusRTUDriver ~~~~~~~~~~~~~~~ -A ModbusRTUDriver connects to a ModbusRTU resource. This driver only supports -local usage and will not work with an exporter. +A :any:`ModbusRTUDriver` connects to a ModbusRTU resource. This driver only +supports local usage and will not work with an exporter. + +The driver is implemented using the +`minimalmodbus `_ Python +library. +The implementation only supports that labgrid will be the master on the Modbus +network. .. code-block:: yaml ModbusRTUDriver: {} Binds to: - port: + resource: - `ModbusRTU`_ +Implements: + - None (yet) + +Arguments: + - None + ShellDriver ~~~~~~~~~~~ -A ShellDriver binds on top of a `ConsoleProtocol` and is designed to interact -with a login prompt and a Linux shell. +A :any:`ShellDriver` binds on top of a :any:`ConsoleProtocol` and is designed +to interact with a login prompt and a Linux shell. Binds to: console: @@ -1465,19 +1682,21 @@ Binds to: Implements: - :any:`CommandProtocol` + - :any:`FileTransferProtocol` .. code-block:: yaml ShellDriver: prompt: 'root@\w+:[^ ]+ ' login_prompt: ' login: ' - username: root + username: 'root' Arguments: - prompt (regex): shell prompt to match after logging in - login_prompt (regex): match for the login prompt - username (str): username to use during login - - password (str): optional, password to use during login + - password (str): optional, password to use during login. + Can be an empty string. - keyfile (str): optional, keyfile to upload after login, making the `SSHDriver`_ usable - login_timeout (int, default=60): timeout for login prompt detection in @@ -1505,13 +1724,13 @@ Arguments: SSHDriver ~~~~~~~~~ -A SSHDriver requires a `NetworkService` resource and allows the execution of -commands and file upload via network. -It uses SSH's `ServerAliveInterval` option to detect failed connections. +An :any:`SSHDriver` requires a `NetworkService`_ resource and allows the +execution of commands and file upload via network. +It uses SSH's ``ServerAliveInterval`` option to detect failed connections. If a shared SSH connection to the target is already open, it will reuse it when running commands. -In that case, `ServerAliveInterval` should be set outside of labgrid, as it +In that case, ``ServerAliveInterval`` should be set outside of labgrid, as it cannot be enabled for an existing connection. Binds to: @@ -1525,21 +1744,26 @@ Implements: .. code-block:: yaml SSHDriver: - keyfile: example.key + keyfile: 'example.key' Arguments: - keyfile (str): optional, filename of private key to login into the remote system - (has precedence over `NetworkService`'s password) - - stderr_merge (bool, default=False): set to True to make `run()` return stderr merged with + (has precedence over `NetworkService`_'s password) + - stderr_merge (bool, default=False): set to True to make ``run()`` return stderr merged with stdout, and an empty list as second element. - connection_timeout (float, default=30.0): timeout when trying to establish connection to target. - - explicit_sftp_mode (bool, default=False): if set to True, `put()` and `get()` will - explicitly use the SFTP protocol for file transfers instead of scp's default protocol + - explicit_sftp_mode (bool, default=False): if set to True, ``put()``, ``get()``, and ``scp()`` + will explicitly use the SFTP protocol for file transfers instead of scp's default protocol + - explicit_scp_mode (bool, default=False): if set to True, ``put()``, ``get()``, and ``scp()`` + will explicitly use the SCP protocol for file transfers instead of scp's default protocol + - username (str, default=username from `NetworkService`_): username used by SSH + - password (str, default=password from `NetworkService`_): password used by SSH UBootDriver ~~~~~~~~~~~ -A UBootDriver interfaces with a U-Boot bootloader via a `ConsoleProtocol`. +A :any:`UBootDriver` interfaces with a U-Boot bootloader via a +:any:`ConsoleProtocol`. Binds to: console: @@ -1547,33 +1771,38 @@ Binds to: Implements: - :any:`CommandProtocol` + - :any:`LinuxBootProtocol` .. code-block:: yaml UBootDriver: prompt: 'Uboot> ' boot_commands: - net: run netboot - spi: run spiboot + net: 'run netboot' + spi: 'run spiboot' Arguments: - prompt (regex, default=""): U-Boot prompt to match - autoboot (regex, default="stop autoboot"): autoboot message to match - password (str): optional, U-Boot unlock password - - interrupt (str, default="\\n"): string to interrupt autoboot (use "\\x03" for CTRL-C) + - interrupt (str, default="\\n"): string to interrupt autoboot (use ``\\x03`` for CTRL-C) - init_commands (tuple): optional, tuple of commands to execute after matching the prompt - password_prompt (str, default="enter Password: "): regex to match the U-Boot password prompt + - boot_expression (regex, default="[\n]U-Boot 20\\d+"): regex to match the U-Boot start string - bootstring (str): optional, regex to match on Linux Kernel boot - boot_command (str, default="run bootcmd"): boot command for booting target - boot_commands (dict, default={}): boot commands by name for LinuxBootProtocol boot command - login_timeout (int, default=30): timeout for login prompt detection in seconds - boot_timeout (int, default=30): timeout for initial Linux Kernel version detection +Attribute: + - boot_detected (bool, defaul=False): boot_expression seen after driver activation + SmallUBootDriver ~~~~~~~~~~~~~~~~ -A SmallUBootDriver interfaces with stripped-down U-Boot variants that are -sometimes used in cheap consumer electronics. +A :any:`SmallUBootDriver` interfaces with stripped-down *U-Boot* variants that +are sometimes used in cheap consumer electronics. SmallUBootDriver is meant as a driver for U-Boot with only little functionality compared to a standard U-Boot. @@ -1582,7 +1811,7 @@ Especially is copes with the following limitations: - The U-Boot does not have a real password-prompt but can be activated by entering a "secret" after a message was displayed. - The command line does not have a built-in echo command. - Thus this driver uses 'Unknown Command' messages as marker before and after + Thus this driver uses "Unknown Command" messages as marker before and after the output of a command. - Since there is no echo we cannot return the exit code of the command. Commands will always return 0 unless the command was not found. @@ -1602,28 +1831,31 @@ This driver needs the following features activated in U-Boot to work: - The U-Boot must support the "bootm" command to boot from a memory location. Binds to: - - :any:`ConsoleProtocol` (see `SerialDriver`_) + console: + - :any:`ConsoleProtocol` (see `SerialDriver`_) Implements: - :any:`CommandProtocol` + - :any:`LinuxBootProtocol` .. code-block:: yaml SmallUBootDriver: prompt: 'ap143-2\.0> ' boot_expression: 'Autobooting in 1 seconds' - boot_secret: "tpl" + boot_secret: 'tpl' Arguments: - boot_expression (str, default="U-Boot 20\\d+"): regex to match the U-Boot start string - boot_secret (str, default="a"): secret used to unlock prompt + - boot_secret_nolf (bool, default=False): send boot_secret without new line - login_timeout (int, default=60): timeout for password/login prompt detection - for other arguments, see `UBootDriver`_ BareboxDriver ~~~~~~~~~~~~~ - -A BareboxDriver interfaces with a barebox bootloader via a `ConsoleProtocol`. +A :any:`BareboxDriver` interfaces with a *barebox* bootloader via a +:any:`ConsoleProtocol`. Binds to: console: @@ -1631,6 +1863,7 @@ Binds to: Implements: - :any:`CommandProtocol` + - :any:`LinuxBootProtocol` .. code-block:: yaml @@ -1643,13 +1876,21 @@ Arguments: - interrupt (str, default="\\n"): string to interrupt autoboot (use "\\x03" for CTRL-C) - bootstring (regex, default="Linux version \\d"): regex that indicating that the Linux Kernel is booting + - boot_expression (regex, default="[\n]barebox 20\d+"): string that indicates that Barebox is + starting - password (str): optional, password to use for access to the shell - login_timeout (int, default=60): timeout for access to the shell +Attribute: + - boot_detected (bool, defaul=False): boot_expression seen after driver activation + ExternalConsoleDriver ~~~~~~~~~~~~~~~~~~~~~ -An ExternalConsoleDriver implements the `ConsoleProtocol` on top of a command -executed on the local computer. +An :any:`ExternalConsoleDriver` implements the :any:`ConsoleProtocol` on top of +a command executed on the local computer. + +Binds to: + - None Implements: - :any:`ConsoleProtocol` @@ -1666,13 +1907,15 @@ Arguments: AndroidFastbootDriver ~~~~~~~~~~~~~~~~~~~~~ -An AndroidFastbootDriver allows the upload of images to a device in the USB or -network fastboot state. +An :any:`AndroidFastbootDriver` allows the upload of images to a device in the +USB or network *Fastboot state*. Binds to: fastboot: - `AndroidUSBFastboot`_ + - RemoteAndroidUSBFastboot - `AndroidNetFastboot`_ + - RemoteAndroidNetFastboot Implements: - None (yet) @@ -1680,8 +1923,8 @@ Implements: .. code-block:: yaml AndroidFastbootDriver: - boot_image: mylocal.image - sparse_size: 100M + boot_image: 'mylocal.image' + sparse_size: '100M' Arguments: - boot_image (str): optional, image key referring to the image to boot @@ -1694,12 +1937,13 @@ Arguments: DFUDriver ~~~~~~~~~ -A DFUDriver allows the download of images to a device in DFU (Device Firmware -Upgrade) mode. +A :any:`DFUDriver` allows the download of images to a device in DFU (Device +Firmware Upgrade) mode. Binds to: dfu: - `DFUDevice`_ + - NetworkDFUDevice Implements: - None (yet) @@ -1713,7 +1957,8 @@ Arguments: OpenOCDDriver ~~~~~~~~~~~~~ -An OpenOCDDriver controls OpenOCD to bootstrap a target with a bootloader. +An :any:`OpenOCDDriver` controls *OpenOCD* to bootstrap a target with a +bootloader. Note that OpenOCD supports specifying USB paths since `a1b308ab `_ which was released with v0.11. @@ -1726,7 +1971,9 @@ Consider updating your OpenOCD version when using multiple USB Blasters. Binds to: interface: - `AlteraUSBBlaster`_ + - NetworkAlteraUSBBlaster - `USBDebugger`_ + - NetworkUSBDebugger Implements: - :any:`BootstrapProtocol` @@ -1734,14 +1981,14 @@ Implements: .. code-block:: yaml OpenOCDDriver: - config: local-settings.cfg - image: bitstream - interface_config: ftdi/lambdaconcept_ecpix-5.cfg - board_config: lambdaconcept_ecpix-5.cfg + config: 'local-settings.cfg' + image: 'bitstream' + interface_config: 'ftdi/lambdaconcept_ecpix-5.cfg' + board_config: 'lambdaconcept_ecpix-5.cfg' load_commands: - - "init" - - "svf -quiet {filename}" - - "exit" + - 'init' + - 'svf -quiet {filename}' + - 'exit' Arguments: - config (str/list): optional, OpenOCD configuration file(s) @@ -1753,11 +2000,13 @@ Arguments: QuartusHPSDriver ~~~~~~~~~~~~~~~~ -A QuartusHPSDriver controls the "Quartus Prime Programmer and Tools" to flash -a target's QSPI. +A :any:`QuartusHPSDriver` controls the "Quartus Prime Programmer and Tools" to +flash a target's QSPI. Binds to: - - `AlteraUSBBlaster`_ + interface: + - `AlteraUSBBlaster`_ + - NetworkAlteraUSBBlaster Implements: - None @@ -1765,19 +2014,24 @@ Implements: Arguments: - image (str): optional, filename of image to write into QSPI flash -The driver can be used in test cases by calling the `flash` function. An +The driver can be used in test cases by calling its ``flash()`` method. An example strategy is included in labgrid. ManualPowerDriver ~~~~~~~~~~~~~~~~~ -A ManualPowerDriver requires the user to control the target power states. This -is required if a strategy is used with the target, but no automatic power +A :any:`ManualPowerDriver` requires the user to control the target power +states. +This is required if a strategy is used with the target, but no automatic power control is available. The driver's name will be displayed during interaction. +Binds to: + - None + Implements: - :any:`PowerProtocol` + - :any:`ResetProtocol` .. code-block:: yaml @@ -1789,17 +2043,22 @@ Arguments: ExternalPowerDriver ~~~~~~~~~~~~~~~~~~~ -An ExternalPowerDriver is used to control a target power state via an external command. +An :any:`ExternalPowerDriver` is used to control a target power state via an +external command. + +Binds to: + - None Implements: - :any:`PowerProtocol` + - :any:`ResetProtocol` .. code-block:: yaml ExternalPowerDriver: - cmd_on: example_command on - cmd_off: example_command off - cmd_cycle: example_command cycle + cmd_on: 'example_command on' + cmd_off: 'example_command off' + cmd_cycle: 'example_command cycle' Arguments: - cmd_on (str): command to turn power to the board on @@ -1809,8 +2068,8 @@ Arguments: NetworkPowerDriver ~~~~~~~~~~~~~~~~~~ -A NetworkPowerDriver controls a `NetworkPowerPort`, allowing control of the -target power state without user interaction. +A :any:`NetworkPowerDriver` controls a `NetworkPowerPort`_, allowing control of +the target power state without user interaction. Binds to: port: @@ -1818,6 +2077,7 @@ Binds to: Implements: - :any:`PowerProtocol` + - :any:`ResetProtocol` .. code-block:: yaml @@ -1829,8 +2089,8 @@ Arguments: PDUDaemonDriver ~~~~~~~~~~~~~~~ -A PDUDaemonDriver controls a `PDUDaemonPort`, allowing control of the target -power state without user interaction. +A :any:`PDUDaemonDriver` controls a `PDUDaemonPort`_, allowing control of the +target power state without user interaction. .. note:: PDUDaemon processes commands in the background, so the actual state change @@ -1842,6 +2102,7 @@ Binds to: Implements: - :any:`PowerProtocol` + - :any:`ResetProtocol` .. code-block:: yaml @@ -1853,15 +2114,17 @@ Arguments: YKUSHPowerDriver ~~~~~~~~~~~~~~~~ -A YKUSHPowerDriver controls a `YKUSHPowerPort`, allowing control of the +A :any:`YKUSHPowerDriver` controls a `YKUSHPowerPort`_, allowing control of the target power state without user interaction. Binds to: port: - `YKUSHPowerPort`_ + - `NetworkYKUSHPowerPort`_ Implements: - :any:`PowerProtocol` + - :any:`ResetProtocol` .. code-block:: yaml @@ -1873,8 +2136,8 @@ Arguments: DigitalOutputPowerDriver ~~~~~~~~~~~~~~~~~~~~~~~~ -A DigitalOutputPowerDriver can be used to control the power of a -device using a DigitalOutputDriver. +A :any:`DigitalOutputPowerDriver` can be used to control the power of a device +using a DigitalOutputDriver. Using this driver you probably want an external relay to switch the power of your DUT. @@ -1883,6 +2146,10 @@ Binds to: output: - :any:`DigitalOutputProtocol` +Implements: + - :any:`PowerProtocol` + - :any:`ResetProtocol` + .. code-block:: yaml DigitalOutputPowerDriver: @@ -1893,14 +2160,17 @@ Arguments: USBPowerDriver ~~~~~~~~~~~~~~ -A USBPowerDriver controls a `USBPowerPort`, allowing control of the target -power state without user interaction. +A :any:`USBPowerDriver` controls a `USBPowerPort`_, allowing control of the +target power state without user interaction. Binds to: - - `USBPowerPort`_ + hub: + - `USBPowerPort`_ + - NetworkUSBPowerPort Implements: - :any:`PowerProtocol` + - :any:`ResetProtocol` .. code-block:: yaml @@ -1912,14 +2182,17 @@ Arguments: SiSPMPowerDriver ~~~~~~~~~~~~~~~~ -A SiSPMPowerDriver controls a `SiSPMPowerPort`, allowing control of the target -power state without user interaction. +A :any:`SiSPMPowerDriver` controls a `SiSPMPowerPort`_, allowing control of the +target power state without user interaction. Binds to: - - `SiSPMPowerPort`_ + port: + - `SiSPMPowerPort`_ + - NetworkSiSPMPowerPort Implements: - :any:`PowerProtocol` + - :any:`ResetProtocol` .. code-block:: yaml @@ -1931,11 +2204,12 @@ Arguments: TasmotaPowerDriver ~~~~~~~~~~~~~~~~~~ -A TasmotaPowerDriver controls a `TasmotaPowerPort`, allowing the outlet to be -switched on and off. +A :any:`TasmotaPowerDriver` controls a `TasmotaPowerPort`_, allowing the outlet +to be switched on and off. Binds to: - - `TasmotaPowerPort`_ + power: + - `TasmotaPowerPort`_ Implements: - :any:`PowerProtocol` @@ -1950,13 +2224,16 @@ Arguments: GpioDigitalOutputDriver ~~~~~~~~~~~~~~~~~~~~~~~ -The GpioDigitalOutputDriver writes a digital signal to a GPIO line. +The :any:`GpioDigitalOutputDriver` writes a digital signal to a GPIO line. This driver configures GPIO lines via `the sysfs kernel interface `. While the driver automatically exports the GPIO, it does not configure it in any other way than as an output. Binds to: - - `SysfsGPIO`_ + gpio: + - `SysfsGPIO`_ + - `MatchedSysfsGPIO`_ + - NetworkSysfsGPIO Implements: - :any:`DigitalOutputProtocol` @@ -1970,20 +2247,26 @@ Arguments: SerialPortDigitalOutputDriver ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The SerialPortDigitalOutputDriver makes it possible to use a UART +The :any:`SerialPortDigitalOutputDriver` makes it possible to use a UART as a 1-Bit general-purpose digital output. This driver acts on top of a SerialDriver and uses the its pyserial port to control the flow control lines. +Binds to: + serial: + - `SerialDriver`_ + Implements: - :any:`DigitalOutputProtocol` .. code-block:: yaml SerialPortDigitalOutputDriver: - signal: "dtr" - bindings: { serial : "nameOfSerial" } + signal: 'dtr' + invert: false + bindings: + serial: 'nameOfSerial' Arguments: - signal (str): control signal to use: "dtr" or "rts" @@ -1995,7 +2278,7 @@ Arguments: FileDigitalOutputDriver ~~~~~~~~~~~~~~~~~~~~~~~ -The FileDigitalOutputDriver uses a file +The :any:`FileDigitalOutputDriver` uses a file to write arbitrary string representations of booleans to a file and read from it. @@ -2006,13 +2289,16 @@ reading defaults to False. A prime example for using this driver is Linux's sysfs. +Binds to: + - None + Implements: - :any:`DigitalOutputProtocol` .. code-block:: yaml FileDigitalOutputDriver: - filepath: "/sys/class/leds/myled/brightness" + filepath: '/sys/class/leds/myled/brightness' Arguments: - filepath (str): file that is used for reads and writes. @@ -2021,7 +2307,7 @@ Arguments: DigitalOutputResetDriver ~~~~~~~~~~~~~~~~~~~~~~~~ -A DigitalOutputResetDriver uses a DigitalOutput to reset the target. +A :any:`DigitalOutputResetDriver` uses a DigitalOutput to reset the target. Binds to: output: @@ -2040,7 +2326,7 @@ Arguments: ModbusCoilDriver ~~~~~~~~~~~~~~~~ -A ModbusCoilDriver controls a `ModbusTCPCoil` resource. +A :any:`ModbusCoilDriver` controls a `ModbusTCPCoil`_ resource. It can set and get the current state of the resource. Binds to: @@ -2059,7 +2345,7 @@ Arguments: HIDRelayDriver ~~~~~~~~~~~~~~ -A HIDRelayDriver controls a `HIDRelay` or `NetworkHIDRelay` resource. +An :any:`HIDRelayDriver` controls an `HIDRelay`_ or `NetworkHIDRelay`_ resource. It can set and get the current state of the resource. Binds to: @@ -2079,9 +2365,13 @@ Arguments: ManualSwitchDriver ~~~~~~~~~~~~~~~~~~ -A ManualSwitchDriver requires the user to control a switch or jumper on the -target. This can be used if a driver binds to a :any:`DigitalOutputProtocol`, -but no automatic control is available. +A :any:`ManualSwitchDriver` requires the user to control a switch or jumper on +the target. +This can be used if a driver binds to a :any:`DigitalOutputProtocol`, but no +automatic control is available. + +Binds to: + - None Implements: - :any:`DigitalOutputProtocol` @@ -2096,12 +2386,13 @@ Arguments: DeditecRelaisDriver ~~~~~~~~~~~~~~~~~~~ -A DeditecRelaisDriver controls a Deditec relay resource. +A :any:`DeditecRelaisDriver` controls a *Deditec* relay resource. It can set and get the current state of the resource. Binds to: relais: - `DeditecRelais8`_ + - NetworkDeditecRelais8 Implements: - :any:`DigitalOutputProtocol` @@ -2115,8 +2406,9 @@ Arguments: MXSUSBDriver ~~~~~~~~~~~~ -A MXUSBDriver is used to upload an image into a device in the mxs USB loader -state. This is useful to bootstrap a bootloader onto a device. +An :any:`MXSUSBDriver` is used to upload an image into a device in the *MXS USB +loader state*. +This is useful to bootstrap a bootloader onto a device. Binds to: loader: @@ -2132,10 +2424,10 @@ Implements: main: drivers: MXSUSBDriver: - image: mybootloaderkey + image: 'mybootloaderkey' images: - mybootloaderkey: path/to/mybootloader.img + mybootloaderkey: 'path/to/mybootloader.img' Arguments: - image (str): optional, key in :ref:`images ` containing the path @@ -2143,14 +2435,17 @@ Arguments: IMXUSBDriver ~~~~~~~~~~~~ -A IMXUSBDriver is used to upload an image into a device in the imx USB loader -state. This is useful to bootstrap a bootloader onto a device. -This driver uses the imx-usb-loader tool from barebox. +An :any:`IMXUSBDriver` is used to upload an image into a device in the *i.MX +USB loader state*. +This is useful to bootstrap a bootloader onto a device. +This driver uses the ``imx-usb-loader`` tool from barebox. Binds to: loader: - `IMXUSBLoader`_ - `NetworkIMXUSBLoader`_ + - `MXSUSBLoader`_ + - `NetworkMXSUSBLoader`_ Implements: - :any:`BootstrapProtocol` @@ -2161,10 +2456,10 @@ Implements: main: drivers: IMXUSBDriver: - image: mybootloaderkey + image: 'mybootloaderkey' images: - mybootloaderkey: path/to/mybootloader.img + mybootloaderkey: 'path/to/mybootloader.img' Arguments: - image (str): optional, key in :ref:`images ` containing the path @@ -2172,10 +2467,11 @@ Arguments: BDIMXUSBDriver ~~~~~~~~~~~~~~ -The BDIMXUSBDriver is used to upload bootloader images into an i.MX device in -the USB SDP mode. -This driver uses the imx_usb tool by Boundary Devices. -Compared to the IMXUSBLoader, it supports two-stage upload of U-Boot images. +The :any:`BDIMXUSBDriver` is used to upload bootloader images into an *i.MX +device* in the *USB SDP mode*. +This driver uses the ``imx_usb`` tool by Boundary Devices. +Compared to the ``imx-usb-loader``, it supports two-stage upload of U-Boot +images. The images paths need to be specified from code instead of in the YAML environment, as the correct image depends on the system state. @@ -2198,9 +2494,10 @@ Arguments: - None RKUSBDriver -~~~~~~~~~~~~ -A RKUSBDriver is used to upload an image into a device in the rockchip USB loader -state. This is useful to bootstrap a bootloader onto a device. +~~~~~~~~~~~ +An :any:`RKUSBDriver` is used to upload an image into a device in the *Rockchip +USB loader state*. +This is useful to bootstrap a bootloader onto a device. Binds to: loader: @@ -2216,12 +2513,12 @@ Implements: main: drivers: RKUSBDriver: - image: mybootloaderkey - usb_loader: myloaderkey + image: 'mybootloaderkey' + usb_loader: 'myloaderkey' images: - mybootloaderkey: path/to/mybootloader.img - myloaderkey: path/to/myloader.bin + mybootloaderkey: 'path/to/mybootloader.img' + myloaderkey: 'path/to/myloader.bin' Arguments: - image (str): optional, key in :ref:`images ` containing the path @@ -2231,8 +2528,9 @@ Arguments: UUUDriver ~~~~~~~~~ -A UUUDriver is used to upload an image into a device in the NXP USB loader -state. This is useful to bootstrap a bootloader onto a device. +A :any:`UUUDriver` is used to upload an image into a device in the *NXP USB +loader state*. +This is useful to bootstrap a bootloader onto a device. Binds to: loader: @@ -2250,25 +2548,30 @@ Implements: main: drivers: UUUDriver: - image: mybootloaderkey - cmd: spl + image: 'mybootloaderkey' + script: 'spl' images: - mybootloaderkey: path/to/mybootloader.img + mybootloaderkey: 'path/to/mybootloader.img' Arguments: - image (str): optional, key in :ref:`images ` containing the path of an image to bootstrap onto the target - - script (str): run built-in script with "uuu -b", called with image as arg0 + - script (str): optional, run built-in script with ``uuu -b``, called with image as arg0 USBStorageDriver ~~~~~~~~~~~~~~~~ -A USBStorageDriver allows access to a USB stick or similar local or +A :any:`USBStorageDriver` allows access to a USB stick or similar local or remote device. Binds to: - - `USBMassStorage`_ - - `NetworkUSBMassStorage`_ + storage: + - `USBMassStorage`_ + - `NetworkUSBMassStorage`_ + - `USBSDMuxDevice`_ + - `NetworkUSBSDMuxDevice`_ + - `USBSDWireDevice`_ + - `NetworkUSBSDWireDevice`_ Implements: - None (yet) @@ -2276,12 +2579,12 @@ Implements: .. code-block:: yaml USBStorageDriver: - image: flashimage + image: 'flashimage' .. code-block:: yaml images: - flashimage: ../images/myusb.image + flashimage: '../images/myusb.image' Arguments: - image (str): optional, key in :ref:`images ` containing the path @@ -2289,7 +2592,7 @@ Arguments: OneWirePIODriver ~~~~~~~~~~~~~~~~ -A OneWirePIODriver controls a `OneWirePIO` resource. +A :any:`OneWirePIODriver` controls a `OneWirePIO`_ resource. It can set and get the current state of the resource. Binds to: @@ -2306,38 +2609,79 @@ Implements: Arguments: - None -.. _TFTPProviderDriver: -.. _NFSProviderDriver: -.. _HTTPProviderDriver: - -TFTPProviderDriver / NFSProviderDriver / HTTPProviderDriver -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -These drivers control their corresponding Provider resources, either locally or -remotely. +TFTPProviderDriver +~~~~~~~~~~~~~~~~~~ +The :any:`TFTPProviderDriver` controls its corresponding TFTP resource, either +locally or remotely. Binds to: provider: - `TFTPProvider`_ - `RemoteTFTPProvider`_ - - `NFSProvider`_ - - `RemoteNFSProvider`_ + +Implements: + - None (yet) + +.. code-block:: yaml + + TFTPProviderDriver: {} + +Arguments: + - None + +The driver can be used in test cases by calling its ``stage()`` method, which +returns the path to be used by the target. + +HTTPProviderDriver +~~~~~~~~~~~~~~~~~~ +The :any:`HTTPProviderDriver` controls its corresponding HTTP resource, either +locally or remotely. + +Binds to: + provider: - `HTTPProvider`_ - `RemoteHTTPProvider`_ +Implements: + - None (yet) + .. code-block:: yaml - TFTPProviderDriver: {} + HTTPProviderDriver: {} Arguments: - None -The driver can be used in test cases by calling the `stage()` function, which +The driver can be used in test cases by calling its ``stage()`` method, which returns the path to be used by the target. +NFSProviderDriver +~~~~~~~~~~~~~~~~~ +An :any:`NFSProviderDriver` controls an `NFSProvider`_ resource. + +Binds to: + provider: + - `NFSProvider`_ + - `RemoteNFSProvider`_ + +Implements: + - None (yet) + +.. code-block:: yaml + + NFSProviderDriver: {} + +Arguments: + - None + +The driver can be used in test cases by calling its ``stage()`` method, which +returns an NFSFile object with ``host``, ``export`` and ``relative_file_path`` +attributes. + QEMUDriver ~~~~~~~~~~ -The QEMUDriver allows the usage of a QEMU instance as a target. It requires -several arguments, listed below. +The :any:`QEMUDriver` allows the usage of a QEMU instance as a target. +It requires several arguments, listed below. The kernel, flash, rootfs and dtb arguments refer to images and paths declared in the environment configuration. @@ -2347,27 +2691,26 @@ Binds to: .. code-block:: yaml QEMUDriver: - qemu_bin: qemu_arm - machine: vexpress-a9 - cpu: cortex-a9 - memory: 512M - boot_args: "root=/dev/root console=ttyAMA0,115200" - extra_args: "" - kernel: kernel - rootfs: rootfs - dtb: dtb - nic: user + qemu_bin: 'qemu_arm' + machine: 'vexpress-a9' + cpu: 'cortex-a9' + memory: '512M' + boot_args: 'root=/dev/root console=ttyAMA0,115200' + extra_args: '' + kernel: 'kernel' + rootfs: 'rootfs' + dtb: 'dtb' + nic: 'user' .. code-block:: yaml tools: - qemu_arm: /bin/qemu-system-arm + qemu_arm: '/bin/qemu-system-arm' paths: - rootfs: ../images/root + rootfs: '../images/root' images: - dtb: ../images/mydtb.dtb - kernel: ../images/vmlinuz - + dtb: '../images/mydtb.dtb' + kernel: '../images/vmlinuz' Implements: - :any:`ConsoleProtocol` @@ -2378,18 +2721,22 @@ Arguments: - machine (str): QEMU machine type - cpu (str): QEMU cpu type - memory (str): QEMU memory size (ends with M or G) - - extra_args (str): extra QEMU arguments, they are passed directly to the QEMU binary + - extra_args (str): optional, extra QEMU arguments, they are passed directly to the QEMU binary - boot_args (str): optional, additional kernel boot argument - kernel (str): optional, reference to the images key for the kernel - disk (str): optional, reference to the images key for the disk image + - disk_opts (str): optional, additional QEMU disk options - flash (str): optional, reference to the images key for the flash image - rootfs (str): optional, reference to the paths key for use as the virtio-9p filesystem - dtb (str): optional, reference to the image key for the device tree - bios (str): optional, reference to the image key for the bios image - - display (str, default="none"): optional, display output to enable; must be one of: + - display (str, default="none"): display output to enable; must be one of: + - none: Do not create a display device - fb-headless: Create a headless framebuffer device - egl-headless: Create a headless GPU-backed graphics card. Requires host support + - qemu-default: Don't override QEMU default settings + - nic (str): optional, configuration string to pass to QEMU to create a network interface The QEMUDriver also requires the specification of: @@ -2401,14 +2748,14 @@ The QEMUDriver also requires the specification of: SigrokDriver ~~~~~~~~~~~~ -The SigrokDriver uses a SigrokDevice resource to record samples and provides +The :any:`SigrokDriver` uses a `SigrokDevice`_ resource to record samples and provides them during test runs. Binds to: sigrok: - `SigrokUSBDevice`_ - - `SigrokDevice`_ - `NetworkSigrokUSBDevice`_ + - `SigrokDevice`_ Implements: - None yet @@ -2416,13 +2763,13 @@ Implements: Arguments: - None -The driver can be used in test cases by calling the `capture`, `stop` and -`analyze` functions. +The driver can be used in test cases by calling its ``capture()``, ``stop()`` +and ``analyze()`` methods. SigrokPowerDriver ~~~~~~~~~~~~~~~~~ -The SigrokPowerDriver uses a `SigrokUSBSerialDevice`_ resource to control a -programmable power supply. +The :any:`SigrokPowerDriver` uses a `SigrokUSBSerialDevice`_ resource to +control a programmable power supply. Binds to: sigrok: @@ -2431,6 +2778,7 @@ Binds to: Implements: - :any:`PowerProtocol` + - :any:`ResetProtocol` .. code-block:: yaml @@ -2444,51 +2792,101 @@ Arguments: - max_current (float): optional, maximum allowed current for protection against accidental damage (in ampere) +SigrokDmmDriver +~~~~~~~~~~~~~~~ +The :any:`SigrokDmmDriver` uses a `SigrokDevice`_ resource to record samples +from a *digital multimeter* (DMM) and provides them during test runs. + +It is known to work with *Unit-T UT61B* and *UT61C* devices but should also +work with other DMMs supported by *Sigrok*. + +Binds to: + sigrok: + - `SigrokUSBDevice`_ + - `NetworkSigrokUSBDevice`_ + - `SigrokUSBSerialDevice`_ + - NetworkSigrokUSBSerialDevice + +Implements: + - None yet + +Arguments: + - None + +Sampling can be started calling ``capture(samples, timeout=None)``. +It sets up sampling and returns immediately. +The default timeout has been chosen to work with *Unit-T UT61B*. +Other devices may require a different timeout setting. + +Samples can be obtained using ``stop()``. +``stop()`` will block until either *sigrok* terminates or *timeout* is reached. +This method returns a ``(unit, samples)`` tuple: +``unit`` is the physical unit reported by the DMM; +samples is an iterable of samples. + +This driver relies on buffering of the subprocess call. +Reading a few samples will very likely work - but obtaining a lot of samples may stall. + USBSDMuxDriver ~~~~~~~~~~~~~~ -The :any:`USBSDMuxDriver` uses a USBSDMuxDevice resource to control a +The :any:`USBSDMuxDriver` uses a `USBSDMuxDevice`_ resource to control a USB-SD-Mux device via `usbsdmux `_ tool. +Binds to: + mux: + - `USBSDMuxDevice`_ + - `NetworkUSBSDMuxDevice`_ + Implements: - None yet Arguments: - None -The driver can be used in test cases by calling the `set_mode()` function with -argument being `dut`, `host`, `off`, or `client`. +The driver can be used in test cases by calling its ``set_mode()`` method with +argument being "dut", "host", "off", or "client". LXAUSBMuxDriver ~~~~~~~~~~~~~~~ -The :any:`LXAUSBMuxDriver` uses a LXAUSBMux resource to control a USB-Mux +The :any:`LXAUSBMuxDriver` uses an `LXAUSBMux`_ resource to control a USB-Mux device via the `usbmuxctl `_ tool. +Binds to: + mux: + - `LXAUSBMux`_ + - `NetworkLXAUSBMux` + Implements: - None yet Arguments: - None -The driver can be used in test cases by calling the `set_links()` function with +The driver can be used in test cases by calling its ``set_links()`` method with a list containing one or more of "dut-device", "host-dut" and "host-device". Not all combinations can be configured at the same time. USBSDWireDriver ~~~~~~~~~~~~~~~ -The :any:`USBSDWireDriver` uses a USBSDWireDevice resource to control a -USB-SD-Wire device via `sd-mux-ctrl `_ +The :any:`USBSDWireDriver` uses a `USBSDWireDevice`_ resource to control a +USB-SD-Wire device via `sd-mux-ctrl `_ tool. +Binds to: + mux: + - `USBSDWireDevice`_ + - `NetworkUSBSDWireDevice` + Implements: - None yet Arguments: - None -The driver can be used in test cases by calling the `set_mode()` function with -argument being `dut`, `host`, `off`, or `client`. +The driver can be used in test cases by calling its ``set_mode()`` method with +argument being "dut", "host", "off", or "client". USBVideoDriver ~~~~~~~~~~~~~~ @@ -2508,19 +2906,19 @@ Implements: Arguments: - None -Although the driver can be used from Python code by calling the `stream()` +Although the driver can be used from Python code by calling the ``stream()`` method, it is currently mainly useful for the ``video`` subcommand of ``labgrid-client``. It supports the `Logitech HD Pro Webcam C920` with the USB ID 046d:082d and a few others. -More cameras can be added to `get_qualities()` and `get_pipeline()` in +More cameras can be added to ``get_qualities()`` and ``get_pipeline()`` in ``labgrid/driver/usbvideodriver.py``. Appropriate configuration parameters can be determined by using the GStreamer ``gst-device-monitor-1.0`` command line utility. USBAudioInputDriver ~~~~~~~~~~~~~~~~~~~ -The :any:`USBAudioInputDriver` is used to receive a audio stream from a local +The :any:`USBAudioInputDriver` is used to receive an audio stream from a local or remote USB audio input. It uses the GStreamer command line utility ``gst-launch`` on the sender side to stream the audio to the client. @@ -2530,7 +2928,7 @@ On the receiver, it either uses ``gst-launch`` for simple playback or complex cases (such as measuring the current volume level). Binds to: - video: + res: - `USBAudioInput`_ - `NetworkUSBAudioInput`_ @@ -2542,8 +2940,8 @@ Arguments: USBTMCDriver ~~~~~~~~~~~~ -The :any:`USBTMCDriver` is used to control a oscilloscope via the USB TMC -protocol. +The :any:`USBTMCDriver` is used to control an oscilloscope via the *USB TMC +protocol*. Binds to: tmc: @@ -2560,20 +2958,23 @@ Currently, it can be used by the ``labgrid-client`` ``tmc`` subcommands to show (and save) a screenshot, to show per channel measurements and to execute raw TMC commands. It only supports the `Keysight DSO-X 2000` series (with the USB ID 0957:1798), -but more devices can be added by extending `on_activate()` in +but more devices can be added by extending ``on_activate()`` in ``labgrid/driver/usbtmcdriver.py`` and writing a corresponding backend in ``labgrid/driver/usbtmc/``. FlashromDriver ~~~~~~~~~~~~~~ -The :any:`FlashromDriver` is used to flash a rom, using the flashrom utility. +The :any:`FlashromDriver` is used to flash a ROM, using the flashrom utility. .. code-block:: yaml FlashromDriver: image: 'foo' + +.. code-block:: yaml + images: - foo: ../images/image_to_load.raw + foo: '../images/image_to_load.raw' Binds to: flashrom_resource: @@ -2612,10 +3013,10 @@ a device. .. code-block:: yaml images: - foo: ../images/flash_device.sh + foo: '../images/flash_device.sh' Binds to: - flashabledevice_resource: + device: - `USBFlashableDevice`_ - `NetworkUSBFlashableDevice`_ @@ -2651,11 +3052,13 @@ Binds to: Implements: - :any:`VideoProtocol` -Although the driver can be used from Python code by calling the `stream()` +Arguments: + - None + +Although the driver can be used from Python code by calling the ``stream()`` method, it is currently mainly useful for the ``video`` subcommand of ``labgrid-client``. - ========== ========================================================= Key Description ========== ========================================================= @@ -2664,8 +3067,8 @@ Key Description ========== ========================================================= Properties of these keys can be selected using the Python format string syntax, -e.g. ``{device.devnode}`` to select the device node path of -:any:`USBFlashableDevice` +e.g. ``{device.devnode}`` to select the device node path of a +:any:`USBFlashableDevice`. DediprogFlashDriver ~~~~~~~~~~~~~~~~~~~ @@ -2675,25 +3078,32 @@ The :any:`DediprogFlashDriver` is used to flash an SPI device using DediprogFlas DediprogFlashDriver: image: 'foo' + +.. code-block:: yaml + images: - foo: ../images/image_to_load.raw + foo: '../images/image_to_load.raw' Binds to: - DediprogFlasher_resource: + flasher: - `DediprogFlasher`_ - `NetworkDediprogFlasher`_ +Implements: + - None (yet) + Arguments: - image (str): optional, key in :ref:`images ` containing the path of an image to flash onto the target The DediprogFlashDriver allows using DediprogFlasher dpcmd to flash or erase SPI -devices. It is assumed that the device flashing is an exporter wired, via -DediprogFlasher SF100 for instance, to the device being flashed. +devices. It is assumed that the device flashing is an exporter wired, via a +*Dediprog SF100 SPI NOR Flash Programmer* for instance, to the device being +flashed. XenaDriver ~~~~~~~~~~ -The XenaDriver allows to use Xena networking test equipment. +The :any:`XenaDriver` allows to use Xena networking test equipment. Using the `xenavalkyrie` library a full API to control the tester is available. Binds to: @@ -2705,8 +3115,8 @@ Currently tested on a `XenaCompact` chassis equipped with a `1 GE test module`. DockerDriver ~~~~~~~~~~~~ -A DockerDriver binds to a `DockerDaemon` and is used to create and control one -docker container. +A :any:`DockerDriver` binds to a `DockerDaemon`_ and is used to create and +control one docker container. | The driver uses the docker python module to interact with the docker daemon. | For more information on the parameters see: @@ -2722,17 +3132,28 @@ Implements: .. code-block:: yaml DockerDriver: - image_uri: "rastasheep/ubuntu-sshd:16.04" - container_name: "ubuntu-lg-example" - host_config: {"network_mode":"bridge"} - network_services: [{"port":22,"username":"root","password":"root"}] + image_uri: 'rastasheep/ubuntu-sshd:16.04' + pull: 'always' + container_name: 'ubuntu-lg-example' + host_config: {'network_mode': 'bridge'} + network_services: [{'port': 22, 'username': 'root', 'password': 'root'}] Arguments: - image_uri (str): identifier of the docker image to use (may have a tag suffix) - - command (str): command to run in the container (optional, depends on image) - - volumes (list): list to configure volumes mounted inside the container (optional) + - pull (str): pull policy, supports "always", "missing", "never". Default is + "always" + + - always: Always pull the image and throw an error if the pull fails. + - missing: Pull the image only when the image is not in the local + containers storage. Throw an error if no image is found and the pull + fails. + - never: Never pull the image but use the one from the local containers + storage. Throw a `docker.errors.ImageNotFound` if no image is found. + + - command (str): optional, command to run in the container (depends on image) + - volumes (list): optional, list to configure volumes mounted inside the container - container_name (str): name of the container - - environment (list): list of environment variables (optional) + - environment (list): optional, list of environment variables - host_config (dict): dictionary of host configurations - network_services (list): dictionaries that describe individual `NetworkService`_ instances that come alive when the container is created. The "address" argument @@ -2741,8 +3162,8 @@ Arguments: LXAIOBusPIODriver ~~~~~~~~~~~~~~~~~ -An LXAIOBusPIODriver binds to a single `LXAIOBusPIO` to toggle and read the PIO -states. +An :any:`LXAIOBusPIODriver` binds to a single `LXAIOBusPIO`_ to toggle and read +the PIO states. Binds to: pio: @@ -2759,9 +3180,29 @@ Implements: Arguments: - None +HttpDigitalOutputDriver +~~~~~~~~~~~~~~~~~~~~~~~ +A :any:`HttpDigitalOutputDriver` binds to an `HttpDigitalOutput`_ to set and +get a digital output state via HTTP. + +Binds to: + http: + - `HttpDigitalOutput`_ + +.. code-block:: yaml + + HttpDigitalOutputDriver: {} + +Implements: + - :any:`DigitalOutputProtocol` + +Arguments: + - None + PyVISADriver ~~~~~~~~~~~~ -The PyVISADriver uses a PyVISADevice resource to control test equipment manageable by PyVISA. +The :any:`PyVISADriver` uses a `PyVISADevice`_ resource to control test +equipment manageable by PyVISA. Binds to: pyvisa_resource: @@ -2775,8 +3216,8 @@ Arguments: NetworkInterfaceDriver ~~~~~~~~~~~~~~~~~~~~~~ -This driver allows controlling a network interface (such as Ethernet or WiFi) on -the exporter using NetworkManager. +The :any:`NetworkInterfaceDriver` allows controlling a network interface (such +as Ethernet or WiFi) on the exporter using NetworkManager. The configuration is based on dictionaries with contents similar to NM's connection files in INI-format. @@ -2784,9 +3225,10 @@ Currently basic wired and wireless configuration options have been tested. To use it, `PyGObject `_ must be installed (on the same system as the network interface). -For Debian, the necessary packages are `python3-gi` and `gir1.2-nm-1.0`. +For Debian, the necessary packages are ``python3-gi`` and ``gir1.2-nm-1.0``. It supports: + - static and DHCP address configuration - WiFi client or AP - connection sharing (DHCP server with NAT) @@ -2795,8 +3237,52 @@ It supports: Binds to: iface: - `NetworkInterface`_ + - `RemoteNetworkInterface`_ - `USBNetworkInterface`_ + +Implements: + - None yet + +Arguments: + - None + +RawNetworkInterfaceDriver +~~~~~~~~~~~~~~~~~~~~~~~~~ +The :any:`RawNetworkInterfaceDriver` allows "raw" control of a network +interface (such as Ethernet or WiFi). + +The labgrid-raw-interface helper (``helpers/labgrid-raw-interface``) needs to +be installed in the PATH and usable via sudo without password. +A configuration file ``/etc/labgrid/helpers.yaml`` must be installed on hosts +exporting network interfaces for the RawNetworkInterfaceDriver, e.g.: + +.. code-block:: yaml + + raw-interface: + denied-interfaces: + - 'eth1' + +It supports: + +- recording traffic +- replaying traffic +- basic statistic collection + +For now, the RawNetworkInterfaceDriver leaves pre-configuration of the exported +network interface to the user, including: + +- disabling DHCP +- disabling IPv6 Duplicate Address Detection (DAD) by SLAAC (Stateless + Address Autoconfiguration) and Neighbor Discovery +- disabling Generic Receive Offload (GRO) + +This might change in the future. + +Binds to: + iface: + - `NetworkInterface`_ - `RemoteNetworkInterface`_ + - `USBNetworkInterface`_ Implements: - None yet @@ -2808,13 +3294,12 @@ Arguments: Strategies ---------- - Strategies are used to ensure that the device is in a certain state during a test. Such a state could be the bootloader or a booted Linux kernel with shell. BareboxStrategy ~~~~~~~~~~~~~~~ -A BareboxStrategy has four states: +A :any:`BareboxStrategy` has four states: - unknown - off @@ -2838,11 +3323,11 @@ Here is an example environment config: ShellDriver: prompt: 'root@\w+:[^ ]+ ' login_prompt: ' login: ' - username: root + username: 'root' BareboxStrategy: {} In order to use the BareboxStrategy via labgrid as a library and transition to -the ``shell`` state: +the "shell" state: .. testsetup:: barebox-strategy @@ -2859,11 +3344,11 @@ the ``shell`` state: >>> s.transition("shell") This command would transition from the bootloader into a Linux shell and -activate the ShellDriver. +activate the `ShellDriver`_. ShellStrategy ~~~~~~~~~~~~~ -A ShellStrategy has three states: +A :any:`ShellStrategy` has three states: - unknown - off @@ -2885,11 +3370,11 @@ Here is an example environment config: ShellDriver: prompt: 'root@\w+:[^ ]+ ' login_prompt: ' login: ' - username: root + username: 'root' ShellStrategy: {} In order to use the ShellStrategy via labgrid as a library and transition to -the ``shell`` state: +the "shell" state: .. testsetup:: shell-strategy @@ -2905,11 +3390,11 @@ the ``shell`` state: >>> s = t.get_driver("ShellStrategy") This command would transition directly into a Linux shell and -activate the ShellDriver. +activate the `ShellDriver`_. UBootStrategy ~~~~~~~~~~~~~ -A UBootStrategy has four states: +A :any:`UBootStrategy` has four states: - unknown - off @@ -2933,11 +3418,11 @@ Here is an example environment config: ShellDriver: prompt: 'root@\w+:[^ ]+ ' login_prompt: ' login: ' - username: root + username: 'root' UBootStrategy: {} In order to use the UBootStrategy via labgrid as a library and transition to -the ``shell`` state: +the "shell" state: .. testsetup:: uboot-strategy @@ -2954,11 +3439,11 @@ the ``shell`` state: >>> s.transition("shell") This command would transition from the bootloader into a Linux shell and -activate the ShellDriver. +activate the `ShellDriver`_. DockerStrategy ~~~~~~~~~~~~~~ -A DockerStrategy has three states: +A :any:`DockerStrategy` has three states: - unknown - gone @@ -2973,17 +3458,17 @@ Here is an example environment config: main: resources: DockerDaemon: - docker_daemon_url: unix://var/run/docker.sock + docker_daemon_url: 'unix://var/run/docker.sock' drivers: DockerDriver: - image_uri: "rastasheep/ubuntu-sshd:16.04" - container_name: "ubuntu-lg-example" - host_config: {"network_mode":"bridge"} - network_services: [{"port":22,"username":"root","password":"root"}] + image_uri: 'rastasheep/ubuntu-sshd:16.04' + container_name: 'ubuntu-lg-example' + host_config: {'network_mode': 'bridge'} + network_services: [{'port': 22, 'username': 'root', 'password': 'root'}] DockerStrategy: {} In order to use the DockerStrategy via labgrid as a library and transition to -the ``accessible`` state: +the "accessible" state: .. testsetup:: docker-strategy @@ -3009,7 +3494,10 @@ Reporters StepReporter ~~~~~~~~~~~~ -The StepReporter outputs individual labgrid steps to `STDOUT`. +.. warning:: + The StepReporter is deprecated, use the `StepLogger`_ instead. + +The :any:`StepReporter` outputs individual labgrid steps to `STDOUT`. .. doctest:: @@ -3026,10 +3514,9 @@ The Reporter can be stopped with a call to the stop function: Stopping the StepReporter if it has not been started will raise an AssertionError, as will starting an already started StepReporter. - ConsoleLoggingReporter ~~~~~~~~~~~~~~~~~~~~~~ -The ConsoleLoggingReporter outputs read calls from the console transports into +The :any:`ConsoleLoggingReporter` outputs read calls from the console transports into files. It takes the path as a parameter. .. doctest:: @@ -3044,11 +3531,34 @@ The Reporter can be stopped with a call to the stop function: >>> from labgrid import ConsoleLoggingReporter >>> ConsoleLoggingReporter.stop() - Stopping the ConsoleLoggingReporter if it has not been started will raise an AssertionError, as will starting an already started StepReporter. +Loggers +------- + +StepLogger +~~~~~~~~~~ +The :any:`StepLogger` logs individual labgrid steps. + +Logging can be set up via ``labgrid.logging.basicConfig()``. + +.. doctest:: + + >>> import logging + >>> from labgrid.logging import basicConfig, StepLogger + >>> basicConfig(level=logging.INFO) + >>> StepLogger.start() + +The logger can be stopped with a call to the stop function: + +.. doctest:: + + >>> from labgrid.logging import StepLogger + >>> StepLogger.stop() +Stopping the StepLogger if it has not been started will raise an +AssertionError, as will starting an already started StepLogger. Environment Configuration ------------------------- @@ -3137,8 +3647,8 @@ becomes: - SerialDriver: {} - SerialDriver: {} -This configuration doesn't specify which :any:`RawSerialPort` to use for each -:any:`SerialDriver`, so it will cause an exception when instantiating the +This configuration doesn't specify which `RawSerialPort`_ to use for each +`SerialDriver`_, so it will cause an exception when instantiating the :any:`Target`. To bind the correct driver to the correct resource, explicit ``name`` and ``bindings`` properties are used: @@ -3162,14 +3672,15 @@ To bind the correct driver to the correct resource, explicit ``name`` and bindings: port: 'bar' -The property name for the binding (e.g. `port` in the example above) is +The property name for the binding (e.g. ``port`` in the example above) is documented for each individual driver in this chapter. The YAML configuration file also supports templating for some substitutions, these are: -- LG_* variables, are replaced with their respective LG_* environment variable -- BASE is substituted with the base directory of the YAML file. +- ``LG_*`` variables, are replaced with their respective ``LG_*`` environment + variable +- ``BASE`` is substituted with the base directory of the YAML file. As an example: @@ -3179,13 +3690,13 @@ As an example: main: resources: RemotePlace: - name: !template $LG_PLACE + name: !template '$LG_PLACE' tools: - qemu_bin: !template "$BASE/bin/qemu-bin" + qemu_bin: !template '$BASE/bin/qemu-bin' -would resolve the qemu_bin path relative to the BASE dir of the YAML file and -try to use the RemotePlace with the name set in the LG_PLACE environment -variable. +would resolve the ``qemu_bin`` path relative to the ``BASE`` dir of the YAML +file and try to use the `RemotePlace`_ with the name set in the ``LG_PLACE`` +environment variable. See the :ref:`labgrid-device-config` man page for documentation on the top-level ``options``, ``images``, ``tools``, and ``examples`` keys in the @@ -3224,37 +3735,37 @@ By default, the class name is inferred from the resource name, and `` will be passed to its constructor. For USB resources, you will most likely want to use :ref:`udev-matching` here. -As a simple example, here is one group called *usb-hub-in-rack12* containing -a single :any:`USBSerialPort` resource (using udev matching), which will be -exported as `exportername/usb-hub-in-rack12/NetworkSerialPort/USBSerialPort`: +As a simple example, here is one group called ``usb-hub-in-rack12`` containing +a single `USBSerialPort`_ resource (using udev matching), which will be +exported as ``exportername/usb-hub-in-rack12/NetworkSerialPort/USBSerialPort``: .. code-block:: yaml usb-hub-in-rack12: USBSerialPort: match: - '@ID_PATH': pci-0000:05:00.0-usb-3-1.3 + '@ID_PATH': 'pci-0000:05:00.0-usb-3-1.3' To export multiple resources of the same class in the same group, you can choose a unique resource name, and then use the ``cls`` parameter to specify the class name instead (which will not be passed as a parameter to the class constructor). -In this next example we will export one :any:`USBSerialPort` as -`exportername/usb-hub-in-rack12/NetworkSerialPort/console-main`, -and another :any:`USBSerialPort` as -`exportername/usb-hub-in-rack12/NetworkSerialPort/console-secondary`: +In this next example we will export one `USBSerialPort`_ as +``exportername/usb-hub-in-rack12/NetworkSerialPort/console-main``, +and another `USBSerialPort`_ as +``exportername/usb-hub-in-rack12/NetworkSerialPort/console-secondary``: .. code-block:: yaml usb-hub-in-rack12: console-main: - cls: USBSerialPort + cls: 'USBSerialPort' match: - '@ID_PATH': pci-0000:05:00.0-usb-3-1.3 + '@ID_PATH': 'pci-0000:05:00.0-usb-3-1.3' console-secondary: - cls: USBSerialPort + cls: 'USBSerialPort' match: - '@ID_PATH': pci-0000:05:00.0-usb-3-1.4 + '@ID_PATH': 'pci-0000:05:00.0-usb-3-1.4' Note that you could also split the resources up into distinct groups instead to achieve the same effect: @@ -3264,11 +3775,11 @@ to achieve the same effect: usb-hub-in-rack12-port3: USBSerialPort: match: - '@ID_PATH': pci-0000:05:00.0-usb-3-1.3 + '@ID_PATH': 'pci-0000:05:00.0-usb-3-1.3' usb-hub-in-rack12-port4: USBSerialPort: match: - '@ID_PATH': pci-0000:05:00.0-usb-3-1.4 + '@ID_PATH': 'pci-0000:05:00.0-usb-3-1.4' Templating ~~~~~~~~~~ @@ -3282,14 +3793,21 @@ is used as a preprocessor for the configuration file: # for idx in range(1, 17) {{ 1000 + idx }}: NetworkSerialPort: - {host: rl1, port: {{ 4000 + idx }}} + host: 'rl1' + port: {{ 4000 + idx }} NetworkPowerPort: # if 1 <= idx <= 8 - {model: apc, host: apc1, index: {{ idx }}} + model: 'apc' + host: 'apc1' + index: {{ idx }} # elif 9 <= idx <= 12 - {model: netio, host: netio4, index: {{ idx - 8 }}} + model: 'netio' + host: 'netio4' + index: {{ idx - 8 }} # elif 13 <= idx <= 16 - {model: netio, host: netio5, index: {{ idx - 12 }}} + model: 'netio' + host: 'netio5' + index: {{ idx - 12 }} # endif # endfor diff --git a/doc/development.rst b/doc/development.rst index f6f069ccf..5a58abb52 100644 --- a/doc/development.rst +++ b/doc/development.rst @@ -23,7 +23,7 @@ Install required dependencies: .. code-block:: bash - sudo apt install python3-dev libow-dev libsnappy-dev + sudo apt install python3-dev libow-dev Install labgrid with development dependencies into the virtualenv in editable mode: @@ -304,6 +304,12 @@ while the `shell` state uses the barebox state to cycle the board and then boot the linux kernel. The `off` state switches the power off. +Oftentimes it is also necessary to wait for specific resources to appear before +a transition can be continued. The `await_resources` function of the target +implements this functionality, it expects a list of resources to wait for and +optionally takes a timeout and whether the resource should be available or +unavailable. + Tips for Writing and Debugging Tests ------------------------------------ @@ -679,7 +685,7 @@ When contributing to documentation it's practical to be able to build it also lo git clone https://github.com/labgrid-project/labgrid.git cd labgrid - pip install -e ".[doc]" + pip install -e ".[dev]" cd doc make html @@ -758,7 +764,7 @@ Step Tracing The Step infrastructure already collects timing and nesting information on executed commands, but is currently only used in the pytest plugin or via the -standalone StepReporter. +standalone StepLogger (or deprecated StepReporter). By writing these events to a file (or sqlite database) as a trace, we can collect data over multiple runs for later analysis. This would become more useful by passing recognized events (stack traces, diff --git a/doc/getting_started.rst b/doc/getting_started.rst index f07086129..c27d6b161 100644 --- a/doc/getting_started.rst +++ b/doc/getting_started.rst @@ -62,7 +62,7 @@ Test your installation by running: .. code-block:: bash labgrid-venv $ labgrid-client --help - usage: labgrid-client [-h] [-x URL] [-c CONFIG] [-p PLACE] [-d] COMMAND ... + usage: labgrid-client [-h] [-x ADDRESS] [-c CONFIG] [-p PLACE] [-d] COMMAND ... ... If the help for labgrid-client does not show up, open an `Issue @@ -170,58 +170,11 @@ exporter, and learn how to access the exporter via the client. Coordinator ~~~~~~~~~~~ -To start the coordinator, we will download the labgrid repository, create an -extra virtualenv and install the dependencies: +We can simply start the coordinator: .. code-block:: bash - $ sudo apt install libsnappy-dev - $ git clone https://github.com/labgrid-project/labgrid - $ cd labgrid - $ virtualenv -p python3 crossbar-venv - $ crossbar-venv/bin/pip install --upgrade pip - $ crossbar-venv/bin/pip install -r crossbar-requirements.txt - $ virtualenv -p python3 labgrid-venv - $ source labgrid-venv/bin/activate - labgrid-venv $ pip install --upgrade pip - labgrid-venv $ pip install . - -All necessary dependencies should be installed now. - -Copy and customize the crossbar config file ``.crossbar/config-anonymous.yaml`` -for your use case: - -.. code-block:: bash - - labgrid-venv $ cp .crossbar/config-anonymous.yaml .crossbar/my-config.yaml - -.. note:: crossbar is a network messaging framework for building distributed - applications, which labgrid plugs into. - -The path to the Python interpreter in the labgrid-venv needs to be configured -in crossbar's config, either manually or with the labgrid-venv being active -via: - -.. code-block:: bash - - labgrid-venv $ sed -i "s#^ executable: .*\$# executable: ${VIRTUAL_ENV}/bin/python3#" .crossbar/my-config.yaml - -.. note:: For long running deployments a different ``workdir`` and port may be - used. - The crossbar config should reside in a ``.crossbar`` directory in the - ``workdir`` in this case. - For an example systemd service file, see - :ref:`remote-getting-started-systemd-files`. - -Now we can finally start the coordinator inside the repository: - -.. code-block:: bash - - $ crossbar-venv/bin/crossbar start --config my-config.yaml - -.. note:: If --config is specified as a relative path, the config is expected - in a .crossbar subdirectory (as is the case in the labgrid - repository). + labgrid-venv $ labgrid-coordinator Exporter ~~~~~~~~ @@ -349,8 +302,8 @@ Now we can connect to the serial console: labgrid-venv $ labgrid-client -p example-place console -.. note:: Using remote connection requires ``microcom`` installed on the host - where the labgrid-client is called. +.. note:: Using remote connection requires ``microcom`` or ``telnet`` installed + on the host where the labgrid-client is called. See :ref:`remote-usage` for some more advanced features. For a complete reference have a look at the :doc:`labgrid-client(1) ` @@ -375,25 +328,19 @@ Follow these instructions to install the systemd files on your machine(s): installation paths of your distribution. #. Adapt the ``ExecStart`` paths of the service files to the respective Python virtual environments of the coordinator and exporter. -#. Create the coordinator configuration file referenced in the ``ExecStart`` - option of the :file:`labgrid-coordinator.service` file by using - :file:`.crossbar/config-anonymous.yaml` as a starting point. You most likely - want to make sure that the ``workdir`` option matches the path given via the - ``--cbdir`` option in the service file; see - :ref:`remote-getting-started-coordinator` for further information. #. Adjust the ``SupplementaryGroups`` option in the :file:`labgrid-exporter.service` file to your distribution so that the exporter gains read and write access on TTY devices (for ``ser2net``); most often, these groups are called ``dialout``, ``plugdev`` or ``tty``. Depending on your udev configuration, you may need multiple groups. -#. Set the coordinator URL the exporter should connect to by overriding the +#. Set the coordinator address the exporter should connect to by overriding the exporter service file; i.e. execute ``systemctl edit labgrid-exporter.service`` and add the following snippet: .. code-block:: [Service] - Environment="LG_CROSSBAR=ws://:/ws" + Environment="LG_COORDINATOR=[:]" #. Create the ``labgrid`` user and group: diff --git a/doc/man.rst b/doc/man.rst index 0523f301a..e6d6f6360 100644 --- a/doc/man.rst +++ b/doc/man.rst @@ -5,3 +5,4 @@ Manual Pages man/client man/device-config man/exporter + man/coordinator diff --git a/doc/man/coordinator.rst b/doc/man/coordinator.rst new file mode 100644 index 000000000..c1c7afbd7 --- /dev/null +++ b/doc/man/coordinator.rst @@ -0,0 +1,2 @@ +.. _labgrid-coordinator: +.. include:: ../../man/labgrid-coordinator.rst diff --git a/doc/overview.rst b/doc/overview.rst index 104cd7147..8d69d85f8 100644 --- a/doc/overview.rst +++ b/doc/overview.rst @@ -210,7 +210,7 @@ labgrid contains components for accessing resources which are not directly accessible on the local machine. The main parts of this are: -labgrid-coordinator (crossbar component) +labgrid-coordinator Clients and exporters connect to the coordinator to publish resources, manage place configuration and handle mutual exclusion. @@ -227,9 +227,8 @@ RemotePlace (managed resource) When used in a `Target`, the RemotePlace expands to the resources configured for the named places. -These components communicate over the `WAMP `_ -implementation `Autobahn `_ and the `Crossbar -`_ WAMP router. +These components communicate over `gRPC `_. The coordinator +acts as a gRPC server to which client and exporter connect. The following sections describe the responsibilities of each component. See :ref:`remote-usage` for usage information. @@ -239,8 +238,8 @@ The following sections describe the responsibilities of each component. See Coordinator ~~~~~~~~~~~ -The `Coordinator` is implemented as a Crossbar component and is started by the -router. +The `Coordinator` is implemented as a gRPC server and is started as a separate +process. It provides separate RPC methods for the exporters and clients. The coordinator keeps a list of all resources for clients and @@ -319,6 +318,12 @@ published as declared in the configuration file. This is useful to register externally configured resources such as network power switches or serial port servers with a labgrid coordinator. +.. note:: + Users will require SSH access to the exporter to access services and command + line utilities. You also have to ensure that users can access usb devices for + i.e. imx-usb-loader. To test a SSH jump to a device over the exporter outside + of labgrid, `ssh -J EXPORTER USER@DEVICE` can be used. + .. _overview-client: Client @@ -381,7 +386,7 @@ variable needs to be set to the remote host which should tunnel the connection to the coordinator. The client then forwards all network traffic - client-to-coordinator and client-to-exporter - through SSH, via their respective proxies. This means that with :code:`LG_PROXY` and -:code:`LG_CROSSBAR` labgrid can be used fully remotely with only a SSH +:code:`LG_COORDINATOR` labgrid can be used fully remotely with only a SSH connection as a requirement. .. note:: diff --git a/doc/usage.rst b/doc/usage.rst index 1e1cd814c..9d423d50b 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -437,22 +437,19 @@ Other labgrid-related pytest plugin options are: Specify a labgrid environment config file. This is equivalent to labgrid-client's ``-c``/``--config``. -``--lg-coordinator=CROSSBAR_URL`` - Specify labgrid coordinator websocket URL. - Defaults to ``ws://127.0.0.1:20408/ws``. - This is equivalent to labgrid-client's ``-x``/``--crossbar``. +``--lg-coordinator=COORDINATOR_ADDRESS`` + Specify labgrid coordinator gRPC address as ``HOST[:PORT]``. + Defaults to ``127.0.0.1:20408``. + This is equivalent to labgrid-client's ``-x``/``--coordinator``. ``--lg-log=[path to logfiles]`` Path to store console log file. If option is specified without path the current working directory is used. ``--lg-colored-steps`` - Enables the ColoredStepReporter. - Different events have different colors. - The more colorful, the more important. - In order to make less important output "blend into the background" different - color schemes are available. - See :ref:`LG_COLOR_SCHEME `. + Previously enabled the ColoredStepReporter, which has been removed with the + StepLogger introduction. + Kept for compatibility reasons without effect. ``--lg-initial-state=STATE_NAME`` Sets the Strategy's initial state. @@ -470,19 +467,6 @@ LG_ENV ^^^^^^ Behaves like ``LG_ENV`` for :doc:`labgrid-client `. -.. _usage-lgcolorscheme: - -LG_COLOR_SCHEME -^^^^^^^^^^^^^^^ -Influences the color scheme used for the Colored Step Reporter. -``dark`` is meant for dark terminal background. -``light`` is optimized for light terminal background. -``dark-256color`` and ``light-256color`` are respective variants for terminals -that support 256 colors. -By default, ``dark`` or ``dark-256color`` (depending on the terminal) are used. - -Takes effect only when used with ``--lg-colored-steps``. - LG_PROXY ^^^^^^^^ Specifies a SSH proxy host to be used for port forwards to access the diff --git a/dockerfiles/Dockerfile b/dockerfiles/Dockerfile index f598ff4b5..15fd5eb1f 100644 --- a/dockerfiles/Dockerfile +++ b/dockerfiles/Dockerfile @@ -1,4 +1,5 @@ -FROM debian:bullseye-slim AS labgrid-base +FROM debian:bookworm-slim AS labgrid-base +ARG VERSION LABEL maintainer="eha@deif.com" @@ -8,11 +9,12 @@ COPY ./ /opt/labgrid/ RUN set -e ;\ apt update -q=2 ;\ - apt install -q=2 --yes --no-install-recommends python3 python3-dev python3-pip python3-setuptools git build-essential libsnappy-dev ;\ - pip3 install -U pip;\ + apt install -q=2 --yes --no-install-recommends python3 python3-dev python3-pip python3-setuptools git build-essential ;\ + pip3 install --break-system-packages -U pip;\ apt clean ;\ rm -rf /var/lib/apt/lists/* ;\ - git clone https://github.com/vishnubob/wait-for-it.git opt/wait-for-it && cd opt/wait-for-it && git reset --hard 54d1f0bfeb6557adf8a3204455389d0901652242 + cd /opt/labgrid ;\ + SETUPTOOLS_SCM_PRETEND_VERSION="$VERSION" pip3 install --break-system-packages --no-cache-dir . # # Client @@ -21,9 +23,7 @@ FROM labgrid-base AS labgrid-client ARG VERSION RUN set -e ;\ - cd /opt/labgrid ;\ - pip3 install yq ;\ - SETUPTOOLS_SCM_PRETEND_VERSION="$VERSION" pip3 install --no-cache-dir . ;\ + pip3 install --break-system-packages yq ;\ apt update -q=2 ;\ apt install -q=2 --yes --no-install-recommends microcom openssh-client rsync jq qemu-system qemu-utils ;\ apt clean ;\ @@ -37,21 +37,13 @@ CMD ["/bin/bash"] FROM labgrid-base AS labgrid-coordinator ARG VERSION -ENV CROSSBAR_DIR=/opt/crossbar - -RUN set -e ;\ - cd /opt/labgrid ;\ - pip3 install virtualenv ;\ - SETUPTOOLS_SCM_PRETEND_VERSION="$VERSION" pip3 install --no-cache-dir . ;\ - virtualenv -p python3 crossbar-venv ;\ - crossbar-venv/bin/pip3 install -r crossbar-requirements.txt ;\ - sed -i "s#^ executable: .*\$# executable: python3#" .crossbar/config-anonymous.yaml - -VOLUME /opt/crossbar +VOLUME /opt/coordinator EXPOSE 20408 -CMD ["/opt/labgrid/crossbar-venv/bin/crossbar", "start", "--config", "/opt/labgrid/.crossbar/config-anonymous.yaml"] +WORKDIR /opt/coordinator + +CMD ["/usr/local/bin/labgrid-coordinator"] # # Exporter @@ -62,8 +54,6 @@ ARG VERSION COPY dockerfiles/exporter/entrypoint.sh /entrypoint.sh RUN set -e ;\ - cd /opt/labgrid ;\ - SETUPTOOLS_SCM_PRETEND_VERSION="$VERSION" pip3 install --no-cache-dir . ;\ apt update -q=2 ;\ apt install -q=2 --yes --no-install-recommends ser2net ;\ apt clean ;\ diff --git a/dockerfiles/README.rst b/dockerfiles/README.rst index 9ab661fec..b0f8ff150 100644 --- a/dockerfiles/README.rst +++ b/dockerfiles/README.rst @@ -5,7 +5,7 @@ This folder contains Dockerfile's for building Docker images for the 3 different components of a Labgrid distributed infrastructure. - **labgrid-coordinator** - An image for with crossbar which can be used to run + An image with the Labgrid coordinator. a Labgrid coordinator instance. - **labgrid-client** An image with the Labgrid client tools and pytest integration. @@ -22,13 +22,14 @@ Example showing how to build labgrid-client image: .. code-block:: bash - $ docker build --target labgrid-client -t labgrid-client -f dockerfiles/Dockerfile . + $ docker build --target labgrid-client -t docker.io/labgrid/client -f dockerfiles/Dockerfile . Using `BuildKit `_ is recommended to reduce build times. -You can also choose to build all 3 images, -with the included script. +You can also choose to build all 3 images with the included script. The script +will automatically use `docker buildx +`` if available. .. code-block:: bash @@ -43,15 +44,13 @@ The script supports ``podman`` as well. $ ./dockerfiles/build.sh It builds for the native platform by default. However, building -for foreign platforms is also supported using `docker buildx -` or `podman -buildx ` -by passing the platform of choice, e.g. `linux/arm64`. +for foreign platforms is also supported by passing the platform(s) of choice, +e.g. `linux/arm64` as an additional argument. .. code-block:: bash $ pip install --upgrade setuptools_scm - $ ./dockerfiles/build.sh linux/arm64 + $ ./dockerfiles/build.sh --platform linux/arm64 Usage @@ -65,19 +64,19 @@ No policy or configuration is done. labgrid-coordinator usage ~~~~~~~~~~~~~~~~~~~~~~~~~ -The labgrid-coordinator comes with a preconfigured Crossbar.io server. +The labgrid-coordinator image can be used to run a coordinator instance. -It listens to port 20408, +It listens on port 20408, so you probably want to publish that so you can talk to the coordinator. -State is written to ``/opt/crossbar``. +State is written to ``/opt/coordinator``. You might want to bind a volume to that -so you can restart the service without loosing state. +so you can restart the service without losing state. .. code-block:: bash - $ docker run -t -p 20408:20408 -v $HOME/crossbar:/opt/crossbar \ - labgrid-coordinator + $ docker run -t -p 20408:20408 -v $HOME/coordinator:/opt/coordinator \ + docker.io/labgrid/coordinator labgrid-client usage @@ -86,18 +85,18 @@ labgrid-client usage The labgrid-client image can be used to run ``labgrid-client`` and ``pytest`` commands. For example listing available places registered at coordinator at -ws://192.168.1.42:20408/ws +192.168.1.42:20408 .. code-block:: bash - $ docker run -e LG_CROSSBAR=ws://192.168.1.42:20408/ws labgrid-client \ + $ docker run -e LG_COORDINATOR=192.168.1.42:20408 docker.io/labgrid/client \ labgrid-client places Or running all pytest/labgrid tests at current directory: .. code-block:: bash - $ docker run -e LG_CROSSBAR=ws://192.168.1.42:20408/ws labgrid-client \ + $ docker run -e LG_COORDINATOR=192.168.1.42:20408 docker.io/labgrid/client \ pytest @@ -114,9 +113,9 @@ Start it with something like: .. code-block:: bash - $ docker run -e LG_CROSSBAR=ws://192.168.1.42:20408/ws \ + $ docker run -e LG_COORDINATOR=192.168.1.42:20408 \ -v $HOME/exporter-conf:/opt/conf \ - labgrid-exporter + docker.io/labgrid/exporter If using ser2net or if "exporting" e.g. a serial device, the devices needed must be added to Docker container (``docker run --device`` option). @@ -136,22 +135,16 @@ create a setup with the following instances The environment serves both to allow checking if the environment still function after changes, and can act as an example how to configure the docker images needed to run a minimal setup. -To use the staging environment to conduct a smoke test first build the images as instructed below: - -.. code-block:: bash - - $ pip install --upgrade setuptools_scm - $ ./dockerfiles/build.sh - -Then use docker compose to start all services except the client: +To use the staging environment to conduct a smoke test, first run docker compose to start all services except the +client: .. code-block:: bash $ cd dockerfiles/staging - $ CURRENT_UID=$(id -u):$(id -g) docker-compose up -d coordinator exporter dut + $ CURRENT_UID=$(id -u):$(id -g) docker compose up -d coordinator exporter dut To run the smoke test just run the client: .. code-block:: bash - $ docker-compose up client + $ docker compose up client diff --git a/dockerfiles/build.sh b/dockerfiles/build.sh index 3ab140f22..74aadf239 100755 --- a/dockerfiles/build.sh +++ b/dockerfiles/build.sh @@ -1,6 +1,8 @@ #!/bin/bash export DOCKER_BUILDKIT=1 +: "${IMAGE_PREFIX:=docker.io/labgrid/}" +: "${IMAGE_TAG:=latest}" die () { local msg @@ -22,14 +24,14 @@ has_docker() { } has_podman() { - command -v podman /dev/null 2>&1 + command -v podman >/dev/null 2>&1 } has_buildx() { local docker_cmd docker_cmd="${1}" - "${docker_cmd}" buildx --help >/dev/null 2>&1 + "${docker_cmd}" buildx version >/dev/null 2>&1 } get_docker_cmd() { @@ -53,12 +55,14 @@ perform_regular_build() { docker_cmd="${1}" script_dir="${2}" version="${3}" + extra_args=("${@:4}") log_info "building for native platform only." for t in client exporter coordinator; do "${docker_cmd}" build --build-arg VERSION="${version}" \ - --target labgrid-${t} -t labgrid-${t}:latest -f "${script_dir}/Dockerfile" . + --target labgrid-${t} -t "${IMAGE_PREFIX}${t}:${IMAGE_TAG}" -f "${script_dir}/Dockerfile" \ + "${extra_args[@]}" . done } @@ -67,16 +71,17 @@ perform_docker_buildx_build() { docker_cmd="${1}" script_dir="${2}" version="${3}" + extra_args=("${@:4}") for t in client exporter coordinator; do - "${docker_cmd}" buildx build --platform "${platform}" --build-arg VERSION="${version}" \ - --target labgrid-${t} -t labgrid-${t}:latest -f "${script_dir}/Dockerfile" . + "${docker_cmd}" buildx build --build-arg VERSION="${version}" \ + --target labgrid-${t} -t "${IMAGE_PREFIX}${t}:${IMAGE_TAG}" -f "${script_dir}/Dockerfile" \ + "${extra_args[@]}" . done } main() { - local platform script_dir version - platform="${1}" + local script_dir version if ! has_docker && ! has_podman; then die "Neither docker nor podman could be found." @@ -88,11 +93,11 @@ main() { cd "${script_dir}/.." || die "Could not cd into repo root dir" - if has_buildx "${docker_cmd}" && [ -n "${platform}" ]; then - perform_docker_buildx_build "${docker_cmd}" "${script_dir}" "${version}" "${platform}" + if has_buildx "${docker_cmd}"; then + perform_docker_buildx_build "${docker_cmd}" "${script_dir}" "${version}" "${@}" else - perform_regular_build "${docker_cmd}" "${script_dir}" "${version}" + perform_regular_build "${docker_cmd}" "${script_dir}" "${version}" "${@}" fi } -main "${1}" +main "${@}" diff --git a/dockerfiles/staging/crossbar/places_example.yaml b/dockerfiles/staging/coordinator/places_example.yaml similarity index 100% rename from dockerfiles/staging/crossbar/places_example.yaml rename to dockerfiles/staging/coordinator/places_example.yaml diff --git a/dockerfiles/staging/docker-compose.yml b/dockerfiles/staging/docker-compose.yml index a5c906944..b78c648b6 100644 --- a/dockerfiles/staging/docker-compose.yml +++ b/dockerfiles/staging/docker-compose.yml @@ -1,15 +1,14 @@ -version: '3.3' services: coordinator: - image: "labgrid-coordinator" + image: "${IMAGE_PREFIX:-docker.io/labgrid/}coordinator" volumes: - - "./crossbar:/home/root/crossbar" + - "./coordinator:/home/root/coordinator" tty: true network_mode: "host" - command: bash -c "cp /home/root/crossbar/places_example.yaml /opt/crossbar/places.yaml && - /opt/labgrid/crossbar-venv/bin/crossbar start --config /opt/labgrid/.crossbar/config-anonymous.yaml" + command: bash -c "cp /home/root/coordinator/places_example.yaml /opt/coordinator/places.yaml && + /usr/local/bin/labgrid-coordinator" client: - image: "labgrid-client" + image: "${IMAGE_PREFIX:-docker.io/labgrid/}client" volumes: - "./client/simple-test:/simple-test" - "./client/.ssh:/root/.ssh" @@ -17,12 +16,12 @@ services: stdin_open: true network_mode: "host" tmpfs: "/tmp" - # Use wait-for-it to ensure exporter service is up, as exporter is assuming exporter to + # Wait until coordinator is up # Use labgrid-client r to ensure the exporter has populated the resource list in the coordinator # Use sleep to fix the problem that sometimes the coordinator is not ready even though the service is up command: timeout 60 bash -c "set -e && cd /simple-test && - /opt/wait-for-it/wait-for-it.sh 127.0.0.1:20408 && + until echo > /dev/tcp/localhost/20408; do sleep 1; done && sleep 5 && while [ -z $$(/usr/local/bin/labgrid-client r) ]; do echo 'Wait one sec on coordinator' && sleep 1; done && /usr/local/bin/labgrid-client -p example-place lock && @@ -33,7 +32,7 @@ services: - exporter - dut exporter: - image: "labgrid-exporter" + image: "${IMAGE_PREFIX:-docker.io/labgrid/}exporter" volumes: - "./exporter-conf:/opt/conf" - "/run/udev:/run/udev:ro" @@ -43,7 +42,7 @@ services: network_mode: "host" stdin_open: true command: bash -c "set -e && - /opt/wait-for-it/wait-for-it.sh 127.0.0.1:20408 -- labgrid-exporter /opt/conf/exporter.yaml" + until echo > /dev/tcp/localhost/20408; do sleep 1; done && labgrid-exporter /opt/conf/exporter.yaml" dut: build: context: "./dut" diff --git a/dockerfiles/staging/dut/Dockerfile b/dockerfiles/staging/dut/Dockerfile index ec76c4d3d..cd1a71dc9 100644 --- a/dockerfiles/staging/dut/Dockerfile +++ b/dockerfiles/staging/dut/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bullseye-slim +FROM debian:bookworm-slim MAINTAINER "Kasper Revsbech" @@ -15,7 +15,7 @@ RUN set -e ;\ sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd # SSH login fix. Otherwise user is kicked off after login -COPY [--chown=root:root] ./authorized_keys /root/.ssh/authorized_keys +COPY --chown=root:root ./authorized_keys /root/.ssh/authorized_keys # As sshd scrubs ENV variables if they are set by the ENV varibale ensure to put the into /etc/profile as shown below ENV NOTVISIBLE "in users profile" diff --git a/examples/barebox/conftest.py b/examples/barebox/conftest.py index 151cacb1b..f0f6af199 100644 --- a/examples/barebox/conftest.py +++ b/examples/barebox/conftest.py @@ -1,8 +1,8 @@ import pytest -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def command(target): - barebox = target.get_driver('CommandProtocol') + barebox = target.get_driver("CommandProtocol") target.activate(barebox) return barebox diff --git a/examples/barebox/test_barebox.py b/examples/barebox/test_barebox.py index 271ac17ff..b9ebf9c4b 100644 --- a/examples/barebox/test_barebox.py +++ b/examples/barebox/test_barebox.py @@ -1,11 +1,11 @@ def test_barebox(command): - stdout, stderr, returncode = command.run('version') + stdout, stderr, returncode = command.run("version") assert returncode == 0 assert stdout assert not stderr - assert 'barebox' in '\n'.join(stdout) + assert "barebox" in "\n".join(stdout) - stdout, stderr, returncode = command.run('false') + stdout, stderr, returncode = command.run("false") assert returncode == 1 assert not stdout assert not stderr diff --git a/examples/barebox/test_bootchooser.py b/examples/barebox/test_bootchooser.py index 57e68e2e6..76498b3b4 100644 --- a/examples/barebox/test_bootchooser.py +++ b/examples/barebox/test_bootchooser.py @@ -2,10 +2,10 @@ def test_bootchooser(command): - stdout, stderr, returncode = command.run('bootchooser -i') + stdout, stderr, returncode = command.run("bootchooser -i") if returncode == 127: pytest.skip("bootchooser command not available") assert returncode == 0 assert not stderr - assert stdout[0].startswith('Good targets') - assert stdout[1] != 'none' + assert stdout[0].startswith("Good targets") + assert stdout[1] != "none" diff --git a/examples/barebox/test_sleep.py b/examples/barebox/test_sleep.py index 1df4ce66e..b7f654243 100644 --- a/examples/barebox/test_sleep.py +++ b/examples/barebox/test_sleep.py @@ -6,14 +6,14 @@ def test_sleep(command): # measure the round-trip-time timestamp = monotonic() - stdout, stderr, returncode = command.run('true') + stdout, stderr, returncode = command.run("true") elapsed_true = monotonic() - timestamp assert returncode == 0 assert not stdout assert not stderr timestamp = monotonic() - stdout, stderr, returncode = command.run('sleep 1') + stdout, stderr, returncode = command.run("sleep 1") elapsed_sleep = monotonic() - timestamp assert returncode == 0 assert not stdout diff --git a/examples/barebox/test_state.py b/examples/barebox/test_state.py index 13e397dcf..49415cdcf 100644 --- a/examples/barebox/test_state.py +++ b/examples/barebox/test_state.py @@ -2,10 +2,10 @@ def test_state(command): - stdout, stderr, returncode = command.run('state') + stdout, stderr, returncode = command.run("state") if returncode == 127: pytest.skip("state command not available") assert returncode == 0 assert not stderr - assert stdout[0] == 'registered state instances:' + assert stdout[0] == "registered state instances:" assert len(stdout) > 1 diff --git a/examples/barebox/test_watchdog.py b/examples/barebox/test_watchdog.py index 43b75183b..2d0b58a86 100644 --- a/examples/barebox/test_watchdog.py +++ b/examples/barebox/test_watchdog.py @@ -2,7 +2,7 @@ def test_watchdog(command): - stdout, stderr, returncode = command.run('wd 1') + stdout, stderr, returncode = command.run("wd 1") if returncode == 127: pytest.skip("wd command not available") assert returncode == 0 @@ -11,6 +11,6 @@ def test_watchdog(command): command._await_prompt() - stdout = command.run_check('echo ${global.system.reset}') + stdout = command.run_check("echo ${global.system.reset}") assert len(stdout) == 1 - assert stdout[0] == 'WDG' + assert stdout[0] == "WDG" diff --git a/examples/deditec-relais8/deditec.py b/examples/deditec-relais8/deditec.py index 189cbefe8..b007994de 100644 --- a/examples/deditec-relais8/deditec.py +++ b/examples/deditec-relais8/deditec.py @@ -1,24 +1,19 @@ -import sys -import labgrid import logging import time -from labgrid import Environment, StepReporter -from labgrid.strategy.bareboxstrategy import Status -from labgrid.driver.deditecrelaisdriver import DeditecRelaisDriver +from labgrid import Target +from labgrid.logging import basicConfig, StepLogger +from labgrid.resource.udev import DeditecRelais8 +from labgrid.driver import DeditecRelaisDriver -# enable debug logging -logging.basicConfig( - level=logging.DEBUG, - format='%(levelname)7s: %(message)s', - stream=sys.stderr, -) +# enable info logging +basicConfig(level=logging.INFO) -# show labgrid steps on the console -StepReporter.start() +# log labgrid steps +StepLogger.start() -t = labgrid.Target('main') -r = labgrid.resource.udev.DeditecRelais8(t, name=None, index=1) +t = Target("main") +r = DeditecRelais8(t, name=None, index=1) d = DeditecRelaisDriver(t, name=None) p = t.get_driver("DigitalOutputProtocol") diff --git a/examples/deditec-relais8/deditec_remote.py b/examples/deditec-relais8/deditec_remote.py index b9f2ca16c..c0e45e385 100644 --- a/examples/deditec-relais8/deditec_remote.py +++ b/examples/deditec-relais8/deditec_remote.py @@ -1,23 +1,16 @@ -import sys -import labgrid import logging import time -from labgrid import Environment, StepReporter -from labgrid.strategy.bareboxstrategy import Status -from labgrid.driver.deditecrelaisdriver import DeditecRelaisDriver +from labgrid import Environment +from labgrid.logging import basicConfig, StepLogger -# enable debug logging -logging.basicConfig( - level=logging.DEBUG, - format='%(levelname)7s: %(message)s', - stream=sys.stderr, -) +# enable info logging +basicConfig(level=logging.INFO) # show labgrid steps on the console -StepReporter.start() +StepLogger.start() -e = labgrid.Environment('import-dedicontrol.yaml') +e = Environment("import-dedicontrol.yaml") t = e.get_target() p = t.get_driver("DigitalOutputProtocol") diff --git a/examples/deditec-relais8/import-dedicontrol.yaml b/examples/deditec-relais8/import-dedicontrol.yaml index 0e8607962..9af4776e9 100644 --- a/examples/deditec-relais8/import-dedicontrol.yaml +++ b/examples/deditec-relais8/import-dedicontrol.yaml @@ -6,4 +6,4 @@ targets: drivers: DeditecRelaisDriver: {} options: - crossbar_url: 'ws://labgrid:20408/ws' + coordinator_address: 'labgrid:20408' diff --git a/examples/docker/conftest.py b/examples/docker/conftest.py index d4ec68ada..46bacd6de 100644 --- a/examples/docker/conftest.py +++ b/examples/docker/conftest.py @@ -1,9 +1,9 @@ import pytest -@pytest.fixture(scope='session') + +@pytest.fixture(scope="session") def command(target): - strategy = target.get_driver('DockerStrategy') - strategy.transition("shell") - shell = target.get_driver('CommandProtocol') + strategy = target.get_driver("DockerStrategy") + strategy.transition("accessible") + shell = target.get_driver("CommandProtocol") return shell - diff --git a/examples/docker/test_shell.py b/examples/docker/test_shell.py index 50b9b7d5d..450af06bb 100644 --- a/examples/docker/test_shell.py +++ b/examples/docker/test_shell.py @@ -1,11 +1,11 @@ def test_shell(command): - stdout, stderr, returncode = command.run('cat /proc/version') + stdout, stderr, returncode = command.run("cat /proc/version") assert returncode == 0 assert len(stdout) > 0 assert len(stderr) == 0 - assert 'Linux' in stdout[0] + assert "Linux" in stdout[0] - stdout, stderr, returncode = command.run('false') + stdout, stderr, returncode = command.run("false") assert returncode != 0 assert len(stdout) == 0 assert len(stderr) == 0 diff --git a/examples/library/test.py b/examples/library/test.py index f0b060218..e2851eaed 100755 --- a/examples/library/test.py +++ b/examples/library/test.py @@ -4,29 +4,28 @@ import sys import logging -from labgrid import Environment, StepReporter +from labgrid import Environment +from labgrid.logging import basicConfig, StepLogger from labgrid.strategy.bareboxstrategy import Status -# enable debug logging -logging.basicConfig( - level=logging.DEBUG, - format='%(levelname)7s: %(message)s', - stream=sys.stderr, -) +# enable info logging +basicConfig(level=logging.INFO) + +# log labgrid steps +StepLogger.start() -# show labgrid steps on the console -StepReporter.start() def run_once(target): - s = target.get_driver('BareboxStrategy') + s = target.get_driver("BareboxStrategy") s.status = Status.unknown # force a power-cycle - s.transition('barebox') - cmd = target['CommandProtocol'] - cmd.run_check('test -e /dev/nand0') + s.transition("barebox") + cmd = target["CommandProtocol"] + cmd.run_check("test -e /dev/nand0") target.deactivate(cmd) + env = Environment(sys.argv[1]) -target = env.get_target('main') +target = env.get_target("main") while True: run_once(target) diff --git a/examples/modbusrtu/conftest.py b/examples/modbusrtu/conftest.py index 4d28384ff..cf9696a98 100644 --- a/examples/modbusrtu/conftest.py +++ b/examples/modbusrtu/conftest.py @@ -1,7 +1,7 @@ import pytest -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def instrument(target): - _modbus = target.get_driver('ModbusRTUDriver') + _modbus = target.get_driver("ModbusRTUDriver") return _modbus diff --git a/examples/network-test/env.yaml b/examples/network-test/env.yaml new file mode 100644 index 000000000..1f8b9b10f --- /dev/null +++ b/examples/network-test/env.yaml @@ -0,0 +1,14 @@ +targets: + main: + resources: + NetworkService: + address: 192.168.1.5 + username: root + NetworkInterface: + ifname: enp2s0f3 + drivers: + SSHDriver: {} + RawNetworkInterfaceDriver: {} + options: + local_iface_to_dut_iface: + enp2s0f3: uplink diff --git a/examples/network-test/pkg-replay-record.py b/examples/network-test/pkg-replay-record.py new file mode 100755 index 000000000..6cbcf3d3f --- /dev/null +++ b/examples/network-test/pkg-replay-record.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# Generates an Ethernet frame via scapy using pcap, copies pcap to DUT, replays pcap on interface, +# records frame locally (or on exporter, adjust env.yaml accordingly), and compares both. + +import logging +import os +from tempfile import NamedTemporaryFile, TemporaryDirectory + +from scapy.all import Ether, Raw, rdpcap, wrpcap, conf + +from labgrid import Environment +from labgrid.logging import basicConfig, StepLogger + + +def generate_frame(): + frame = Ether(dst="11:22:33:44:55:66", src="66:55:44:33:22:11", type=0x9000) + padding = "\x00" * (conf.min_pkt_size - len(frame)) + frame /= Raw(load=padding) + return frame + + +basicConfig(level=logging.INFO) +StepLogger.start() +env = Environment("env.yaml") +target = env.get_target() + +netdrv = target.get_driver("RawNetworkInterfaceDriver") +ssh = target.get_driver("SSHDriver") + +# get DUT interface +exporter_iface = netdrv.iface.ifname +dut_iface = env.config.get_target_option(target.name, "local_iface_to_dut_iface")[exporter_iface] + +# generate test frame +generated_frame = generate_frame() + +# write pcap, copy to DUT +remote_pcap = "/tmp/pcap" +with NamedTemporaryFile() as pcap: + wrpcap(pcap.name, generated_frame) + ssh.put(pcap.name, remote_pcap) + +# copy recorded pcap from DUT, compare with generated frame +with TemporaryDirectory() as tempdir: + # start record on exporter + tempf = os.path.join(tempdir, "record.pcap") + with netdrv.record(tempf, count=1) as record: + # replay pcap on DUT + ssh.run_check(f"ip link set {dut_iface} up") + ssh.run_check(f"tcpreplay -i {dut_iface} {remote_pcap}") + + remote_frame = rdpcap(tempf) + assert remote_frame[0] == generated_frame[0] + +print("statistics", netdrv.get_statistics()) +print("address", netdrv.get_address()) diff --git a/examples/networkmanager/nm.env b/examples/networkmanager/nm.env index c96ef21da..be767f2fb 100644 --- a/examples/networkmanager/nm.env +++ b/examples/networkmanager/nm.env @@ -6,4 +6,4 @@ targets: drivers: NetworkInterfaceDriver: {} options: - crossbar_url: 'ws://labgrid/ws' + coordinator_address: 'labgrid:20408' diff --git a/examples/networkmanager/nm.py b/examples/networkmanager/nm.py index 587e39085..505364d50 100644 --- a/examples/networkmanager/nm.py +++ b/examples/networkmanager/nm.py @@ -1,86 +1,82 @@ -import logging, sys +import logging from pprint import pprint -from labgrid import * +from labgrid import Environment +from labgrid.logging import basicConfig, StepLogger # enable debug logging -logging.basicConfig( - level=logging.DEBUG, - format='%(levelname)7s: %(message)s', - stream=sys.stderr, -) +basicConfig(level=logging.DEBUG) # show labgrid steps on the console -StepReporter.start() +StepLogger.start() -e = Environment('nm.env') +e = Environment("nm.env") t = e.get_target() -d = t.get_driver('NetworkInterfaceDriver') +d = t.get_driver("NetworkInterfaceDriver") # based on https://developer.gnome.org/NetworkManager/stable/ch01.html, but adapted to python dicts s_client = { - 'connection': { - 'type': "802-11-wireless", + "connection": { + "type": "802-11-wireless", }, - '802-11-wireless': { - 'mode': "infrastructure", - 'ssid': "local-rpi", + "802-11-wireless": { + "mode": "infrastructure", + "ssid": "local-rpi", }, - '802-11-wireless-security': { - 'key-mgmt': "wpa-psk", - 'psk': "obMinwyurArc5", + "802-11-wireless-security": { + "key-mgmt": "wpa-psk", + "psk": "obMinwyurArc5", }, - 'ipv4': { - 'method': "auto", - 'ignore-auto-dns': True, - 'ignore-auto-routes': True, - 'never-default': True, + "ipv4": { + "method": "auto", + "ignore-auto-dns": True, + "ignore-auto-routes": True, + "never-default": True, }, - 'ipv6': { - 'method': "link-local", + "ipv6": { + "method": "link-local", }, } s_ap = { - 'connection': { - 'type': "802-11-wireless", + "connection": { + "type": "802-11-wireless", }, - '802-11-wireless': { - 'mode': "ap", - 'ssid': "local-rpi", + "802-11-wireless": { + "mode": "ap", + "ssid": "local-rpi", }, - '802-11-wireless-security': { - 'key-mgmt': "wpa-psk", - 'psk': "obMinwyurArc5", + "802-11-wireless-security": { + "key-mgmt": "wpa-psk", + "psk": "obMinwyurArc5", }, - 'ipv4': { + "ipv4": { #'method': "auto", #'method': "link-local", - 'method': "shared", - 'addresses': ["172.16.0.2/29"], + "method": "shared", + "addresses": ["172.16.0.2/29"], }, - 'ipv6': { - 'method': "link-local", + "ipv6": { + "method": "link-local", }, } d.disable() -d.wait_state('disconnected') +d.wait_state("disconnected") print("access points after scan") pprint(d.get_access_points()) d.configure(s_ap) -d.wait_state('activated') +d.wait_state("activated") print("settings in AP mode") pprint(d.get_settings()) print("state in AP mode") pprint(d.get_state()) -#d.configure(s_client) -#d.wait_state('activated') -#print("settings in client mode") -#pprint(d.get_settings()) -#print("state in client mode") -#pprint(d.get_state()) - +# d.configure(s_client) +# d.wait_state('activated') +# print("settings in client mode") +# pprint(d.get_settings()) +# print("state in client mode") +# pprint(d.get_state()) diff --git a/examples/power/power_example.py b/examples/power/power_example.py index 82a8ac6f5..4fc5fed68 100644 --- a/examples/power/power_example.py +++ b/examples/power/power_example.py @@ -3,7 +3,7 @@ @pytest.fixture() def pdu(target): - return target.get_driver('NetworkPowerDriver') + return target.get_driver("NetworkPowerDriver") def test_something(pdu): diff --git a/examples/pyvisa/pyvisa_example.py b/examples/pyvisa/pyvisa_example.py index 16743ef40..da37ca052 100644 --- a/examples/pyvisa/pyvisa_example.py +++ b/examples/pyvisa/pyvisa_example.py @@ -3,13 +3,13 @@ @pytest.fixture() def signal_generator(target): - return target.get_driver('PyVISADriver').get_session() + return target.get_driver("PyVISADriver").get_session() def test_with_signal_generator_example(signal_generator): - signal_generator.write('*RST') + signal_generator.write("*RST") # Setup channel 1 - signal_generator.write('C1:BSWV WVTP,SQUARE,HLEV,5,LLEV,0,DUTY,50') + signal_generator.write("C1:BSWV WVTP,SQUARE,HLEV,5,LLEV,0,DUTY,50") # Switch on channel 1 - signal_generator.write('C1:OUTP ON,LOAD,HZ,PLRT,NOR') + signal_generator.write("C1:OUTP ON,LOAD,HZ,PLRT,NOR") diff --git a/examples/qemu-networking/qemunetworkstrategy.py b/examples/qemu-networking/qemunetworkstrategy.py index 995223494..48becd0da 100644 --- a/examples/qemu-networking/qemunetworkstrategy.py +++ b/examples/qemu-networking/qemunetworkstrategy.py @@ -17,7 +17,7 @@ import attr from labgrid import target_factory, step -from labgrid.strategy import Strategy +from labgrid.strategy import Strategy, StrategyError from labgrid.util import get_free_port @@ -75,7 +75,7 @@ def update_network_service(self): networkservice.port = local_port else: networkservice.address = new_address - networkserivce.port = self.__remote_port + networkservice.port = self.__remote_port @step(args=["state"]) def transition(self, state, *, step): @@ -83,7 +83,7 @@ def transition(self, state, *, step): state = Status[state] if state == Status.unknown: - raise StrategyError(f"can not transition to {new_status}") + raise StrategyError(f"can not transition to {state}") elif self.status == state: step.skip("nothing to do") @@ -99,4 +99,4 @@ def transition(self, state, *, step): self.target.activate(self.shell) self.update_network_service() - self.status = status + self.status = state diff --git a/examples/qemu-networking/test_qemu_networking.py b/examples/qemu-networking/test_qemu_networking.py index 8f256128b..c1cc971b3 100644 --- a/examples/qemu-networking/test_qemu_networking.py +++ b/examples/qemu-networking/test_qemu_networking.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. + def test_shell(shell_command): shell_command.run("true") diff --git a/examples/remote/test_barebox.py b/examples/remote/test_barebox.py index e8ae6319d..4564c7a94 100644 --- a/examples/remote/test_barebox.py +++ b/examples/remote/test_barebox.py @@ -1,14 +1,14 @@ def test_target(target): - barebox = target.get_driver('CommandProtocol') + barebox = target.get_driver("CommandProtocol") target.activate(barebox) - stdout, stderr, returncode = barebox.run('version') + stdout, stderr, returncode = barebox.run("version") assert returncode == 0 assert stdout assert not stderr - assert 'barebox' in '\n'.join(stdout) + assert "barebox" in "\n".join(stdout) - stdout, stderr, returncode = barebox.run('false') + stdout, stderr, returncode = barebox.run("false") assert returncode == 1 assert not stdout assert not stderr diff --git a/examples/shell/conftest.py b/examples/shell/conftest.py index 22aecbdcf..c4ab03d57 100644 --- a/examples/shell/conftest.py +++ b/examples/shell/conftest.py @@ -1,8 +1,8 @@ import pytest -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def command(target): - shell = target.get_driver('CommandProtocol') + shell = target.get_driver("CommandProtocol") target.activate(shell) return shell diff --git a/examples/shell/test_hwclock.py b/examples/shell/test_hwclock.py index c774d0ee2..843c6fc83 100644 --- a/examples/shell/test_hwclock.py +++ b/examples/shell/test_hwclock.py @@ -3,7 +3,7 @@ def test_hwclock_rate(command): """Test that the hardware clock rate is not too inaccurate.""" - result = command.run_check('hwclock -c | head -n 3') + result = command.run_check("hwclock -c | head -n 3") hw_time, sys_time, freq_offset_ppm, tick = result[-1].strip().split() assert abs(int(freq_offset_ppm)) < 1000 @@ -15,11 +15,11 @@ def test_hwclock_value(command): """ def get_time(): - result = command.run_check('hwclock --utc --show')[0].strip() - return datetime.strptime(result, '%Y-%m-%d %H:%M:%S.%f+0:00') + result = command.run_check("hwclock --utc --show")[0].strip() + return datetime.strptime(result, "%Y-%m-%d %H:%M:%S.%f+0:00") def set_time(time): - time = time.strftime('%Y-%m-%d %H:%M:%S.%f+0:00') + time = time.strftime("%Y-%m-%d %H:%M:%S.%f+0:00") command.run_check(f'hwclock --utc --set --date "{time}"') offset = abs((get_time() - datetime.utcnow()).total_seconds()) diff --git a/examples/shell/test_memory.py b/examples/shell/test_memory.py index c4c8841bc..7c6477b2c 100644 --- a/examples/shell/test_memory.py +++ b/examples/shell/test_memory.py @@ -8,26 +8,26 @@ def test_memory_mbw(command): """Test memcopy bandwidth""" try: - command.run_check('which mbw') + command.run_check("which mbw") except ExecutionError: pytest.skip("mbw missing") - result = command.run_check('mbw -qt0 8M') + result = command.run_check("mbw -qt0 8M") result = result[-1].strip() pattern = r"AVG\s+.*Copy:\s+(?P\S+)\s+MiB/s" - bw, = map(float, re.fullmatch(pattern, result).groups()) + (bw,) = map(float, re.fullmatch(pattern, result).groups()) assert bw > 40 # > 40 MiB/second def test_memory_memtester_short(command): """Test RAM for errors""" try: - command.run_check('which memtester') + command.run_check("which memtester") except ExecutionError: pytest.skip("memtester missing") - result = command.run_check('memtester 128k 1 | tail -n 1') + result = command.run_check("memtester 128k 1 | tail -n 1") result = result[-1].strip() assert result == "Done." diff --git a/examples/shell/test_rt.py b/examples/shell/test_rt.py index 85824ea62..b2fa1b96f 100644 --- a/examples/shell/test_rt.py +++ b/examples/shell/test_rt.py @@ -8,11 +8,11 @@ def test_rt_cyclictest_short(command): """Test a basic cyclictest run""" try: - command.run_check('which cyclictest') + command.run_check("which cyclictest") except ExecutionError: pytest.skip("cyclictest missing") - result = command.run_check('cyclictest -SN -D 5 -q') + result = command.run_check("cyclictest -SN -D 5 -q") result = result[-1].strip() pattern = r"Min:\s+(?P\w+)\s+Act:\s+\w+\s+Avg:\s+(?P\w+)\s+Max:\s+(?P\w+)" @@ -25,13 +25,13 @@ def test_rt_cyclictest_short(command): def test_rt_hackbench_short(command): """Test a basic hackbench run""" try: - command.run_check('which hackbench') + command.run_check("which hackbench") except ExecutionError: pytest.skip("hackbench missing") - result = command.run_check('hackbench -f 10') + result = command.run_check("hackbench -f 10") result = result[-1].strip() pattern = r"Time:\s+(?P