Metadata-Version: 2.4
Name: NetConfigPulse
Version: 0.1.0
Summary: Network configuration backup core engine with CLI-first workflows.
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 engine for backing up network device configurations over SSH. It connects to routers and switches, runs vendor-specific commands, saves the output to disk, and generates diffs against previous backups. Maintained by 21Vianet.

## Quick Start

Install from the source checkout when developing or running the bundled example
configuration:

```bash
pip install -e .[dev]
python -m netconfigpulse validate \
  --inventory config/devices.example.yaml \
  --commands config/commands.default.yaml \
  --credentials config/credentials.example.yaml \
  --config config/runtime.example.yaml
pytest tests -q
```

This validates the example configuration and runs the test suite. Use `backup`
only after replacing the example devices and credentials with real values.

## Installation

From PyPI after the first stable release:

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

From TestPyPI while testing a pre-release package:

```bash
pip install \
  --index-url https://test.pypi.org/simple/ \
  --extra-index-url https://pypi.org/simple/ \
  NetConfigPulse
netconfigpulse --help
```

`--extra-index-url` lets pip download third-party dependencies such as
`netmiko` and `PyYAML` from the official PyPI index, because TestPyPI is only a
test package index and may not host those dependencies.

The package install provides the CLI and Python library. 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.
