christianmanivong a211629875 feat(ping): add a generic ping sweep every driver inherits
Sweeping a range is orchestration, not device mechanics: the only
vendor-specific part is executing a single ping, and NAPALM already
standardises that. PingSweepMixin therefore owns the loop, the reply parsing,
the target cap and the progress reporting, and is mixed into DeviceTypeDriver
so any driver implementing ping() becomes a usable sweep source without
writing sweep code of its own.

driver_supports_ping() answers "can this driver ping?" by introspection
instead of a hand-maintained list, with SUPPORTS_PING = False as the opt-out
for a driver that inherits a ping it cannot actually use.

The generic implementation is deliberately sequential — a NAPALM connection is
a single session and not safe to drive from several threads at once. A driver
whose device offers something faster overrides ping_sweep and keeps the return
shape; see napalm-opnsense's batched job API version.
2026-08-13 16:50:47 +07:00
2026-05-11 21:31:52 +02:00
2026-05-11 21:31:52 +02:00

napalm-device-types

Abstract intermediate device-type base classes for NAPALM drivers.

Instead of inheriting directly from napalm.base.NetworkDriver, a driver can inherit from one of the device-type classes here to gain a richer, type-specific contract:

# without napalm-device-types
class OpenWrtDriver(NetworkDriver):
    ...

# with napalm-device-types
from napalm_device_types import AccessPointDriver

class OpenWrtDriver(AccessPointDriver):
    ...

Why?

NAPALM's NetworkDriver defines a common interface for all network devices. In practice, devices fall into distinct categories with very different capabilities. A switch exposes spanning-tree and PoE data; a firewall exposes NAT tables and VPN tunnels; a NAS exposes disk pools and shares. Writing these methods directly in a concrete driver mixes concerns and makes drivers harder to discover and compare.

napalm-device-types sits in between: it adds one well-typed layer of abstract methods per device category, so every driver for the same category exposes the same interface.

Design principle: generic vs. device-specific logic

When adding behavior to a device-type base class, split it along one line: would this exact logic work unchanged for a different vendor's driver of the same device-type, if that driver only implemented the same abstract methods?

  • If yes, it's generic — implement it once as a concrete method on the device-type base class (here, in this repo).
  • If no — it talks to the device itself (a specific REST endpoint, a CLI command, a vendor-specific payload format) — it belongs in the concrete driver as the implementation of an abstract method the base class declares.

Concretely: matching/comparison/reconciliation algorithms, orchestration flows, and generic data shapes belong here. Only the actual device communication belongs in vendor/napalm-<name>.

Worked example — firewall rule diff/apply (FirewallDriver):

class FirewallDriver(DeviceTypeDriver):
    # Abstract — every driver implements its own device communication.
    def get_firewall_rules(self) -> List[FirewallRuleDict]: raise NotImplementedError
    def apply_firewall_rule(self, rule: FirewallRuleDict, *, uuid: Optional[str] = None) -> Dict[str, Any]: raise NotImplementedError
    def commit_firewall_rules(self) -> Dict[str, Any]: raise NotImplementedError

    # Concrete — the matching/comparison/orchestration algorithm is identical
    # for every firewall vendor, so it lives here once.
    def diff_firewall_rules(self, desired: List[FirewallRuleDict]) -> FirewallRuleDiffDict:
        ...  # matches self.get_firewall_rules() against `desired` by description

    def apply_firewall_ruleset(self, desired: List[FirewallRuleDict]):
        ...  # computes the diff, calls apply_firewall_rule() per change, commits

A new driver (FortiGate, pfSense, …) gets diff_firewall_rules/ apply_firewall_ruleset for free the moment it implements the three abstract methods — it never needs to reimplement the reconciliation logic itself.

Second worked example — ping sweeps (PingSweepMixin, mixed into DeviceTypeDriver, so every device-type driver has it):

class PingSweepMixin:
    # Concrete — the loop, the reply parsing, the target cap and the progress
    # reporting are the same for every device that can ping at all.
    def ping_sweep(self, destinations, *, count=1, timeout=1, …) -> PingSweepResultDict:
        ...  # calls NAPALM's standard ping() once per destination

