Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into snmp-driver
Browse files Browse the repository at this point in the history
  • Loading branch information
bennyz committed Feb 10, 2025
2 parents 66241a9 + c653ff9 commit e8a480d
Show file tree
Hide file tree
Showing 30 changed files with 490 additions and 290 deletions.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ serve-docs:
clean-docs:
uv run --isolated --all-packages --group docs $(MAKE) -C docs clean

doctest:
uv run --isolated --all-packages --group docs $(MAKE) -C docs doctest

test-%: packages/%
uv run --isolated --directory $< pytest

Expand All @@ -33,7 +36,7 @@ clean-test:
sync:
uv sync --all-packages --all-extras

test: test-packages
test: test-packages doctest

generate:
buf generate
Expand Down
78 changes: 48 additions & 30 deletions docs/source/api-reference/adapters/network.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,55 +41,73 @@ export:
path: /tmp/test.sock
```
Forward a remote TCP port to a local TCP port
```{testcode}
# random port on localhost
with TcpPortforwardAdapter(client.tcp_port) as addr:
print(addr[0], addr[1]) # 127.0.0.1 38406

# specific address and port
with TcpPortforwardAdapter(client.tcp_port, local_host="192.0.2.1", local_port=8080) as addr:
print(addr[0], addr[1]) # 192.0.2.1 8080
### Forward a remote TCP port to a local TCP port
```{doctest}
>>> # random port on localhost
>>> with TcpPortforwardAdapter(client=client.tcp_port) as addr:
... print(addr[0], addr[1])
127.0.0.1 ...
>>>
>>> # specific address and port
>>> with TcpPortforwardAdapter(client=client.tcp_port, local_host="127.0.0.2", local_port=8080) as addr:
... print(addr[0], addr[1])
127.0.0.2 8080
```

Forward a remote Unix domain socket to a local socket
```{testcode}
with UnixPortforwardAdapter(client.unix_socket) as addr:
print(addr) # /tmp/jumpstarter-w30wxu64/socket

# the type of the remote socket and the local one doesn't have to match
# e.g. forward a remote Unix domain socket to a local TCP port
with TcpPortforwardAdapter(client.unix_socket) as addr:
print(addr[0], addr[1]) # 127.0.0.1 38406
### Forward a remote Unix domain socket to a local socket

```{doctest}
>>> with UnixPortforwardAdapter(client=client.unix_socket) as addr:
... print(addr)
/tmp/jumpstarter-.../socket
>>> # the type of the remote socket and the local one doesn't have to match
>>> # e.g. forward a remote Unix domain socket to a local TCP port
>>> with TcpPortforwardAdapter(client=client.unix_socket) as addr:
... print(addr[0], addr[1])
127.0.0.1 ...
```

Connect to a remote TCP port with a web-based VNC client

```{testcode}
with NovncAdapter(client.tcp_port) as url:
print(url) # https://novnc.com/noVNC/vnc.html?autoconnect=1&reconnect=1&host=127.0.0.1&port=36459
# open the url in browser to access the VNC client
```{doctest}
>>> with NovncAdapter(client=client.tcp_port) as url:
... print(url) # open the url in browser to access the VNC client
https://novnc.com/noVNC/vnc.html?autoconnect=1&reconnect=1&host=127.0.0.1&port=...
```

Interact with a remote TCP port as if it's a serial console

