Skip to main content

The nervous system for your network — a unified, async-first Python SDK for network automation.

Project description


██████╗ ██╗     ███████╗██╗  ██╗ █████╗ ██████╗
██╔══██╗██║     ██╔════╝╚██╗██╔╝██╔══██╗██╔══██╗
██████╔╝██║     █████╗   ╚███╔╝ ███████║██████╔╝
██╔═══╝ ██║     ██╔══╝   ██╔██╗ ██╔══██║██╔══██╗
██║     ███████╗███████╗██╔╝ ██╗██║  ██║██║  ██║
╚═╝     ╚══════╝╚══════╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝

The nervous system for your network.

A unified, async-first Python SDK for network automation — transport, parsing, intent, telemetry, topology, and AI in one platform.


PyPI version Python License Docs Discord


Why Plexar?

The Python network automation ecosystem is fragmented across a dozen libraries — each solving one layer well, none solving the whole problem.

You need to... Current reality
Connect to devices Netmiko or Scrapli or Paramiko
Parse CLI output TextFSM or TTP or Genie (Cisco-only)
Abstract vendors NAPALM (limited drivers, no async)
Orchestrate at scale Nornir + plugins
Detect config drift Build it yourself
Push with rollback Only if using NETCONF
Stream telemetry pyGNMI (raw, unnormalized)
Model topology Doesn't exist
Test network state pyATS (Cisco-only)
Get AI-assisted RCA Doesn't exist

Plexar collapses all of this into a single, layered, async-native SDK.


Quickstart

pip install plexar
from plexar import Network

net = Network()
net.inventory.load("netbox", url="https://netbox.corp.com", token_env="NB_TOKEN")

# Connect to all leaf switches and get BGP state — concurrently
async with net.pool(max_concurrent=50) as pool:
    results = await pool.map(lambda d: d.get_bgp_summary(), net.devices(role="leaf"))

for device, bgp in results:
    for peer in bgp.peers:
        if peer.state != "established":
            print(f"⚠️  {device.hostname}{peer.neighbor_ip} is {peer.state}")

Core Features

🔌 Async Transport Layer

Connect over SSH, NETCONF, RESTCONF, gNMI, or SNMP — async throughout, with automatic fallback, connection pooling, and per-device rate limiting.

device = Device(
    hostname="spine-01",
    platform="arista_eos",
    transport=Transport.SSH,        # or NETCONF, GNMI, RESTCONF
    credentials=Credentials(password_env="DEVICE_PASS")
)
await device.connect()

🏭 Vendor-Neutral Data Models

Every get_* call returns a normalized Pydantic model — not raw text — regardless of vendor.

# Same API across Cisco, Arista, Juniper, Palo Alto
interfaces = await device.get_interfaces()
bgp        = await device.get_bgp_summary()
routes     = await device.get_routing_table()

# Fully typed, validated, serializable
print(bgp.peers[0].state)           # "established"
print(interfaces[0].speed_mbps)     # 10000

🎯 Intent Engine

Declare what you want. Plexar figures out how to get there, per vendor.

from plexar.intent import Intent
from plexar.intent.primitives import BGPIntent, InterfaceIntent

intent = Intent(devices=net.devices(role="leaf"))
intent.ensure(BGPIntent(asn=65001, neighbors=["10.0.0.1"], address_family="evpn"))
intent.ensure(InterfaceIntent(name="Ethernet1", mtu=9214, admin_state="up"))

plan = await intent.compile()
print(plan.diff())           # see exactly what will change, per device

result = await intent.apply()
report = await intent.verify()
print(report.compliant)      # True

🔄 Transactional Config Push

Every push is a transaction. Automatic rollback on verification failure.

async with device.transaction() as txn:
    await txn.push(new_config)
    ok = await txn.verify([
        ("bgp_peers_up", lambda r: r.peers_established >= 4),
    ])
    if not ok:
        await txn.rollback()    # guaranteed, across all transports

📡 Drift Detection

Continuously compare running state against desired state. Get alerted. Auto-remediate.

monitor = DriftMonitor(inventory=net.inventory, interval_seconds=300)

@monitor.on_drift
async def handle_drift(event):
    await alert_slack(f"Drift on {event.device}: {event.summary}")
    await event.remediate()     # optional: auto-fix

await monitor.start()

🌐 Topology Engine

Understand your network as a graph. Discover via LLDP/CDP. Compute blast radius.

topo = TopologyEngine(net.inventory)
await topo.discover()

path   = topo.shortest_path("leaf-01", "spine-02")
blast  = topo.blast_radius("core-sw-01")   # what breaks if this dies?

topo.export_d3("topology.html")            # interactive browser visualization

🤖 AI Engine

