netdryrun

Registre officiel Autres

Dry-run network changes in a real containerlab lab: BGP, routes, reachability, with evidence.

Installation

Ajoutez ceci à la configuration MCP de votre client (Claude Desktop : claude_desktop_config.json ; Cursor : .cursor/mcp.json ; VS Code et les autres utilisent la même forme « mcpServers »).

PYPI package netdryrun
Claude Code
claude mcp add netdryrun -- uvx netdryrun==0.3.0 mcp
Configuration JSON (Claude Desktop, Cursor, VS Code)
{
    "mcpServers": {
        "netdryrun": {
            "command": "uvx",
            "args": [
                "netdryrun==0.3.0",
                "mcp"
            ]
        }
    }
}

Documentation

Tirée du README du projet sur GitHub, actualisée chaque semaine. Les liens ouvrent GitHub.

Sommaire
  1. Why
  2. Quick start
  3. netdryrun.yml
  4. Commands
  5. GitHub Action
  6. For AI agents (MCP)
  7. How it compares
  8. Honest limits
  9. Roadmap
  10. License

Test network changes before they touch production.

Netdryrun™ boots your topology as a real virtual network (containerlab), applies your configs, and checks how the network actually behaves: BGP sessions, prefix counts, routes, reachability. It catches mistakes like route leaks before they reach production, and leaves evidence a reviewer can trust.

$ netdryrun run examples/frr-bgp-broken
Deploying lab ndr-frr-broken (3 nodes)...
  [PASS] r1: BGP session to 10.0.12.2 is established -- state is established (6.3s)
  [PASS] r1: ping 192.168.3.1 from 192.168.1.1 expects success -- 3/3 replies (2.3s)
  ... (3 more passing checks trimmed)
  [FAIL] r1: route 0.0.0.0/0 via bgp is absent -- unexpectedly present via bgp (not selected) (0.1s)
FAILED: 5 passed, 1 failed (6 checks)

That run caught a leaked default route that was received but not selected. A ping test or a config linter would have missed it.

Status: v0.3.0, early. Works today with FRR, Nokia SR Linux, SONiC (docker-sonic-vs) and Arista EOS (cEOS, bring your own image) on Linux and WSL2. On the verdict corpus (30 realistic BGP changes, expected verdicts committed before any run), Netdryrun judged 24/30 correctly, ping-only checks 20/30 and all-sessions-up checks 15/30. Feedback and issues are very welcome.

Why

  • It tests behavior, not syntax. Linters and config diffs can't tell you whether BGP will come up or whether a prefix leaks. A real control plane can.
  • It's built for CI. One GitHub Action, a pass/fail exit code, a Markdown summary in the job, JUnit XML, and a JSON evidence file with every command and raw output.
  • It doesn't lie about convergence. Positive checks wait for the network to converge. Negative checks ("this route must be absent") run only after convergence and must hold for a time window, so they can't pass just because BGP hasn't come up yet.

Quick start

Try it in your browser (no install)

The codespace comes with Docker, containerlab and Netdryrun already installed. When the terminal shows Ready, run:

bash examples/agent-demo/demo.sh

A route-leaking change is rejected, then the fix passes every check. It uses your GitHub account's free Codespaces hours (2-core machine). Details.

On your own machine

Requirements: Linux or WSL2, Docker, containerlab, Python 3.10+.

pip install netdryrun
git clone https://github.com/netdryrun/netdryrun && cd netdryrun
netdryrun run examples/frr-bgp          # 3 FRR routers, should pass
netdryrun run examples/srl-frr          # SR Linux + FRR, should pass
netdryrun run examples/frr-bgp-broken   # a bad change, should fail
On your own containerlab topology (3 commands)
netdryrun record my-lab/topology.clab.yml --deploy   # snapshot today's behavior into my-lab/netdryrun.yml
# ...edit your configs (the change you want to test)...
netdryrun run my-lab                                   # did anything break?

record writes a baseline of every established BGP session, prefix count and BGP route. Review it, commit it, and every future change is tested against it, locally or in CI.

netdryrun.yml

lab: topology.clab.yml      # your containerlab topology
timeout: 90                 # seconds a positive check may wait for convergence

checks:
  - type: bgp_session       # session reaches a state (default: established)
    node: r1
    neighbor: 10.0.12.2
  - type: bgp_prefixes      # accepted prefix count within a range
    node: r1
    neighbor: 10.0.12.2
    min: 1
    max: 10
  - type: route             # route present (or absent), optionally via a protocol
    node: r1
    prefix: 192.168.3.0/24
    protocol: bgp
  - type: ping              # reachability, optionally from a source address
    node: r1
    source: 192.168.1.1
    target: 192.168.3.1
  - type: route             # negative check: must stay absent after convergence
    node: r1
    prefix: 0.0.0.0/0
    protocol: bgp
    present: false

Failure scenarios break something, check the network still behaves, then restore it. They can take links down, or make an FRR neighbor announce routes it shouldn't, to prove a max-prefix limit or an import filter actually holds:

scenarios:
  - name: r1-r2 link fails
    link_down: {node: r1, interface: eth1}
    checks:
      - {type: ping, node: r1, source: 192.168.1.1, target: 192.168.2.1}   # rerouted via r3
      - {type: bgp_session, node: r1, neighbor: 10.0.12.2, state: down}
  - name: customer floods 200 prefixes
    inject_routes: {node: ce1, count: 200, from: 10.100.16.0/24}
    checks:
      - {type: bgp_prefixes, node: up1, neighbor: 10.0.2.1, max: 51}   # the limit stops the flood

Every check also accepts name, timeout and vrf. Ping accepts expect: failure and count. Drivers are detected from the topology; override them with nodes: {r1: {driver: frr}}. Nodes that are configured after boot (SONiC) take setup commands: nodes: {sonic1: {setup: [...]}}.