See [pexpect](https://pexpect.readthedocs.io/en/stable/api/fdpexpect.html) for API documentation

```{testcode}
with PexpectAdapter(client.tcp_port) as expect:
expect.expect("localhost login:")
expect.send("root\n")
expect.expect("Password:")
expect.send("secret\n")
```{doctest}
>>> # the server echos all inputs
>>> with PexpectAdapter(client=client.tcp_port) as expect:
... assert expect.send("hello") == 5 # written 5 bytes
... assert expect.expect(["hi", "hello"]) == 1 # found string at index 1
```

Connect to a remote TCP port with the fabric SSH client

See [fabric](https://docs.fabfile.org/en/latest/api/connection.html#fabric.connection.Connection) for API documentation

```{testcode}
:skipif: True
with FabricAdapter(client=client.tcp_port, connect_kwargs={"password": "secret"}) as conn:
conn.run("uname")
```

```{testsetup} *
from jumpstarter_driver_network.adapters import *
from jumpstarter_driver_network.driver import *
from jumpstarter_driver_composite.driver import Composite
from jumpstarter.common.utils import serve
instance = serve(Composite(children={"tcp_port": EchoNetwork(), "unix_socket": EchoNetwork()}))
client = instance.__enter__()
```

```{testcleanup} *
instance.__exit__(None, None, None)
```
13 changes: 4 additions & 9 deletions docs/source/api-reference/drivers.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,9 @@ This project is still evolving, so these docs may be incomplete or out-of-date.
```

## Example
```{testsetup} *
import jumpstarter.common.importlib
def import_class(class_path, allow, unsafe):
return globals()["ExampleClient"]
jumpstarter.common.importlib.import_class = import_class
```

```{testcode}
from sys import modules
from types import SimpleNamespace
from anyio import connect_tcp, sleep
from contextlib import asynccontextmanager
from collections.abc import Generator
Expand Down Expand Up @@ -74,6 +67,8 @@ class ExampleClient(DriverClient):
def echo_generator(self, message) -> Generator[str, None, None]:
yield from self.streamingcall("echo_generator", message)
modules["example"] = SimpleNamespace(ExampleClient=ExampleClient)
with serve(ExampleDriver()) as client:
print(client.echo("hello"))
assert list(client.echo_generator("hello")) == ["hello"] * 10
Expand Down
6 changes: 3 additions & 3 deletions docs/source/api-reference/drivers/can.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
The CAN driver is a driver for using CAN bus connections.

```{eval-rst}
.. autoclass:: jumpstarter_driver_can.client.CanClient
.. autoclass:: jumpstarter_driver_can.client.CanClient()
:members:
```

```{eval-rst}
.. autoclass:: jumpstarter_driver_can.client.IsoTpClient
.. autoclass:: jumpstarter_driver_can.client.IsoTpClient()
:members:
```
```
2 changes: 1 addition & 1 deletion docs/source/api-reference/drivers/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ can.md
pyserial.md
sdwire.md
snmp.md
tftp.md
ustreamer.md
yepkit.md
```
26 changes: 19 additions & 7 deletions docs/source/api-reference/drivers/pyserial.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export:
## PySerialClient API
```{eval-rst}
.. autoclass:: jumpstarter_driver_pyserial.client.PySerialClient
.. autoclass:: jumpstarter_driver_pyserial.client.PySerialClient()
:members: pexpect, open, stream, open_stream, close
```
Expand All @@ -41,20 +41,32 @@ Using expect without a context manager
session = pyserialclient.open()
session.sendline("Hello, world!")
session.expect("Hello, world!")
session.close()
pyserialclient.close()
```

Using a simple BlockingStream with a context manager
```{testcode}
with pyserialclient.stream() as stream:
stream.write(b"Hello, world!")
data = stream.read(13)
stream.send(b"Hello, world!")
data = stream.receive()
```

Using a simple BlockingStream without a context manager
```{testcode}
stream = pyserialclient.open_stream()
stream.write(b"Hello, world!")
data = stream.read(13)
stream.close()
stream.send(b"Hello, world!")
data = stream.receive()
```

```{testsetup} *
from jumpstarter_driver_pyserial.driver import PySerial
from jumpstarter.common.utils import serve
instance = serve(PySerial(url="loop://"))
pyserialclient = instance.__enter__()
```

```{testcleanup} *
instance.__exit__(None, None, None)
```
19 changes: 18 additions & 1 deletion docs/source/api-reference/drivers/sdwire.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,27 @@ The SDWire driver is an storgate multiplexer driver for using the SDWire
multiplexer. This device multiplexes an SD card between the DUT and the
exporter host.

## Driver Configuration

```{literalinclude} sdwire.yaml
:language: yaml
```

```{doctest}
:hide:
>>> from jumpstarter.config import ExporterConfigV1Alpha1DriverInstance
>>> ExporterConfigV1Alpha1DriverInstance.from_path("source/api-reference/drivers/sdwire.yaml").instantiate()
Traceback (most recent call last):
...
FileNotFoundError: failed to find sd-wire device
```

## Client API

The SDWire driver implements the `StorageMuxClient` class, which is a generic
storage class.

```{eval-rst}
.. autoclass:: jumpstarter_driver_opendal.client.StorageMuxClient
.. autoclass:: jumpstarter_driver_opendal.client.StorageMuxClient()
:members:
```
8 changes: 8 additions & 0 deletions docs/source/api-reference/drivers/sdwire.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
type: "jumpstarter_driver_sdwire.driver.SDWire"
config:
# optional serial number of the sd-wire device
# the first one found would be used if unset
serial: "sdw-00001"
# optional path to the block device exposed by sd-wire
# automatically detected if unset
storage_device: "/dev/disk/by-diskseq/1"
89 changes: 89 additions & 0 deletions docs/source/api-reference/drivers/tftp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# TFTP Driver

**driver**: `jumpstarter_driver_tftp.driver.Tftp`

The TFTP driver provides a read-only TFTP server that can be used to serve files.

## Driver Configuration
```yaml
export:
tftp:
type: jumpstarter_driver_tftp.driver.Tftp
config:
root_dir: /var/lib/tftpboot # Directory to serve files from
host: 192.168.1.100 # Host IP to bind to (optional)
port: 69 # Port to listen on (optional)
```
### Config parameters
| Parameter | Description | Type | Required | Default |
|-----------|-------------|------|----------|---------|
| root_dir | Root directory for the TFTP server | str | no | "/var/lib/tftpboot" |
| host | IP address to bind the server to | str | no | auto-detect |
| port | Port number to listen on | int | no | 69 |
## TftpServerClient API
```{eval-rst}
.. autoclass:: jumpstarter_driver_tftp.client.TftpServerClient()
:members:
:show-inheritance:
```
## Exception Classes
```{eval-rst}
.. autoclass:: jumpstarter_driver_tftp.driver.TftpError
:members:
:show-inheritance:

.. autoclass:: jumpstarter_driver_tftp.driver.ServerNotRunning
:members:
:show-inheritance:

.. autoclass:: jumpstarter_driver_tftp.driver.FileNotFound
:members:
:show-inheritance:
```
## Examples
```{doctest}
>>> import tempfile
>>> import os
>>> from jumpstarter_driver_tftp.driver import Tftp
>>> with tempfile.TemporaryDirectory() as tmp_dir:
... # Create a test file
... test_file = os.path.join(tmp_dir, "test.txt")
... with open(test_file, "w") as f:
... _ = f.write("hello")
...
... # Start TFTP server
... tftp = Tftp(root_dir=tmp_dir, host="127.0.0.1", port=6969)
... tftp.start()
...
... # List files
... files = tftp.list_files()
... assert "test.txt" in files
...
... tftp.stop()
```

```{testsetup} *
import tempfile
import os
from jumpstarter_driver_tftp.driver import Tftp
from jumpstarter.common.utils import serve
# Create a persistent temp dir that won't be removed by the example
TEST_DIR = tempfile.mkdtemp(prefix='tftp-test-')
instance = serve(Tftp(root_dir=TEST_DIR, host="127.0.0.1"))
client = instance.__enter__()
```

```{testcleanup} *
instance.__exit__(None, None, None)
import shutil
shutil.rmtree(TEST_DIR, ignore_errors=True)
```
19 changes: 18 additions & 1 deletion docs/source/api-reference/drivers/ustreamer.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,24 @@ The Ustreamer driver is a driver for using the ustreamer video streaming server
driven by the jumpstarter exporter. This driver takes a video device and
exposes both snapshot and streaming interfaces.

## Driver configuration

```{literalinclude} ustreamer.yaml
:language: yaml
```

```{doctest}
:hide:
>>> from jumpstarter.config import ExporterConfigV1Alpha1DriverInstance
>>> ExporterConfigV1Alpha1DriverInstance.from_path("source/api-reference/drivers/ustreamer.yaml").instantiate()
Traceback (most recent call last):
...
io.UnsupportedOperation: fileno
```

## Client API

```{eval-rst}
.. autoclass:: jumpstarter_driver_ustreamer.client.UStreamerClient
.. autoclass:: jumpstarter_driver_ustreamer.client.UStreamerClient()
:members:
```
8 changes: 8 additions & 0 deletions docs/source/api-reference/drivers/ustreamer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
type: "jumpstarter_driver_ustreamer.driver.UStreamer"
config:
# name or path of the ustreamer executable
# defaults to finding ustreamer from path
executable: "ustreamer"
args: # extra arguments to pass to ustreamer
brightness: auto # --brightness=auto
contrast: default # --contract=default
Loading

0 comments on commit e8a480d

Please sign in to comment.