Automatización SSH con Netmiko
Use este skill al escribir o revisar automatización en Python que se conecte a dispositivos de red con Netmiko. Mantenga la ruta predeterminada como solo lectura; los cambios de configuración necesitan una ventana de cambios separada, revisión entre pares y un plan de reversión.
Cuándo Usarlo
- Recopilar la salida de comandos
showen routers, switches o firewalls. - Crear un pequeño script de auditoría para evidencia de interfaces, enrutamiento o configuración.
- Agregar tiempos de espera y manejo de excepciones a scripts SSH de red.
- Analizar la salida de comandos con TextFSM cuando exista una plantilla.
- Revisar la automatización antes de que toque dispositivos en producción.
Valores Predeterminados de Seguridad
- Comience con recolección de solo lectura usando
send_command(). - Mantenga el inventario pequeño y explícito; no barra rangos de direcciones completos.
- Use variables de entorno, un vault o
getpass; nunca codifique credenciales. - Establezca tiempos de espera de conexión y lectura.
- Limite la concurrencia para no sobrecargar dispositivos antiguos.
- Exija una bandera explícita del operador antes de
send_config_set(). - No llame a
save_config()hasta que el cambio haya sido verificado y aprobado.
Patrón de Conexión de Solo Lectura
import os
from getpass import getpass
from netmiko import ConnectHandler
from netmiko.exceptions import (
NetmikoAuthenticationException,
NetmikoTimeoutException,
ReadTimeout,
)
device = {
"device_type": "cisco_ios",
"host": "192.0.2.10",
"username": os.environ.get("NETMIKO_USERNAME") or input("Usuario: "),
"password": os.environ.get("NETMIKO_PASSWORD") or getpass("Contraseña: "),
"secret": os.environ.get("NETMIKO_ENABLE_SECRET"),
"conn_timeout": 10,
"auth_timeout": 20,
"banner_timeout": 15,
"read_timeout_override": 30,
}
try:
with ConnectHandler(**device) as conn:
if device.get("secret") and not conn.check_enable_mode():
conn.enable()
output = conn.send_command("show ip interface brief", read_timeout=30)
print(output)
except NetmikoAuthenticationException:
print("Autenticación fallida")
except NetmikoTimeoutException:
print("La conexión SSH expiró")
except ReadTimeout:
print("La lectura del comando expiró")
Use direcciones de los rangos de documentación en los ejemplos. Mantenga el inventario real en un archivo local ignorado o en un sistema gestionado por secretos.
Recolección por Lotes
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Any
def collect_show(device: dict[str, Any], command: str) -> dict[str, Any]:
host = device["host"]
try:
with ConnectHandler(**device) as conn:
output = conn.send_command(command, read_timeout=45)
return {"host": host, "ok": True, "output": output}
except (NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout) as exc:
return {"host": host, "ok": False, "error": type(exc).__name__}
results = []
with ThreadPoolExecutor(max_workers=8) as pool:
futures = [pool.submit(collect_show, device, "show version") for device in devices]
for future in as_completed(futures):
results.append(future.result())
Mantenga max_workers bajo a menos que se sepa que el parque de dispositivos y los sistemas AAA
pueden manejar un volumen de conexiones mayor.
Análisis Estructurado
Netmiko puede pedirle a TextFSM, TTP o Genie que analice la salida de comandos compatibles. Trate la salida del analizador como una optimización, no como la única ruta de evidencia.
with ConnectHandler(**device) as conn:
parsed = conn.send_command(
"show ip interface brief",
use_textfsm=True,
raise_parsing_error=False,
read_timeout=30,
)
if isinstance(parsed, str):
print("No se encontró una plantilla de análisis; guarde la salida sin procesar para revisión")
else:
for row in parsed:
print(row)
Si el análisis impulsa una decisión crítica, conserve la salida sin procesar del comando junto con el resultado analizado para que un operador pueda inspeccionar las discrepancias.
Patrón de Cambios Protegidos
import os
commands = [
"interface GigabitEthernet0/1",
"description CHANGE-1234 UPLINK-TO-CORE",
]
apply_changes = os.environ.get("APPLY_NETWORK_CHANGES") == "1"
if not apply_changes:
print("Solo simulación. Comandos candidatos:")
print("\n".join(commands))
else:
with ConnectHandler(**device) as conn:
conn.enable()
before = conn.send_command("show running-config interface GigabitEthernet0/1")
output = conn.send_config_set(commands)
after = conn.send_command("show running-config interface GigabitEthernet0/1")
print(before)
print(output)
print(after)
print("Verifique el comportamiento antes de guardar la configuración de inicio.")
Guardar la configuración es un paso de aprobación separado. En producción, incluya un fragmento de reversión y capture la evidencia de antes/después en el registro de cambios.
Lista de Verificación de Revisión
- ¿El script identifica una fuente de inventario explícita?
- ¿Están las credenciales ausentes del código fuente, registros y mensajes de excepción?
- ¿Están establecidos
conn_timeout,auth_timeoutyread_timeoutdel comando? - ¿Se informan las fallas por dispositivo sin detener todo el lote?
- ¿Evita el script escaneos amplios y concurrencia ilimitada?
- ¿Están los cambios de configuración detrás de una bandera de simulación o explícita del operador?
- ¿Está
save_config()separado del envío inicial y vinculado a la verificación?
Anti-Patrones
- Codificar contraseñas, secretos de enable o claves privadas en el código fuente.
- Enviar comandos de configuración como la ruta de código predeterminada.
- Ejecutar automatización contra un rango CIDR en lugar de un inventario revisado.
- Registrar configuraciones completas en ejecución en sistemas compartidos sin sanitización.
- Tratar el éxito del analizador como prueba de que el estado del dispositivo es correcto.
Véase También
- Skill:
cisco-ios-patterns - Skill:
network-config-validation - Skill:
network-interface-health