|
| 1 | +# Cisco Switch Docu-Crawler - Advanced Scripting & Integration Guide |
| 2 | + |
| 3 | +The **Cisco Switch Docu-Crawler** is designed to be fully scriptable. This guide explains how to integrate the crawler into automated environments (such as cron jobs, Ansible playbooks, CI/CD pipelines, or custom Python orchestration scripts) by leveraging environment variables, CLI flags, and structured output files. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## 🔐 Credentials Automation |
| 8 | + |
| 9 | +To run the crawler non-interactively without prompting for input, you can pass credentials using environment variables: |
| 10 | + |
| 11 | +| Variable | Description | |
| 12 | +| --- | --- | |
| 13 | +| `CRAWLER_USER` or `CRAWLER_USERNAME` | SSH/Telnet Login Username | |
| 14 | +| `CRAWLER_PASSWORD` | SSH/Telnet Login Password | |
| 15 | +| `CRAWLER_SECRET` or `CRAWLER_ENABLE_SECRET` | Cisco Privileged EXEC Enable Secret | |
| 16 | + |
| 17 | +### Bash Example: |
| 18 | +```bash |
| 19 | +export CRAWLER_USER="admin" |
| 20 | +export CRAWLER_PASSWORD="SecureLogin123" |
| 21 | +export CRAWLER_SECRET="SecureEnable123" |
| 22 | + |
| 23 | +python3 cisco_crawler.py --subnets 10.0.1.0/24,10.0.2.0/24 --disable-telnet |
| 24 | +``` |
| 25 | + |
| 26 | +--- |
| 27 | + |
| 28 | +## ⚙️ CLI Reference Table |
| 29 | + |
| 30 | +The `cisco_crawler.py` command line supports the following flags: |
| 31 | + |
| 32 | +| Argument | Type | Default | Description | |
| 33 | +| --- | --- | --- | --- | |
| 34 | +| `--subnets` | string | *Local subnet* | Comma-separated target networks to scan (e.g. `10.0.0.0/24,192.168.1.0/24`) | |
| 35 | +| `--simulate` | flag | `False` | Run simulated scan & crawl (uses local mock switches without network traffic) | |
| 36 | +| `--disable-telnet` | flag | `False` | Disables Telnet connections and restricts connection protocol to SSH-only | |
| 37 | +| `--threads` | integer | `10` | Number of concurrent workers for switch scanning and crawls | |
| 38 | +| `--timeout` | integer | `10` | Connection and socket read timeout in seconds | |
| 39 | +| `--retry` | string | `None` | Path to `failed_hosts.json` to retry only previously failed targets | |
| 40 | +| `--baseline` | string | `None` | Filepath to save the collected network operational state (for baseline auditing) | |
| 41 | +| `--compare` | string | `None` | Filepath to a baseline JSON to compare the current network state against | |
| 42 | +| `--verbose` or `-v` | flag | `False` | Enable detailed debug logs printed to standard output | |
| 43 | + |
| 44 | +--- |
| 45 | + |
| 46 | +## 🐍 Custom Python Integration Examples |
| 47 | + |
| 48 | +### Example 1: Nightly Automation Script (subprocess) |
| 49 | +This script runs the crawler, automatically passing subnets and credentials via the process environment, and exits with a status code. |
| 50 | + |
| 51 | +```python |
| 52 | +import subprocess |
| 53 | +import os |
| 54 | +import sys |
| 55 | + |
| 56 | +def run_nightly_backup(): |
| 57 | + print("[*] Starting scheduled backup run...") |
| 58 | + |
| 59 | + # Configure run environment |
| 60 | + env = os.environ.copy() |
| 61 | + env["CRAWLER_USER"] = "admin" |
| 62 | + env["CRAWLER_PASSWORD"] = "SecurePassword123" |
| 63 | + env["CRAWLER_SECRET"] = "SecureEnableSecret123" |
| 64 | + |
| 65 | + # Call crawler process |
| 66 | + cmd = [ |
| 67 | + "python3", "cisco_crawler.py", |
| 68 | + "--subnets", "192.168.1.0/24", |
| 69 | + "--disable-telnet", |
| 70 | + "--threads", "15", |
| 71 | + "--timeout", "8" |
| 72 | + ] |
| 73 | + |
| 74 | + result = subprocess.run(cmd, env=env, capture_output=True, text=True) |
| 75 | + |
| 76 | + if result.returncode == 0: |
| 77 | + print("[+] Crawler completed successfully!") |
| 78 | + print(result.stdout) |
| 79 | + else: |
| 80 | + print("[!] Crawler execution failed!", file=sys.stderr) |
| 81 | + print(result.stderr, file=sys.stderr) |
| 82 | + sys.exit(result.returncode) |
| 83 | + |
| 84 | +if __name__ == "__main__": |
| 85 | + run_nightly_backup() |
| 86 | +``` |
| 87 | + |
| 88 | +### Example 2: Parsing Config Variables JSON |
| 89 | +You can parse the structured JSON variables output file (`migration_config_variables.json`) inside your own automation scripts to build inventory assets or populate CMDBs. |
| 90 | + |
| 91 | +```python |
| 92 | +import json |
| 93 | +import glob |
| 94 | +import os |
| 95 | + |
| 96 | +def load_latest_device_variables(): |
| 97 | + # Locate the most recent run deliverables directory |
| 98 | + runs = glob.glob("deliverables/run_*") |
| 99 | + if not runs: |
| 100 | + print("No crawler runs found.") |
| 101 | + return |
| 102 | + |
| 103 | + latest_run = max(runs, key=os.path.getmtime) |
| 104 | + vars_file = os.path.join(latest_run, "migration", "migration_config_variables.json") |
| 105 | + |
| 106 | + if not os.path.exists(vars_file): |
| 107 | + print(f"Variables JSON not found in: {latest_run}") |
| 108 | + return |
| 109 | + |
| 110 | + with open(vars_file, "r") as f: |
| 111 | + devices = json.load(f) |
| 112 | + |
| 113 | + print(f"--- Decoupled Configuration Parameters ({latest_run}) ---") |
| 114 | + for hostname, config in devices.items(): |
| 115 | + print(f"\nDevice: {hostname}") |
| 116 | + print(f" Management IP: {config.get('management_ip')}") |
| 117 | + print(f" Model: {config.get('model')}") |
| 118 | + print(f" DNS Servers: {', '.join(config.get('dns_servers', []))}") |
| 119 | + print(f" NTP Servers: {', '.join(config.get('ntp_servers', []))}") |
| 120 | + print(f" L3 Interfaces:") |
| 121 | + for intf in config.get("l3_interfaces", []): |
| 122 | + print(f" - {intf.get('interface')}: {intf.get('ip_address')} (Subnet: {intf.get('subnet')})") |
| 123 | + |
| 124 | +if __name__ == "__main__": |
| 125 | + load_latest_device_variables() |
| 126 | +``` |
| 127 | + |
| 128 | +### Example 3: Running a Configuration State Baseline Audit |
| 129 | +This example automates baseline checks, saving the baseline config and raising alerts if the state changes in a future run. |
| 130 | + |
| 131 | +```python |
| 132 | +import subprocess |
| 133 | +import os |
| 134 | +import sys |
| 135 | + |
| 136 | +def check_network_baseline(): |
| 137 | + baseline_path = "backups/production_baseline.json" |
| 138 | + env = os.environ.copy() |
| 139 | + env["CRAWLER_USER"] = "admin" |
| 140 | + env["CRAWLER_PASSWORD"] = "SecurePassword123" |
| 141 | + env["CRAWLER_SECRET"] = "SecureEnableSecret123" |
| 142 | + |
| 143 | + # 1. Create baseline if it doesn't exist |
| 144 | + if not os.path.exists(baseline_path): |
| 145 | + print(f"[*] Baseline not found. Generating new baseline file at {baseline_path}...") |
| 146 | + cmd = ["python3", "cisco_crawler.py", "--subnets", "192.168.1.0/24", "--baseline", baseline_path] |
| 147 | + subprocess.run(cmd, env=env, check=True) |
| 148 | + print("[+] Baseline successfully saved.") |
| 149 | + return |
| 150 | + |
| 151 | + # 2. Compare current network state against the baseline |
| 152 | + print("[*] Comparing current network state against baseline...") |
| 153 | + cmd = ["python3", "cisco_crawler.py", "--subnets", "192.168.1.0/24", "--compare", baseline_path] |
| 154 | + result = subprocess.run(cmd, env=env, capture_output=True, text=True) |
| 155 | + |
| 156 | + # Check output for configuration delta drift |
| 157 | + if "State Comparison: CHANGES DETECTED" in result.stdout: |
| 158 | + print("[!] WARNING: Network drift detected!") |
| 159 | + # Print comparison report details |
| 160 | + print(result.stdout) |
| 161 | + # Here you could trigger email notifications or slack webhooks |
| 162 | + else: |
| 163 | + print("[+] Network state is consistent with the baseline.") |
| 164 | + |
| 165 | +if __name__ == "__main__": |
| 166 | + check_network_baseline() |
| 167 | +``` |
0 commit comments