CheckFRRSR LinuxArista EOS (cEOS)SONiC (VS)
bgp_session✅✅✅✅
bgp_prefixes (accepted = received minus rejected by policy)✅✅✅✅
route present / absent✅✅✅✅
bgp_route best-path attributes: communities (standard, large, well-known), AS path (exact or regex), local preference, MED, next hop✅✅✅✅
ping success / failure✅✅✅✅

Commands

CommandWhat it does
netdryrun run [path]Deploy the lab, run the checks, destroy the lab. Add --keep to leave it up for debugging
netdryrun check [path]Run the checks against a lab that is already running
netdryrun validate [path]Validate netdryrun.yml without running anything
netdryrun record topology.clab.ymlSnapshot a known-good lab into a netdryrun.yml baseline, so you don't write checks by hand

Reports: --json FILE (full evidence), --markdown FILE, --junit FILE. Under GitHub Actions, a summary is appended to the job page automatically. Exit codes: 0 passed, 1 a check failed, 2 config or lab error, 3 inconclusive (the lab never reached a testable state, so the run says nothing about the change).

GitHub Action

name: Network dry run
on: pull_request
jobs:
  dryrun:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: netdryrun/netdryrun@v0.3.0
        with:
          path: network/        # folder containing netdryrun.yml

The action installs containerlab, runs the checks, uploads the JSON and JUnit reports as an artifact, and fails the job if any check fails. Outputs: status, passed, failed, inconclusive-reason.

For AI agents (MCP)

AI can propose network changes. Netdryrun verifies the outcome. Agents: read AGENTS.md.

AI agents are starting to change networks. Netdryrun gives them a safety gate: an MCP server that lets an agent dry-run its change in a real lab and proceed only if every check passes.

pip install netdryrun

Add it to your MCP client (for example Claude Code):

claude mcp add netdryrun -- netdryrun mcp

Tools: dry_run (deploy, check, destroy, verdict), run_checks (against a running lab), validate, check_types. Each result carries a separate verdict field: SAFE TO PROCEED, DO NOT APPLY THIS CHANGE, or INCONCLUSIVE: the lab did not reach a testable state; ... when the lab never came up.

Demo (examples/agent-demo): the ticket says the customer's new block must reach the upstream and the peer. The tempting change passes every ticket check, session and ping, but leaks routes; Netdryrun rejects it. The two-line fix passes the same checks. (A scripted replay of the two attempts; CI runs it.)

Safety model:

  • The server never touches production. It has no tool that pushes config anywhere; it only builds and tests labs.
  • Tool results contain normalized check results, not raw device output, so text inside a lab (a banner, a route description) can't inject instructions into the agent. Raw evidence goes to the JSON report file.
  • A lab is code: containerlab runs privileged containers. Only point the server at topologies you trust, on a machine you'd let run Docker.

How it compares

Netdryrun complements these tools; it doesn't replace them.

ToolWhat it does bestWhere Netdryrun fits
BatfishStatic analysis of configs, without booting anything; fast and broadNetdryrun runs the real control plane, so it sees runtime behavior: convergence, timers, what actually lands in the RIB
netlabBuilds labs from a high-level topology, with built-in validationUse netlab to build the lab and Netdryrun for convergence-aware CI gates and evidence reports
pyATS / GenieA full test framework with deep Cisco parsersNetdryrun is a small declarative YAML file plus one GitHub Action, with no test code to write
containerlabRuns the labNetdryrun drives containerlab; it doesn't replace it

What Netdryrun adds: positive checks wait for convergence, negative checks run only after convergence and must hold, received-but-not-selected routes count as present, and every run produces one pass/fail plus an evidence file.

Honest limits

  • Lab routers are not your production routers. A pass on FRR, SR Linux, SONiC VS or Arista cEOS is strong evidence for protocol logic and policy intent, but not proof of hardware, scale or version-specific behavior. For Arista, bring your own licensed cEOS image to test real vendor code.
  • Checks cover IPv4/IPv6 unicast BGP (sessions, prefix counts, best-path attributes), routes and ping, with link-down, node-isolation and route-injection (FRR only) failure scenarios. More checks are coming: OSPF/IS-IS adjacencies, path/traceroute.

How results are checked, and who writes the code (mostly AI agents): How it's built.

Roadmap

  • Outage replays: famous real-world outages (route leaks, mass withdrawals) rebuilt as runnable labs and check packs. Would your change have caused one?
  • More failure scenarios, more check types (OSPF/IS-IS, traceroute) and drivers
  • PR comments with diffs, and signed evidence for change tickets

License

Apache-2.0. "Netdryrun" and the Netdryrun logo are trademarks; see TRADEMARKS.md.

Questions fréquentes

Qu'est-ce que netdryrun ?

Dry-run network changes in a real containerlab lab: BGP, routes, reachability, with evidence.

Comment installer netdryrun ?

Il s'exécute sur votre machine. Dans Claude Code : claude mcp add netdryrun -- uvx netdryrun==0.3.0 mcp. Pour les autres clients, collez la configuration JSON de cette page dans leurs réglages MCP.

Faut-il une clé API pour netdryrun ?

Son entrée au registre n'indique aucune clé ni secret obligatoire. Certaines fonctions peuvent tout de même demander un compte auprès du service auquel il se connecte.

netdryrun est-il open source ?

Oui. Son code est sur GitHub sous licence Apache-2.0.

Qui maintient netdryrun ?

Il est publié par netdryrun. AI.tn le référence sans le maintenir ni le recommander.

Issu du registre MCP officiel. Être listé n'est pas une recommandation : vérifiez le code et les permissions d'un serveur avant de lui donner accès à vos données.