feat: a public command channel and container engine access #18

Merged
christianmanivong merged 1 commits from feat/container-engine-channel into main 2026-10-07 16:01:51 +00:00
Owner

Access layer for NetOrk/netork#765, phase 1 (netork#769): the driver builds the connection, netOrk does the talking. No container model and no parsing here.

Channel (channel.py)

  • CommandChannelMixin declares run_command(command, *, privileged, timeout, stdin) -> CommandResult and open_stream(command, *, privileged) -> ByteStream, under TYPE_CHECKING like the role bases.
  • run_on_transport() / open_stream_on_transport(): paramiko exec channel, no PTY, separate stderr, real exit status. Usable by any SSH driver (Proxmox, OpenWrt later).
  • ParamikoExecStream drains stderr on every read (shared window) and keeps its last 64 KiB. write() never half-closes: dial-stdio answers an unfinished request with HTTP 499 after a half-close.

Container engine (container_engine.py)

  • ContainerEngineMixin.container_engines() -> [{engine, api}], probed with command -v.
  • open_container_engine(engine) -> ContainerEngineConnection with open_api() (<binary> system dial-stdio), run_cli(args) and stream_cli(args, merge_stderr=False). Arguments are quoted with shlex.join.
  • Hook _container_engine_binary(engine): QNAP returns its Container Station path.
  • Mixed in by drivers, not role bases, so a Windows OSDriver without a shell channel does not claim it.

One change against the issue text: a driver without a channel does not raise NotImplementedError from open_stream; it simply has no run_command/open_stream, so hasattr(driver, "open_stream") is the test (the role-base rule, as decided in the netork#765 plan). open_container_engine raises ContainerEngineUnavailable (a NotImplementedError) for an engine it cannot reach. Windows stays open for a follow-up issue.

Additive (2.6.0). Docker*Dict stays until netOrk stops reading get_docker_info (phase 9 of the plan, then 3.0.0).

Tests: test_channel.py, test_container_engine.py; suite 380 passed.

Merge before NAPALM/napalm-linux and NAPALM/napalm-qnap-qts, whose CI installs this package from main.

Priority: P3

Closes #17

Access layer for NetOrk/netork#765, phase 1 (netork#769): the driver builds the connection, netOrk does the talking. No container model and no parsing here. **Channel** (`channel.py`) - `CommandChannelMixin` declares `run_command(command, *, privileged, timeout, stdin) -> CommandResult` and `open_stream(command, *, privileged) -> ByteStream`, under `TYPE_CHECKING` like the role bases. - `run_on_transport()` / `open_stream_on_transport()`: paramiko exec channel, no PTY, separate stderr, real exit status. Usable by any SSH driver (Proxmox, OpenWrt later). - `ParamikoExecStream` drains stderr on every read (shared window) and keeps its last 64 KiB. `write()` never half-closes: `dial-stdio` answers an unfinished request with HTTP 499 after a half-close. **Container engine** (`container_engine.py`) - `ContainerEngineMixin.container_engines()` -> `[{engine, api}]`, probed with `command -v`. - `open_container_engine(engine) -> ContainerEngineConnection` with `open_api()` (`<binary> system dial-stdio`), `run_cli(args)` and `stream_cli(args, merge_stderr=False)`. Arguments are quoted with `shlex.join`. - Hook `_container_engine_binary(engine)`: QNAP returns its Container Station path. - Mixed in by drivers, not role bases, so a Windows `OSDriver` without a shell channel does not claim it. One change against the issue text: a driver without a channel does not raise `NotImplementedError` from `open_stream`; it simply has no `run_command`/`open_stream`, so `hasattr(driver, "open_stream")` is the test (the role-base rule, as decided in the netork#765 plan). `open_container_engine` raises `ContainerEngineUnavailable` (a `NotImplementedError`) for an engine it cannot reach. Windows stays open for a follow-up issue. Additive (2.6.0). `Docker*Dict` stays until netOrk stops reading `get_docker_info` (phase 9 of the plan, then 3.0.0). Tests: `test_channel.py`, `test_container_engine.py`; suite 380 passed. Merge before NAPALM/napalm-linux and NAPALM/napalm-qnap-qts, whose CI installs this package from main. Priority: P3 Closes #17
christianmanivong added 1 commit 2026-10-07 15:59:08 +00:00
feat: a public command channel and container engine access
CI / test (3.10) (push) Successful in 25s
CI / test (3.11) (push) Successful in 22s
CI / test (3.12) (push) Successful in 23s
CI / test (3.10) (pull_request) Successful in 24s
CI / test (3.11) (pull_request) Successful in 22s
CI / test (3.12) (pull_request) Successful in 22s
735b683028
netOrk reached a host's shell through napalm-linux's private `_send`: an
interactive PTY with stdout and stderr merged and no exit code. For Docker
it went further and opened its own paramiko connections around the driver.

This adds the access layer only, no container model (NetOrk/netork#765):

- `channel.py`: `CommandChannelMixin` declares `run_command()` (stdout,
  stderr, exit code) and `open_stream()` (a `ByteStream` to a running
  command). Declared under TYPE_CHECKING, so hasattr stays truthful.
  `run_on_transport()` and `open_stream_on_transport()` implement both on
  a paramiko exec channel for any SSH driver; `ParamikoExecStream` drains
  stderr on every read so the shared window never stalls.
- `container_engine.py`: `ContainerEngineMixin` with `container_engines()`
  and `open_container_engine()`. The returned `ContainerEngineConnection`
  opens the engine API over `<binary> system dial-stdio` and runs the CLI
  with caller-chosen arguments. The driver decides the binary through
  `_container_engine_binary()`; callers never see the path.
- README: why the container model lives in netOrk and not here.

Additive; the Docker*Dict declarations stay until netOrk no longer reads
them. Version 2.6.0.

Refs #17
christianmanivong merged commit 3aed0b48d7 into main 2026-10-07 16:01:51 +00:00
christianmanivong deleted branch feat/container-engine-channel 2026-10-07 16:01:51 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: NAPALM/napalm-device-types#18