Natural language RCA. Autonomous remediation. LLM-assisted parsing for unknown output.

ai = NetworkAI(net)

# Ask in plain English
rca = await ai.ask("Why is traffic slow between dc1 and dc2?")
print(rca.root_cause)           # "BGP prefix limit reached on leaf-03"
print(rca.affected_devices)     # ["leaf-03", "spine-01"]

# Parse unknown CLI output — no template required
raw    = await device.run("show platform qos queue-stats")
parsed = await ai.parse(raw, hint="QoS queue statistics")

🧪 Network Testing Framework

pytest-native. Mock driver for CI/CD. No real devices needed in your pipeline.

@pytest.mark.asyncio
async def test_all_bgp_peers_established(net):
    async for device in net.devices(role="leaf"):
        bgp = await device.get_bgp_summary()
        assert all(p.state == "established" for p in bgp.peers)

async def test_no_config_drift(net):
    report = await net.drift_report()
    assert report.is_clean, report.summary()

Architecture

┌─────────────────────────────────────────────────────┐
│                     USER API                        │
├─────────────────────────────────────────────────────┤
│               AI ENGINE  ·  INTENT ENGINE           │
├─────────────────────────────────────────────────────┤
│          STATE MANAGER  ·  TOPOLOGY ENGINE          │
├─────────────────────────────────────────────────────┤
│              DEVICE ABSTRACTION LAYER               │
├─────────────────────────────────────────────────────┤
│     SSH  ·  NETCONF  ·  RESTCONF  ·  gNMI  ·  SNMP │
├─────────────────────────────────────────────────────┤
│   Cisco  ·  Juniper  ·  Arista  ·  Palo Alto  · …  │
└─────────────────────────────────────────────────────┘

Supported Platforms

Vendor SSH NETCONF RESTCONF gNMI
Cisco IOS / IOS-XE
Cisco NX-OS
Cisco IOS-XR
Arista EOS
Juniper JunOS
Palo Alto PAN-OS
Fortinet FortiOS
Nokia SR-OS

✅ Stable · ⚡ In Progress · — Roadmap


Compared to the Ecosystem

Capability Netmiko NAPALM Nornir pyATS Plexar
Async-native
Vendor-neutral models ⚠️ ⚠️
Intent engine
Drift detection
Transactional push
Streaming telemetry
Topology graph
AI-assisted RCA
Mock driver / CI-CD ⚠️
Multi-vendor testing

Installation

# Core
pip install plexar

# With AI engine
pip install plexar[ai]

# With gNMI telemetry
pip install plexar[gnmi]

# Everything
pip install plexar[all]

Requires Python 3.11+


Documentation

Full documentation, tutorials, and API reference at plexar.dev


Contributing

We welcome contributions — especially new vendor drivers.

git clone https://github.com/plexar/plexar
cd plexar
pip install -e ".[dev]"
pytest

See CONTRIBUTING.md and the Driver Authoring Guide.


Roadmap

  • Core device model + async SSH
  • Cisco IOS/NX-OS/XR drivers
  • Arista EOS driver
  • Juniper JunOS driver
  • Intent engine v1
  • Drift monitor
  • Topology engine
  • gNMI telemetry
  • AI parser + RCA
  • Digital twin / simulation
  • Web UI (enterprise)

License

Apache 2.0 — see LICENSE


Built with obsession by the Plexar team and contributors.

plexar.dev · Discord · Twitter

The nervous system for your network.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

plexar-0.1.0a1.tar.gz (66.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

plexar-0.1.0a1-py3-none-any.whl (63.5 kB view details)

Uploaded Python 3

File details

Details for the file plexar-0.1.0a1.tar.gz.

File metadata

  • Download URL: plexar-0.1.0a1.tar.gz
  • Upload date:
  • Size: 66.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for plexar-0.1.0a1.tar.gz
Algorithm Hash digest
SHA256 a021cf923d862c403dc3644b3dd76cd88b5b9d1371d403d2abfa716669fe0eb3
MD5 339121d1035a84960cd52371f0bca291
BLAKE2b-256 e4080462e87366a435eabcb1c636f2793c3a6452bcc720fa51375011804c8737

See more details on using hashes here.

File details

Details for the file plexar-0.1.0a1-py3-none-any.whl.

File metadata

  • Download URL: plexar-0.1.0a1-py3-none-any.whl
  • Upload date:
  • Size: 63.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for plexar-0.1.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 434b417478ca376035d64c9072b570fe109438452f0fa4a517aba9cbd80c51e3
MD5 c731bde84d2788ea713c754273e7ca68
BLAKE2b-256 32376dab4e0bf8f847ab882e82516315d81446e4baef07f4aaacc9f90427190e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page