A driver becomes a usable sweep source the moment it implements NAPALM's ping() — nothing else is required, and driver_supports_ping(cls) reports whether it did (introspection, not a hand-maintained list). A driver whose device offers something genuinely faster overrides ping_sweep and keeps the return shape: napalm-opnsense starts a batch of ping jobs over the diagnostics API, waits once for all of them, and reads every result with a single request — a per-host loop would be unusable there.

This mirrors a similar split already documented on the consumer side, in NetOrk's docs/ARCHITECTURE.md ("Device Warnings — Trennung von Erkennung und Präsentation"): drivers return raw signals, the higher layer gives them meaning. Same shape of separation, different axis — device-specific vs. generic here, detection vs. presentation there.

Installation

pip install napalm-device-types

Requires Python ≥ 3.9 and NAPALM ≥ 4.0.

Available base classes

Class Target devices Example implementations
AccessPointDriver Wireless access points OpenWrt, Ubiquiti UniFi, Cisco Meraki AP
SwitchDriver Ethernet switches Cisco IOS, Arista EOS, Juniper EX
FirewallDriver Firewalls & UTM appliances pfSense, Fortinet FortiOS, Cisco ASA
HypervisorDriver Hypervisors & virtualisation platforms Proxmox VE, VMware ESXi, KVM/libvirt
OSDriver General-purpose operating systems Linux, BSD, macOS
StorageDriver Storage appliances & NAS/SAN TrueNAS, Synology DSM, QNAP QTS

Usage

Access Point

from napalm_device_types import AccessPointDriver

class OpenWrtDriver(AccessPointDriver):

    def get_wireless_clients(self):
        # return List[WirelessClientDict]
        ...

    def get_ssids(self):
        # return Dict[str, SSIDDict]
        ...

Switch

from napalm_device_types import SwitchDriver

class CiscoIOSDriver(SwitchDriver):

    def get_spanning_tree(self):
        # return Dict[str, SpanningTreeDict]
        ...

    def get_poe_status(self):
        # return PoESummaryDict
        ...

Firewall

from napalm_device_types import FirewallDriver

class PfSenseDriver(FirewallDriver):

    def get_nat_translations(self):
        # return List[NATTranslationDict]
        ...

    def get_vpn_tunnels(self):
        # return Dict[str, VPNTunnelDict]
        ...

Hypervisor

from napalm_device_types import HypervisorDriver

class ProxmoxDriver(HypervisorDriver):

    def get_vms(self):
        # return List[VMDict]
        ...

    def snapshot_create(self, name, snapshot, description="", include_memory=False):
        ...

OS / Linux

from napalm_device_types import OSDriver

class LinuxDriver(OSDriver):

    def get_packages(self):
        # return List[PackageDict]
        ...

    def get_services(self):
        # return List[ServiceDict]
        ...

    def get_users(self):
        # return List[UserDict]
        ...

    def get_processes(self):
        # return List[ProcessDict]
        ...

    def get_cron_jobs(self):
        # return List[CronJobDict]
        ...

Storage / NAS

from napalm_device_types import StorageDriver

class TrueNASDriver(StorageDriver):

    def get_disks(self):
        # return List[PhysicalDiskDict]
        ...

    def get_shares(self):
        # return Dict[str, NASShareDict]
        ...

Return types

All return types are TypedDict classes defined in napalm_device_types.models. Import them directly for type annotations in your driver:

from napalm_device_types.models import (
    PhysicalDiskDict,
    DiskPoolDict,
    NASShareDict,
    VolumeSnapshotDict,
)

Development

git clone https://github.com/chrismanivong/napalm-device-types.git
cd napalm-device-types
pip install -e ".[dev]"

Run type-checking:

mypy napalm_device_types

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/router-driver)
  3. Implement your changes
  4. Open a Pull Request

License

Apache-2.0 – see LICENSE for details.

S
Description
No description provided
Readme
206 KiB
Languages
Python 100%