Skip to content

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.

License

Notifications You must be signed in to change notification settings

Sparty-5A/cisco-nso-orchestration

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

9 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🌐 Cisco NSO Network Orchestration Framework

Python 3.12+ Cisco NSO RESTCONF License: MIT

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.


✨ Features

Core Capabilities

  • 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

Safety & Reliability

  • 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

Operations & Development

  • 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

πŸ—οΈ Architecture

High-Level Design

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  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                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Network Topology

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)

πŸš€ Quick Start

Prerequisites

  • Python 3.12 or higher
  • Access to Cisco NSO (DevNet Sandbox or local instance)
  • VPN connection to DevNet Sandbox (if using sandbox)

Installation

# 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

First Deployment

# 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-rtr01

πŸ“ Project Structure

cisco-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

πŸ’» Usage Examples

Device-Level Intent

# 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

Service-Level Intent

# 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

NSO Operations

# 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"

πŸ”§ Configuration

Intent File Format (Device-Level)

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"

Inventory Format

hosts.yaml:

dev-dist-rtr01:
  mgmt_ip: 10.10.20.176
  device_type: ios-xe
  groups: [distribution, development]
  platform: cisco-iosxe

groups.yaml:

distribution:
  vars:
    role: distribution
    ospf_area: 0

defaults.yaml:

vars:
  dns_servers: [8.8.8.8, 8.8.4.4]
  ntp_servers: [10.10.20.1]

πŸ§ͺ Testing

Running Tests

# 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

Test Coverage

  • Unit Tests - Pydantic model validation, template rendering
  • Integration Tests - Service deployment logic, NSO communication (mocked)
  • Validation Tests - Intent file validation, inventory validation

πŸ›‘οΈ Safety Features

Deletion Policies

# 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 intent

Dry-Run Mode

Always 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

Reconciliation Engine

# 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)

🎯 Multi-Platform Support

Platform Differences Abstracted

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!


πŸ“Š Key Design Patterns

3-Tier Variable Inheritance

# 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)
}

Query-Augmented Intent

Users provide minimal intent:

bgp:
  asn: 65001
  router_id: 10.100.100.1

Framework queries NSO for related config and builds complete context for templates.

Idempotency

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

πŸ“š Additional Documentation


πŸŽ“ Technologies Used

  • 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

🀝 Contributing

This is a portfolio project demonstrating production-grade network automation practices. Contributions and feedback welcome!

Development Setup

# Install development dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Format code
black .

# Lint code
ruff check .

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


πŸ™ Acknowledgments

  • Built for Cisco DevNet Sandbox
  • Uses Cisco NSO for network orchestration
  • Inspired by infrastructure-as-code principles from Terraform and Ansible

πŸ“ž Contact

Author: Scott Penry
Email: scottpenry@comcast.net
GitHub: @Sparty-5A


⭐ Show Your Support

Give a ⭐️ if this project helped you!


Built for production network automation | Multi-platform Cisco orchestration | Intent-based configuration

About

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.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published