π¨π΄ EspaΓ±ol
A network automation repository built as Infrastructure as Code on Cisco devices (IOSv, CSR1000v) in a lab environment (PNETLab). It is organized as a series of progressive labs (Lab 2 to Lab 6), each building a complete network configuration and validation pipeline.
This is not an application or an installable package: it is a collection of pipelines where Git is the Source of Truth and every network change follows the same flow:
- Intent β declarative topology/intent in YAML versioned in Git.
- Render β configuration generation with Jinja2 templates.
- Pre-deploy validation with Batfish (labs 5 and 6).
- Precheck β verification of the current device state.
- Deploy β with Ansible (
cisco.ios.ios_config,network_cliconnection + libssh) or Netmiko (lab 3). - Postcheck and evidence collection with Nornir.
- Validation with pyATS / Genie and artifact generation in
artifacts/.
| Component | URL / Detail | Role |
|---|---|---|
| NetBox | http://192.168.1.16:8000 |
Source of Truth and dynamic inventory (netbox.netbox.nb_inventory) |
| AWX | http://192.168.1.13:30143 |
Job Template orchestrator |
| Jenkins | β | Triggers AWX Job Templates via REST API (Lab 5), with automatic rollback |
| Management network | 172.30.30.0/26 |
OOB connectivity to all devices |
βββ ansible.cfg # Global config: NetBox inventory, legacy libssh
βββ collections/requirements.yml # Required Ansible collections
βββ inventories/netbox/ # Dynamic inventory (nb_inventory plugin)
βββ group_vars/ # Cisco IOS connection vars (root)
βββ execution-environments/ # Containerfiles for AWX Execution Environments
β βββ iosv/ # Minimal EE (ansible-core 2.15.13) for IOSv
β βββ lab5-csr1000v/ # Full EE (nornir, pyats, genie, pybatfish...)
βββ scripts/netbox/ # NetBox bootstrap from intent (labs 4 and 5)
βββ shared/ # Shared Nornir inventory and scripts (Lab 2)
βββ playbooks/awx/ # AWX smoke test (show_version.yml)
βββ docs/ # Phase documentation
βββ lab2-inter-vlan/ # Lab 2: Inter-VLAN routing (Ansible pipeline)
βββ lab3-router-on-a-stick/ # Lab 3: Router-on-a-Stick (Python pipeline)
βββ lab4-ospf-ansible-pipeline/ # Lab 4: OSPF single-area (100% Ansible)
βββ lab5-ospf-multiarea-jenkins-pipeline/ # Lab 5: OSPF multi-area + Jenkins/AWX
βββ lab6-eigrp-cicd-pipeline/ # Lab 6: EIGRP + CI/CD + APIs (in progress)
Each lab is self-contained (its own ansible.cfg, inventories, vars and artifacts/ where applicable).
Pure Ansible pipeline. playbooks/lab2_full_change.yml imports the phases render β precheck β deploy β postcheck and then invokes the Nornir/Genie scripts from shared/scripts/.
- Intent:
intent/lab2_intent.yml(VLANs 10/20/30/40, switches SW_DMZ, DSW1, ASW1, ASW2). - Jinja2 templates in
templates/.
Same flow as Lab 2 but orchestrated with Python scripts: scripts/lab3_full_change.py runs render β precheck β deploy (Netmiko) β postcheck β nornir β genie.
- Devices: R1 (router), SW_DMZ, ASW1, ASW2.
- Note: the scripts have hardcoded lab credentials (
netdevops/cisco); this is a known historical exception, not a pattern to replicate.
Orchestrated 100% by Ansible with numbered playbooks 00_β07_; playbooks/lab4_pipeline.yml imports them in order.
- Source of truth:
intent/lab4_source_of_truth.yml; dynamic inventory from NetBox (localhost_vars/andgroup_vars/). - Topology: R1βR4 (OSPF area 0, /30 links
10.0.x.x), SW_DMZ, ASW1, PC1 (LAN10.10.30.0/24). Seelab4-ospf-ansible-pipeline/README.mdfor the full addressing. - Lab rule: only the management configuration is done manually via CLI; everything else is applied by Ansible.
Multi-area OSPF on CSR1000v with Ansible roles (lab5_render, lab5_batfish, lab5_precheck, lab5_ospf, lab5_postcheck, lab5_nornir, lab5_pyats, lab5_validate, lab5_cleanup). playbooks/lab5_pipeline.yml imports playbooks 01_β08_.
- Introduces Batfish as a pre-deploy validation gate.
- The
Jenkinsfilelocates the AWX Job Template by name, launches it only ifEXECUTE_PIPELINE=true, waits for the result and, on failure, triggers an automatic rollback (template ID 16). - Credentials in
group_vars/vault.yml(Ansible Vault).
Declarative topology in vars/topology.yml (6 CSR1000v, LAB6). API-first approach:
scripts/sync_netbox.pyβ syncs the Git topology into NetBox.validation/validate_netbox_sot.pyβ validates NetBox against Git (read-only).scripts/render_eigrp.pyandscripts/render_batfish_candidates.pyβ config rendering and Batfish snapshots.- Playbooks
01_β03_(pre-change backup, Batfish pre-deploy, precheck) withlab6_*roles. - The
batfish/,jenkins/,netconf/,nornir/,postman/,pyats/,restconf/subdirectories are reserved for future work.
- Automation environment Python:
/opt/automation/venv/bin/python. - Ansible with the collections from
collections/requirements.yml:ansible-galaxy collection install -r collections/requirements.yml
- Access to NetBox, AWX and the management network
172.30.30.0/26.
| Variable | Usage |
|---|---|
NETBOX_TOKEN |
Required for NetBox API scripts (bootstrap_*, sync_netbox.py, validate_netbox_sot.py) |
NETBOX_URL |
Optional (default http://192.168.1.16:8000) |
IOS_PASSWORD |
SSH password for IOS devices in the root inventory and Lab 4 |
awx-api-token |
Jenkins credential holding the AWX API token (Lab 5) |
lab5*/group_vars/vault.yml and lab6*/group_vars/vault.yml are encrypted and define vault_ios_username, vault_ios_password, vault_ios_enable_password. Run with --ask-vault-pass or a vault password file.
From the repository root, unless otherwise noted:
# Verify the NetBox dynamic inventory
ansible-inventory --graph
# Reachability of inventory hosts
ansible all -m ansible.builtin.command -a 'ping -c 2 {{ ansible_host }}' -c local
# AWX smoke test
ansible-playbook playbooks/awx/show_version.yml
# Lab 2 (full Ansible pipeline)
ansible-playbook lab2-inter-vlan/playbooks/lab2_full_change.yml
# Lab 3 (full Python pipeline)
python3 lab3-router-on-a-stick/scripts/lab3_full_change.py
# Lab 4 (uses its local ansible.cfg)
cd lab4-ospf-ansible-pipeline && ansible-playbook playbooks/lab4_pipeline.yml
# Lab 5 (requires vault password)
cd lab5-ospf-multiarea-jenkins-pipeline && ansible-playbook playbooks/lab5_pipeline.yml --ask-vault-pass
# Lab 6
cd lab6-eigrp-cicd-pipeline
export NETBOX_TOKEN=<token>
python3 scripts/sync_netbox.py # sync NetBox
python3 validation/validate_netbox_sot.py # validate NetBox vs Git
python3 scripts/render_eigrp.py # render configs to configs/
ansible-playbook playbooks/01_backup_prechange.yml --ask-vault-pass
# Build Execution Environments (example)
cd execution-environments/lab5-csr1000v && podman build -t ee-lab5-csr1000v .Each lab stores evidence in artifacts/:
rendered/β rendered configurations (versioned in Git).precheck/,postcheck/,deploy/β evidence from each phase.nornir/βshowoutputs collected with Nornir.genie//pyats/β JSON validation reports.batfish/β pre-deploy validation results.backups/β pre-change backups (some ignored in.gitignoredue to size).
.gitkeep files keep empty directories under version control.
There is no traditional test framework (pytest, unit-test CI): validation is domain-specific and acts as a pipeline gate β each phase aborts the flow if it fails.
- Batfish (offline pre-deploy validation): labs 5 and 6, output in
artifacts/batfish/output/. - pyATS / Genie: parse the
showoutputs collected by Nornir and generate JSON reports; exit code β 0 on any FAIL. - SoT validation:
lab6*/validation/validate_netbox_sot.pycompares NetBox againstvars/topology.yml(read-only). - When modifying a pipeline: at minimum run
ansible-playbook --syntax-checkon the affected playbook and, if possible, a render/precheck run without deploy.
- Language: mix of Spanish and English. Documentation,
Jenkinsfileand recent scripts (labs 5 and 6) in Spanish; labs 2 and 3 in English. Keep the language of the file being edited. - Playbooks numbered by phase (
00_cleanup,01_render,02_precheck, ...) plus a*_pipeline.ymlthat only doesimport_playbookin order. - Ansible roles prefixed with the lab name (
lab5_*,lab6_*), withdefaults/,tasks/,vars/andfiles/for Python scripts. - Deployment always from a rendered file:
cisco.ios.ios_configwithsrc: .../artifacts/rendered/{{ inventory_hostname }}.cfgandsave_when. - All evidence is saved with
delegate_to: localhost. - Python scripts:
#!/usr/bin/env python3, paths relative withPath(__file__).resolve().parent, console output with[OK]/[FAIL]/[PASS]prefixes andsys.exit(1)on error.
- Never commit plaintext credentials: use Ansible Vault or environment variables (
NETBOX_TOKEN,IOS_PASSWORD). - Connections use
ansible.netcommon.network_cliwith libssh and legacy algorithms (ssh-rsa,diffie-hellman-group-exchange-sha1, ...) because the lab IOSv devices are old;host_key_checking = Falseis a lab environment requirement, not a general recommendation. - NetBox scripts disable TLS verification (
verify=False) because the lab NetBox uses HTTP/self-signed certificates; deliberate and only valid for the lab. - The Lab 5
Jenkinsfilehas a safe mode: withoutEXECUTE_PIPELINE=trueit only validates that the Job Template exists, without launching it.
This project is licensed under the MIT License.
AGENTS.mdβ detailed guide for AI agents (structure, commands, conventions).lab4-ospf-ansible-pipeline/README.mdβ full Lab 4 addressing and topology.docs/lab4/β phase documentation (e.g. NetBox dynamic inventory).