FastAPI-based Remote Lab Manager for Netlab topologies to enable remote testing of Neops Function Blocks against virtual networking labs
Project description
Neops Remote Lab
[!IMPORTANT] Developer Preview Disclaimer
This repository is an early-stage developer preview and is not production-ready. While we are currently finalizing an open-source friendly license, all rights are reserved in the interim. We encourage you to explore the code, experiment with it, and share your feedback via issues or discussions. Use of this software is at your own risk and provided "as-is" without warranty.
A FastAPI service exposing exclusive, queue-brokered access to a real Netlab topology — drive it from a pytest11 plugin (Python), the bundled REST API (any stack), or both.
neops-remote-lab fronts a Netlab host with a small HTTP service and a pytest fixture. Every consumer asks for a session, waits in a FIFO queue, gets the lab, and tears it down when the last consumer walks away. Topologies are identified by their SHA-256 content hash, so byte-identical files share the running lab — and FRR, Nokia SR Linux, and Cisco IOL all work out of the box.
On PyPI · Docs · Worker SDK consumes it as a stable contract
Install
For the Python client / pytest fixture (library install) — pick whichever your project uses:
uv add neops-remote-lab # uv (recommended)
poetry add neops-remote-lab # Poetry
pip install neops-remote-lab # pip
For the runnable server CLI (isolated install):
uv tool install neops-remote-lab # uv (recommended)
pipx install neops-remote-lab # pipx
Picking between the two? Library install is what most consumers want — it gives you the pytest fixture and
RemoteLabClient. CLI install is for operators standing up the server itself.
Quick start
Use it (consumer)
export REMOTE_LAB_URL=http://lab.example.com:8000
from neops_remote_lab.testing.fixture import remote_lab_fixture
demo = remote_lab_fixture("tests/topologies/demo.yml")
def test_lab_has_two_devices(demo):
assert len(demo) == 2
pytest -v
The fixture handles session creation, queue waiting, topology upload, heartbeat,
and teardown. The Worker SDK imports remote_lab_fixture directly as a
stable public API.
Run it (operator)
neops-remote-lab # binds 0.0.0.0:8000 by default
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8000/healthz
# 204 = alive
netlab must be on PATH — the launcher refuses to start otherwise. For a fresh
host, see Netlab host setup;
for a systemd-managed deployment, see the
Operator runbook.
How a session flows
sequenceDiagram
participant ClientA as Client A
participant ClientB as Client B
participant Server
ClientA->>Server: POST /session
Server-->>ClientA: 201 session_id (ACTIVE)
ClientB->>Server: POST /session
Server-->>ClientB: 201 session_id (WAITING, position=1)
ClientA->>Server: POST /lab (topology.yml)
Note over Server: acquire lab (netlab up)
Server-->>ClientA: 200 lab acquired
loop heartbeat (until release)
ClientA->>Server: POST /session/heartbeat
Server-->>ClientA: 204
end
ClientB->>Server: GET /session/{id}
Server-->>ClientB: status=WAITING, position=1
ClientA->>Server: DELETE /session/{id}
Note over Server: tear down lab, promote next in queue
Server-->>ClientA: 204
ClientB->>Server: GET /session/{id}
Server-->>ClientB: status=ACTIVE, position=0
The full state machine, heartbeat cadence, and timeout semantics live on Session queue; SHA-256-keyed reuse semantics on Lab lifecycle.
Why use it
- One lab per host, enforced. Cross-process
filelock+ reference counting; two clients can't trample each other's topology. Topology identity is the SHA-256 of file content, so byte-identical files share a running lab. - Stable Python contract.
remote_lab_fixtureis the public surface the Worker SDK imports directly. - Driveable from any HTTP stack. pytest is convenient; cURL works; Go works; whatever you already have works.
- Operator-friendly. Single-instance
systemdunit, structured logs,/healthzand/debug/health, automatic recovery from stale Netlab instances on startup.
Environment variables
| Variable | Purpose | Default |
|---|---|---|
REMOTE_LAB_URL |
Base URL for client + fixtures | — (required) |
REMOTE_LAB_REQUEST_TIMEOUT |
Per-HTTP-request timeout (seconds) | 30 |
REMOTE_LAB_SESSION_TIMEOUT |
Client-side session-queue wait limit (seconds) | 600 |
REMOTE_LAB_ACQUISITION_TIMEOUT |
Max wait for lab acquisition (seconds) | 600 |
Timeout-coordination guidance + RemoteLabClient constructor overrides:
Client config.
Network access
The service ships without HTTP authentication — the only access boundary on
/lab/* is the X-Session-ID of an active session. Treat it as internal-trust;
deploy behind a network enclosure. The recommended path is self-hosted
Headscale + Headplane
because that's what the project's reference deployment uses, but any equivalent
enclosure works (managed Tailscale, plain WireGuard, an internal VLAN with IP
allowlists, mTLS at a reverse proxy). Walkthroughs and alternatives:
Headscale: quick setup
and its
Other approaches
section.
Troubleshooting
| Symptom | Likely cause + fix |
|---|---|
Server exits with Another Remote Lab Manager instance is already running. |
Stale single-instance filelock from a crashed prior run. See Stale-lock recovery. |
Address already in use on port 8000 |
Prior server didn't exit cleanly, or another service holds the port. lsof -i :8000 and kill, or start with --port. |
POST /lab returns 423 Locked |
Caller's session isn't ACTIVE, or a different topology owns the host. GET /session/{id} to check; release / force-destroy if stuck. |
Full table: Debugging.
Documentation
The full docs are at docs.neops.io/neops-remote-lab. Common entry points:
- Use from Python — pytest fixtures,
RemoteLabClient, client config. - Run the service — install,
systemd, security model, REST contract, debugging. - Deploy & Operate — Netlab host setup, VPN paths, vendor walkthroughs (FRR / SR Linux / IOL).
- Concepts — architecture, session queue, lab lifecycle, topology format.
- Cookbook — runnable end-to-end recipes.
Interactive OpenAPI UI is at http://<host>:8000/docs once the server is running.
Tests
make test # 39 tests; uses a stubbed LabManager (no Netlab needed)
Contributing
Branch from develop. make check runs lint, typecheck, audit, and tests. Full
contributor guide: Contributing.
License
See the Developer Preview Disclaimer at the top of this file. A formal open-source license will be applied once finalized.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file neops_remote_lab-1.4.0.tar.gz.
File metadata
- Download URL: neops_remote_lab-1.4.0.tar.gz
- Upload date:
- Size: 25.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b0c5a55ecf9f00808054d57fac497a839f55b925e1b15c3ccdc5f25e02a12dc4
|
|
| MD5 |
7f368784cf4bb39599e63ac731585bdb
|
|
| BLAKE2b-256 |
b2f6f16f2e2954b4181d3150a483b4cb516c19399688fc7ad8b9f73f9b29a00c
|
File details
Details for the file neops_remote_lab-1.4.0-py3-none-any.whl.
File metadata
- Download URL: neops_remote_lab-1.4.0-py3-none-any.whl
- Upload date:
- Size: 30.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ad4cd5a0183a0819f1f7cfa4a3fc769e27b59dfcd32abbec952f155d3583ad1
|
|
| MD5 |
9f1109421840bd717d5fcb210532f86d
|
|
| BLAKE2b-256 |
8fa5b69fe0f9f73fde4e410d3e4b89d0746b038d64b529211ee968bd7ab19ebc
|