Production-grade Infrastructure as Code framework for Cisco network orchestration using NSO. Features multi-platform support (IOS-XE, IOS-XR, NX-OS), intent-based configuration, reconciliation engine, and comprehensive testing. Built for Cisco DevNet Sandbox environments.
- Multi-Platform Support - IOS-XE, IOS-XR, and NX-OS with platform-specific templates
- Intent-Based Configuration - Declarative YAML intent files with Pydantic validation
- Reconciliation Engine - Calculates minimal configuration diffs and applies only necessary changes
- Idempotency - Safe to run multiple times, only applies changes when needed
- NSO Integration - RESTCONF API communication with Cisco NSO
- Safe Deletion Modes - Configurable policies for managing untracked resources
- Dry-Run Mode - Preview changes before applying to production
- Pre-Deployment Validation - Schema validation and prerequisite checks
- Configuration Backups - NSO maintains rollback files automatically
- Error Handling - Graceful failure with detailed error messages
- 3-Tier Variable Inheritance - Defaults β Groups β Hosts for DRY configuration
- Inventory Management - Centralized device metadata with group-based organization
- Template System - Jinja2 templates for platform-specific XML generation
- Comprehensive Testing - Unit and integration tests with pytest
- CI/CD Ready - GitHub Actions workflows for automated testing
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β INTENT LAYER β
β ββ YAML files (device_bgp_configs.yaml) β
β Pydantic models (device_models.py) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β INVENTORY LAYER β
β ββ Device inventory (hosts.yaml, groups.yaml) β
β 3-tier variable inheritance β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ORCHESTRATION LAYER β
β ββ Device engine (device_engine.py) β
β Service orchestrator (service_orchestrator.py) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVICE LAYER β
β ββ BGP peering (bgp_peering.py) β
β Template renderer (template_renderer.py) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NSO LAYER β
β ββ RESTCONF client (nso_client.py) β
β Device synchronization β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NETWORK DEVICES β
β ββ IOS-XE, IOS-XR, NX-OS β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The framework orchestrates configurations across a Cisco DevNet Sandbox topology:
Production Environment:
- Core Layer: IOS-XR routers (core-rtr01)
- Distribution Layer: IOS-XE routers (dist-rtr01)
- Access Layer: NX-OS switches (dist-sw01)
Development Environment:
- Mirror topology for safe testing (dev-core-rtr01, dev-dist-rtr01, dev-dist-sw01)
- Python 3.12 or higher
- Access to Cisco NSO (DevNet Sandbox or local instance)
- VPN connection to DevNet Sandbox (if using sandbox)
# Clone repository
git clone https://github.com/Sparty-5A/cisco-nso-orchestration.git
cd cisco-nso-orchestration
# Install dependencies
pip install -e ".[dev]"
# Configure NSO connection (DevNet Sandbox defaults)
export NSO_HOST=10.10.20.49
export NSO_PORT=8080
export NSO_USER=developer
export NSO_PW=C1sco12345
# Verify NSO connectivity
python scripts/sync_devices.py# Preview changes (dry-run)
python scripts/apply_device_intent.py \
--intent intent/device_bgp_configs.yaml \
--device dev-dist-rtr01 \
--dry-run
# Deploy BGP configuration
python scripts/apply_device_intent.py \
--intent intent/device_bgp_configs.yaml \
--device dev-dist-rtr01
# Verify idempotency (run again, should show no changes)
python scripts/apply_device_intent.py \
--intent intent/device_bgp_configs.yaml \
--device dev-dist-rtr01cisco-nso-orchestration/
βββ automation/ # Core automation engine
β βββ device_engine.py # Reconciliation & orchestration
β βββ device_models.py # Pydantic intent models
β βββ inventory_loader.py # Inventory management
β βββ nso_client.py # RESTCONF client
β βββ service_orchestrator.py # Service-level orchestration
β βββ template_renderer.py # Jinja2 template engine
β
βββ services/ # Service implementations
β βββ bgp_peering.py # BGP deployment logic
β
βββ templates/ # Configuration templates
β βββ ios-xe/ # IOS-XE specific templates
β β βββ bgp_service.xml.j2
β βββ ios-xr/ # IOS-XR specific templates
β βββ bgp_service.xml.j2
β
βββ scripts/ # User interface scripts
β βββ apply_device_intent.py # Main deployment CLI
β βββ deploy_service.py # Service deployment CLI
β βββ sync_devices.py # NSO device sync
β βββ run_any_command.py # Ad-hoc command execution
β
βββ intent/ # Intent definitions
β βββ device_bgp_configs.yaml # BGP intent
β βββ device_loopbacks.yaml # Loopback intent
β
βββ inventory/ # Device inventory
β βββ hosts.yaml # Device definitions
β βββ groups.yaml # Group definitions
β βββ defaults.yaml # Default values
β
βββ tests/ # Test suite
β βββ test_device_models.py # Model validation tests
β βββ test_template_renderer.py # Template tests
β βββ test_bgp_service.py # Service logic tests
β
βββ docs/ # Documentation
βββ ARCHITECTURE.md # Architecture deep dive
βββ QUICK_START.md # 10-minute guide
βββ WORKFLOW.md # Detailed workflows
# Deploy loopback interfaces
python scripts/apply_device_intent.py \
--intent intent/device_loopbacks.yaml \
--device dev-dist-rtr01
# Deploy BGP configuration
python scripts/apply_device_intent.py \
--intent intent/device_bgp_configs.yaml \
--device dev-dist-rtr01
# Combine multiple intents (merge configurations)
python scripts/apply_device_intent.py \
--intent intent/device_loopbacks.yaml \
--intent intent/device_bgp_configs.yaml \
--device dev-dist-rtr01
# Deploy to all devices in intent file
python scripts/apply_device_intent.py \
--intent intent/device_bgp_configs.yaml# Deploy BGP service to multiple devices
python scripts/deploy_service.py \
--intent intent/service_bgp_peering.yaml \
--dry-run
# Deploy with validation
python scripts/deploy_service.py \
--intent intent/service_bgp_peering.yaml \
--validate
# Deploy to inventory group
python scripts/deploy_service.py \
--intent intent/service_bgp_peering.yaml \
--group distribution# Sync all devices from NSO
python scripts/sync_devices.py
# Run ad-hoc command
python scripts/run_any_command.py \
--device dev-dist-rtr01 \
--command "show ip bgp summary"devices:
- name: dev-dist-rtr01
device_type: ios-xe
delete_unmanaged_loopbacks: false # Safe mode
delete_unmanaged_bgp_neighbors: false # Safe mode
loopbacks:
- id: 100
ipv4: 10.100.100.1
netmask: 255.255.255.255
description: "Management loopback"
bgp:
asn: 65001
router_id: 10.100.100.1
neighbors:
- ip: 10.200.200.1
remote_asn: 65001
description: "iBGP to core-rtr01"
update_source: "Loopback0"hosts.yaml:
dev-dist-rtr01:
mgmt_ip: 10.10.20.176
device_type: ios-xe
groups: [distribution, development]
platform: cisco-iosxegroups.yaml:
distribution:
vars:
role: distribution
ospf_area: 0defaults.yaml:
vars:
dns_servers: [8.8.8.8, 8.8.4.4]
ntp_servers: [10.10.20.1]# Run all tests
pytest
# Run specific test categories
pytest -m unit
pytest -m integration
# Run with coverage
pytest --cov=automation --cov=services --cov-report=html
# Run specific test file
pytest tests/test_device_models.py -v- Unit Tests - Pydantic model validation, template rendering
- Integration Tests - Service deployment logic, NSO communication (mocked)
- Validation Tests - Intent file validation, inventory validation
# SAFE MODE (default) - Recommended for production
delete_unmanaged_bgp_neighbors: false
# Only manages declared neighbors, ignores others
# STRICT MODE - Use with caution
delete_unmanaged_bgp_neighbors: true
# Deletes any BGP neighbors NOT in intentAlways preview changes before applying:
# Preview changes
python scripts/apply_device_intent.py \
--intent intent/device_bgp_configs.yaml \
--dry-run
# Review output, then deploy
python scripts/apply_device_intent.py \
--intent intent/device_bgp_configs.yaml# 1. Query current device state via NSO
current_config = nso_client.get_bgp_config(device)
# 2. Compare with intent
if is_configured(current_config, intent):
return "SKIPPED - Already configured"
# 3. Calculate minimal diff
changes = calculate_diff(current_config, intent)
# 4. Apply only necessary changes
nso_client.apply_config(changes)IOS-XE Template (XML):
<config xmlns="http://tail-f.com/ns/config/1.0">
<devices xmlns="http://tail-f.com/ns/ncs">
<device>
<name>{{ device_name }}</name>
<config>
<native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
<router>
<bgp xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-bgp">
<!-- IOS-XE specific structure -->
</bgp>
</router>
</native>
</config>
</device>
</devices>
</config>IOS-XR Template (XML):
<config xmlns="http://tail-f.com/ns/config/1.0">
<devices xmlns="http://tail-f.com/ns/ncs">
<device>
<name>{{ device_name }}</name>
<config>
<router xmlns="http://tail-f.com/ned/cisco-ios-xr">
<bgp>
<!-- IOS-XR specific structure -->
</bgp>
</router>
</config>
</device>
</devices>
</config>Framework automatically selects correct template based on device_type!
# Resolution order: defaults β groups β hosts
final_vars = {
**defaults.vars, # Layer 1: Global defaults
**group1.vars, # Layer 2: Group-level
**group2.vars,
**host.vars # Layer 3: Host-specific (highest priority)
}Users provide minimal intent:
bgp:
asn: 65001
router_id: 10.100.100.1Framework queries NSO for related config and builds complete context for templates.
def is_configured(current, intent) -> bool:
"""Check if device already matches intent."""
if current.asn != intent.asn:
return False
if current.router_id != intent.router_id:
return False
# ... more checks
return True # No changes needed- Architecture Deep Dive - Complete technical architecture
- Quick Start Guide - 10-minute getting started
- Workflow Documentation - Detailed user journeys
- Portfolio Polish - Showcase tips
- Python 3.12+ - Modern Python with type hints
- Cisco NSO - Network Services Orchestrator
- RESTCONF - RESTful API for network management
- Pydantic - Data validation and settings management
- Jinja2 - Template engine for XML generation
- pytest - Testing framework
- PyYAML - YAML parsing
- Loguru - Modern logging
- httpx - Async HTTP client
This is a portfolio project demonstrating production-grade network automation practices. Contributions and feedback welcome!
# Install development dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Format code
black .
# Lint code
ruff check .This project is licensed under the MIT License - see the LICENSE file for details.
- Built for Cisco DevNet Sandbox
- Uses Cisco NSO for network orchestration
- Inspired by infrastructure-as-code principles from Terraform and Ansible
Author: Scott Penry
Email: scottpenry@comcast.net
GitHub: @Sparty-5A
Give a βοΈ if this project helped you!
Built for production network automation | Multi-platform Cisco orchestration | Intent-based configuration