Metadata-Version: 2.4
Name: NetConfigPulse
Version: 0.1.1
Summary: CLI-first Python package for network device configuration backup, versioned snapshots, and diffs.
Author: NetConfigPulse Maintainers
License-Expression: Apache-2.0
Project-URL: Repository, https://github.com/21v-bluecloud/NetConfigPulse
Project-URL: Documentation, https://github.com/21v-bluecloud/NetConfigPulse/blob/main/README.md
Keywords: network,backup,configuration,ssh,netmiko
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: System Administrators
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: netmiko==4.6.0
Requires-Dist: PyYAML>=6.0.1
Provides-Extra: dev
Requires-Dist: pytest>=8.2; extra == "dev"
Dynamic: license-file

# NetConfigPulse

[中文文档](https://github.com/21v-bluecloud/NetConfigPulse/blob/main/README.zh-CN.md)

[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776AB)
![CLI First](https://img.shields.io/badge/interface-CLI%20first-1f6feb)
![Open Core](https://img.shields.io/badge/model-open%20core-0a7f5a)

NetConfigPulse is an open source Python package and CLI for network device configuration backup. It connects to routers and switches over SSH, runs vendor-specific collection commands, saves versioned backup files, and generates diffs against previous runs. Maintained by 21Vianet.

## Quick Start

Install the released package from PyPI:

```bash
pip install NetConfigPulse
netconfigpulse --help
```

Use the CLI with your own YAML files, or clone the repository and start from the
sample configuration:

```bash
netconfigpulse validate \
  --inventory config/devices.example.yaml \
  --commands config/commands.default.yaml \
  --credentials config/credentials.example.yaml \
  --config config/runtime.example.yaml
```

This validates the example configuration without connecting to any device. Use
`backup` only after replacing the example devices and credentials with real
values.

## Installation

From PyPI:

```bash
pip install NetConfigPulse
netconfigpulse --help
```

The PyPI package includes both the `netconfigpulse` console command and the
Python library API. To run the example configuration files from this repository,
clone the repository or copy the `config/` directory, then run:

```bash
netconfigpulse validate \
  --inventory config/devices.example.yaml \
  --commands config/commands.default.yaml \
  --credentials config/credentials.example.yaml \
  --config config/runtime.example.yaml
```

## CLI Usage

Three subcommands: `validate`, `backup`, `import-csv`.

After installation, both `python -m netconfigpulse ...` and the console command
`netconfigpulse ...` are supported.

### validate

Loads and validates all config files. Reports errors without connecting to any devices.

```bash
python -m netconfigpulse validate \
  --inventory config/devices.example.yaml \
  --commands config/commands.default.yaml \
  --credentials config/credentials.example.yaml \
  --config config/runtime.example.yaml
```

### backup

Runs the backup workflow: connect to devices, execute commands, save output, generate diffs.

```bash
python -m netconfigpulse backup \
  --inventory config/devices.example.yaml \
  --commands config/commands.default.yaml \
  --credentials config/credentials.example.yaml \
  --config config/runtime.example.yaml
```

All flags:

| Flag | Required | Default | Description |
|------|----------|---------|-------------|
| `--inventory` | yes | — | Path to device inventory YAML |
| `--commands` | yes | — | Path to vendor command catalog YAML |
| `--credentials` | no | — | Path to credentials YAML (can also use env vars, see below) |
| `--config` | no | — | Path to runtime config YAML |
| `--device` | no | — | Filter: backup a single device by name |
| `--tag` | no | — | Filter: backup only devices with this tag |
| `--group` | no | — | Filter: backup only devices in this group |
| `--output` | no | `text` | Output format: `text` or `json` |
| `--concurrency` | no | `10` | Max parallel SSH sessions |
| `--backup-root` | no | `backups` | Root directory for backup files |

When `--config` is provided, values in the runtime config file take precedence
over the `--backup-root` and `--concurrency` command-line defaults.

### import-csv

Imports a vendor command catalog from CSV and writes it as YAML.

```bash
python -m netconfigpulse import-csv \
  --input device_cli_command.csv \
  --output config/commands.default.yaml
```

### Exit codes

- `0` — all devices succeeded
- `1` — the entire run failed (no devices succeeded)
- `2` — partial failure (some succeeded, some failed)

## End-to-End Example

### Input files

`config/devices.example.yaml`

```yaml
devices:
  - name: edge-r1
    host: 192.0.2.10
    vendor: cisco_ios
    tags:
      - production
      - edge
    group: core
```

`config/commands.default.yaml`

```yaml
vendors:
  cisco_ios:
    backup:
      - terminal length 0
      - show running
```

`config/credentials.example.yaml`

```yaml
defaults:
  username: backup-user
  password: change-me
```

`config/runtime.example.yaml`

```yaml
backup_root: backups
concurrency: 10
min_backup_size_bytes: 2000
backup_retry_attempts: 3
backup_retry_delay_seconds: 10
```

### Run

```bash
python -m netconfigpulse backup \
  --inventory config/devices.example.yaml \
  --commands config/commands.default.yaml \
  --credentials config/credentials.example.yaml \
  --config config/runtime.example.yaml
```

### Text output

```text
Backed up 1 device: 1 succeeded, 0 failed, 0 warned, 1 changed.
```

### JSON output

Pass `--output json` to get machine-readable output:

```json
{
  "total_devices": 1,
  "success_count": 1,
  "failed_count": 0,
  "warning_count": 0,
  "changed_devices": 1,
  "unchanged_devices": 0,
  "results": [
    {
      "device_name": "edge-r1",
      "status": "success",
      "file_path": "backups/2026-04-21/edge-r1/edge-r1_020202.cfg",
      "diff_file_path": "backups/2026-04-21/diff/edge-r1/edge-r1_020202_diff.cfg",
      "has_changes": true,
      "backup_size": 12345,
      "warnings": [],
      "error": null
    }
  ]
}
```

## Python Library API

NetConfigPulse can also be used directly as a Python library.

### Minimal example

```python
from pathlib import Path

from netconfigpulse import load_commands, load_credentials, load_inventory, run_backup
from netconfigpulse.models.config import BackupOptions

devices = load_inventory("config/devices.example.yaml")
commands = load_commands("config/commands.default.yaml")
credentials = load_credentials("config/credentials.example.yaml")

result = run_backup(
    devices=devices,
    command_catalog=commands,
    credentials=credentials,
    options=BackupOptions(
        backup_root=Path("backups"),
        concurrency=10,
    ),
)

# result is a dataclass, not JSON — access fields directly
print(result.total_devices)
print(result.success_count)
print(result.failed_count)

# to convert to dict / JSON:
from dataclasses import asdict
import json

print(json.dumps(asdict(result), indent=2))
```

### Available functions

| Function | Purpose |
|----------|---------|
| `load_inventory(path)` | Read device list from YAML |
| `load_commands(path)` | Read vendor command catalog from YAML |
| `load_credentials(path)` | Read credentials from YAML (also picks up env vars) |
| `run_backup(...)` | Run the full backup workflow (connect, collect, save, diff) |
| `generate_diff(current, previous)` | Produce a unified diff between two backup texts |
| `filter_dynamic_content(text)` | Replace timestamps and uptime with placeholders to avoid false diffs |

### Result shape

`run_backup(...)` returns a `BackupRunResult` with aggregated counters and per-device entries.

Top-level fields:

- `total_devices: int`
- `success_count: int`
- `failed_count: int`
- `warning_count: int`
- `changed_devices: int`
- `unchanged_devices: int`
- `results: list[DeviceBackupResult]`

Each `DeviceBackupResult` includes:

- `device_name: str`
- `status: str`
- `file_path: str | None`
- `diff_file_path: str | None`
- `has_changes: bool`
- `backup_size: int | None`
- `warnings: list[str]`
- `error: str | None`

### Return example

```python
BackupRunResult(
    total_devices=1,
    success_count=1,
    failed_count=0,
    warning_count=0,
    changed_devices=1,
    unchanged_devices=0,
    results=[
        DeviceBackupResult(
            device_name="edge-r1",
            status="success",
            file_path="backups/2026-04-21/edge-r1/edge-r1_020202.cfg",
            diff_file_path="backups/2026-04-21/diff/edge-r1/edge-r1_020202_diff.cfg",
            has_changes=True,
            backup_size=12345,
            warnings=[],
            error=None,
        )
    ],
)
```

## Configuration Files

- `config/devices.example.yaml` — device inventory
- `config/commands.default.yaml` — full vendor command catalog
- `config/commands.minimal.yaml` — smaller starter sample
- `config/credentials.example.yaml` — credentials (can also be provided via env vars)
- `config/runtime.example.yaml` — runtime options (backup root, concurrency, backup-size warning threshold, retry policy)

Credentials can be provided via YAML file or environment variables. Env vars override YAML defaults:

- `NETCONFIGPULSE_DEFAULT_USERNAME`
- `NETCONFIGPULSE_DEFAULT_PASSWORD`
- `NETCONFIGPULSE_DEFAULT_SECRET`

The `--credentials` flag is optional. If omitted, credentials come from env vars only.

`min_backup_size_bytes` warns when a text backup is smaller than the configured
threshold, which helps catch truncated or prompt-only backup files. Set it to
`0` to disable this warning. Binary backup output is written as bytes and does
not use the text-size warning.

`backup_retry_attempts` and `backup_retry_delay_seconds` retry transient SSH
failures, empty backup output, and text backups that are below the minimum-size
threshold before the run records the final result.

## Project Layout

```text
netconfigpulse/
  cli/              # argparse entry point
  core/             # backup engine orchestrator
  loaders/          # YAML/CSV parsers
  models/           # dataclasses (Device, BackupOptions, results, etc.)
  transports/ssh/   # netmiko-based SSH runner
  diffing/          # unified diff generator
  filters/          # dynamic content filters (timestamps, uptime)
  output/           # text and JSON renderers
config/             # default and example configuration files
scripts/
tests/
docs/
```

## Integration Scenarios

- Call the CLI from CI/CD pipelines or cron schedulers
- Consume machine-readable output via `--output json`
- Embed the Python package into a larger internal service

## Governance

NetConfigPulse is maintained by 21Vianet as the lead steward. External contributions are welcome, while roadmap direction, release management, and future commercial packaging remain under maintainer control.
