Introdução ao Netmiko
Netmiko é uma biblioteca Python multi-vendor que simplifica a interação com dispositivos de rede via SSH. Construída sobre a biblioteca Paramiko, Netmiko adiciona funcionalidades específicas para automação de redes, como:
- Suporte a uma vasta gama de fabricantes e plataformas (Cisco IOS, IOS-XE, IOS-XR, NX-OS, ASA, Juniper JunOS, Arista EOS, HP, etc.).
- Detecção automática de prompt do dispositivo.
- Tratamento de paginação (comandos como
terminal length 0). - Métodos simplificados para enviar comandos show e comandos de configuração.
- Tratamento de erros mais robusto e específico para redes.
Netmiko é uma das bibliotecas mais populares e amplamente utilizadas para automação de redes baseada em CLI (Command-Line Interface) devido à sua facilidade de uso e suporte abrangente.
Netmiko vs. Paramiko vs. NAPALM:
Paramiko: Biblioteca SSH de baixo nível, base para Netmiko. Requer mais código para tarefas de rede.
Netmiko: Focada em simplificar a interação CLI via SSH. Ideal para enviar comandos e configurações, especialmente em ambientes com muitos dispositivos Cisco.
NAPALM: Focada em abstração multi-vendor, oferecendo uma API unificada para obter dados e gerenciar configurações. Ideal para ambientes heterogêneos e validação de estado.
Muitas vezes, Netmiko é a escolha ideal para tarefas que envolvem interação direta com a CLI de dispositivos Cisco.
Instalação
Se ainda não instalou, use o pip:
pip install netmiko
Conectando com Netmiko
Conectar-se a um dispositivo com Netmiko é simples. Você precisa fornecer um dicionário com os detalhes da conexão.
Estrutura do Dicionário de Conexão
O dicionário de conexão geralmente inclui:
device_type: String que identifica o tipo de dispositivo (ex:cisco_ios,cisco_xe,cisco_xr,cisco_nxos,cisco_asa). A lista completa está na documentação do Netmiko.host: Endereço IP ou hostname do dispositivo.username: Nome de usuário para login.password: Senha para login.secret: (Opcional) Senha do modo enable (se aplicável). Netmiko tentará entrar no modo enable automaticamente sesecretfor fornecido.port: (Opcional) Porta SSH (padrão 22).session_log: (Opcional) Caminho para um arquivo onde a sessão será registrada.
Exemplo de Conexão
from netmiko import ConnectHandler
import json
def connect_netmiko(device_info):
"""Estabelece uma conexão SSH usando Netmiko."""
try:
print(f"Conectando a {device_info["host"]} ({device_info["device_type"]})...")
# Cria o objeto de conexão
net_connect = ConnectHandler(**device_info)
print(f"Conectado com sucesso a {net_connect.base_prompt}")
return net_connect
except Exception as e:
print(f"Falha ao conectar a {device_info["host"]}: {e}")
return None
# Informações do dispositivo
router_ios = {
"device_type": "cisco_ios",
"host": "192.168.1.1",
"username": "admin",
"password": "cisco123",
"secret": "enable_pass", # Senha do modo enable
"session_log": "router_ios_session.log"
}
switch_nxos = {
"device_type": "cisco_nxos",
"host": "192.168.1.10",
"username": "admin",
"password": "cisco123",
# NX-OS geralmente não usa 'secret' para enable
}
# Conectando ao roteador IOS
net_connect_ios = connect_netmiko(router_ios)
# Conectando ao switch NX-OS
# net_connect_nxos = connect_netmiko(switch_nxos)
# É importante fechar a conexão quando terminar
if net_connect_ios:
print(f"\nPrompt atual: {net_connect_ios.find_prompt()}")
# Desconectando
net_connect_ios.disconnect()
print("Conexão com router_ios fechada.")
# if net_connect_nxos:
# net_connect_nxos.disconnect()
# print("Conexão com switch_nxos fechada.")
Gerenciamento de Conexões:
É fundamental chamar o método disconnect() para fechar a conexão SSH e liberar recursos. O uso de blocos try...finally ou gerenciadores de contexto (with ConnectHandler(...) as net_connect:) é recomendado para garantir que a desconexão ocorra mesmo em caso de erros.
Enviando Comandos Show
Netmiko simplifica o envio de comandos show e a coleta de suas saídas.
Método send_command()
O método send_command() é o principal para executar comandos que retornam dados (comandos show).
from netmiko import ConnectHandler
# ... (função connect_netmiko e device_info definidos como antes) ...
# Conectando ao dispositivo
net_connect = connect_netmiko(router_ios)
if net_connect:
try:
# Enviando um comando simples
print("\nEnviando 'show version'...")
output_version = net_connect.send_command("show version")
print("--- Saída Show Version (parcial) ---")
print("\n".join(output_version.splitlines()[:10])) # Mostra as primeiras 10 linhas
print("...")
# Enviando um comando que pode exigir modo privilegiado
# Netmiko entra no modo enable automaticamente se 'secret' foi fornecido
print("\nEnviando 'show running-config'...")
# use_textfsm=True será explicado na próxima seção
output_run_config = net_connect.send_command("show running-config", expect_string=r"#")
print("--- Saída Show Running-Config (parcial) ---")
print("\n".join(output_run_config.splitlines()[:15]))
print("...")
# Enviando comando com opções
print("\nEnviando 'show ip interface brief'...")
output_ip_int = net_connect.send_command(
"show ip interface brief",
delay_factor=2, # Aumenta o tempo de espera (útil para comandos lentos)
strip_prompt=True, # Remove o prompt da saída
strip_command=True # Remove o comando da saída
)
print("--- Saída Show IP Interface Brief ---")
print(output_ip_int)
except Exception as e:
print(f"Erro ao enviar comando: {e}")
finally:
# Garantindo a desconexão
net_connect.disconnect()
print("\nConexão fechada.")
Parâmetros Comuns de send_command()
command_string: O comando a ser executado.expect_string: (Opcional) Uma expressão regular que indica o prompt esperado após o comando. Útil para garantir que o comando foi concluído. Netmiko geralmente detecta isso automaticamente.delay_factor: (Opcional) Multiplicador para os tempos de espera internos. Aumente se os comandos demorarem mais para retornar.max_loops: (Opcional) Número máximo de vezes para verificar a saída antes de desistir.strip_prompt: (Opcional) Remove o prompt do final da saída (padrão: True).strip_command: (Opcional) Remove o comando enviado do início da saída (padrão: True).use_textfsm: (Opcional) Usa TextFSM para parsear a saída em dados estruturados (padrão: False). Veremos isso em detalhes.use_genie: (Opcional) Usa Cisco Genie (anteriormente pyATS) para parsear a saída (padrão: False). Requer instalação separada.
Enviando Comandos de Configuração
Netmiko também facilita o envio de comandos de configuração.
Método send_config_set()
O método send_config_set() é usado para enviar um ou mais comandos de configuração. Ele entra automaticamente no modo de configuração, envia os comandos e sai.
from netmiko import ConnectHandler
# ... (função connect_netmiko e device_info definidos como antes) ...
# Conectando ao dispositivo
net_connect = connect_netmiko(router_ios)
if net_connect:
try:
# Lista de comandos de configuração
config_commands = [
"interface Loopback99",
"description Configurada via Netmiko",
"ip address 99.99.99.99 255.255.255.255",
"no shutdown"
]
print("\nEnviando comandos de configuração...")
# Enviando os comandos
output_config = net_connect.send_config_set(config_commands)
print("--- Saída da Configuração ---")
print(output_config)
# Verificando se a interface foi criada (opcional)
print("\nVerificando a configuração...")
output_verify = net_connect.send_command("show ip interface brief | include Loopback99")
print(output_verify)
# Salvando a configuração
print("\nSalvando a configuração...")
output_save = net_connect.save_config()
print(output_save)
except Exception as e:
print(f"Erro ao enviar configuração: {e}")
finally:
# Garantindo a desconexão
net_connect.disconnect()
print("\nConexão fechada.")
Método send_config_from_file()
Você também pode enviar comandos de configuração a partir de um arquivo.
# Criando um arquivo de configuração
config_file_content = """
! Configurações adicionais
logging host 10.10.10.10
snmp-server community MyCommunity RO
ntp server 192.168.100.100
"""
config_filename = "extra_config.txt"
with open(config_filename, "w") as f:
f.write(config_file_content)
# Conectando e enviando do arquivo
net_connect = connect_netmiko(router_ios)
if net_connect:
try:
print(f"\nEnviando configuração do arquivo {config_filename}...")
output_file_config = net_connect.send_config_from_file(config_filename)
print("--- Saída da Configuração (do arquivo) ---")
print(output_file_config)
# Salvando novamente
output_save = net_connect.save_config()
print(output_save)
except Exception as e:
print(f"Erro ao enviar configuração do arquivo: {e}")
finally:
net_connect.disconnect()
print("\nConexão fechada.")
Parâmetros Comuns de send_config_set()
config_commands: Uma lista de strings, cada uma sendo um comando de configuração.exit_config_mode: (Opcional) Se deve sair do modo de configuração após enviar os comandos (padrão: True).delay_factor: (Opcional) Multiplicador de delay.max_loops: (Opcional) Número máximo de loops.strip_prompt: (Opcional) Remove o prompt da saída (padrão: False).strip_command: (Opcional) Remove o comando da saída (padrão: False).config_mode_command: (Opcional) Comando para entrar no modo de configuração (padrão:configure terminal).
Método save_config()
O método save_config() envia o comando apropriado para salvar a configuração no dispositivo (ex: write memory para Cisco IOS, copy running-config startup-config para NX-OS). Ele retorna a saída do comando de salvamento.
Parsing de Saída com TextFSM
Uma das maiores dificuldades na automação baseada em CLI é extrair informações úteis da saída de texto não estruturada dos comandos. Netmiko integra-se com a biblioteca TextFSM (desenvolvida pelo Google) para resolver isso.
TextFSM usa arquivos de template (modelos) que definem como extrair dados estruturados (tabelas) de saídas de texto específicas. A comunidade, especialmente através do projeto ntc-templates, mantém uma vasta coleção de templates para comandos comuns em diversos dispositivos.
Usando TextFSM com Netmiko
Para usar TextFSM com Netmiko, você precisa:
- Instalar TextFSM:
pip install textfsm - Instalar ou ter acesso aos templates ntc-templates. A maneira mais fácil é instalar a biblioteca
ntc_templates:pip install ntc_templates. Netmiko tentará encontrar esses templates automaticamente. - Definir a variável de ambiente
NET_TEXTFSMpara o diretório onde os templates estão localizados (se não usar a bibliotecantc_templates) OU garantir que a bibliotecantc_templatesesteja instalada. - Usar o argumento
use_textfsm=Trueno métodosend_command().
from netmiko import ConnectHandler
import os
import pprint # Para imprimir dicionários/listas de forma legível
# Certifique-se de que ntc_templates está instalado: pip install ntc_templates
# ... (função connect_netmiko e device_info definidos como antes) ...
# Conectando ao dispositivo
net_connect = connect_netmiko(router_ios)
if net_connect:
try:
# Exemplo 1: show ip interface brief
print("\nExecutando 'show ip interface brief' com TextFSM...")
# Netmiko procurará um template como cisco_ios_show_ip_interface_brief.textfsm
parsed_output_ip_int = net_connect.send_command("show ip interface brief", use_textfsm=True)
print("--- Saída Parseada (Lista de Dicionários) ---")
pprint.pprint(parsed_output_ip_int)
# Acessando dados específicos
if parsed_output_ip_int:
print("\nDetalhes da primeira interface UP:")
for interface_info in parsed_output_ip_int:
if interface_info["status"] == "up":
print(f" Interface: {interface_info["intf"]}")
print(f" IP: {interface_info["ipaddr"]}")
print(f" Protocolo: {interface_info["proto"]}")
break
# Exemplo 2: show version
print("\nExecutando 'show version' com TextFSM...")
parsed_output_version = net_connect.send_command("show version", use_textfsm=True)
print("--- Saída Parseada (Dicionário) ---")
pprint.pprint(parsed_output_version)
# Acessando dados específicos
if parsed_output_version:
print(f"\nVersão do IOS: {parsed_output_version[0]["version"]}")
print(f"Hostname: {parsed_output_version[0]["hostname"]}")
print(f"Uptime: {parsed_output_version[0]["uptime"]}")
# Exemplo 3: Comando sem template
print("\nExecutando 'show clock' com TextFSM (sem template esperado)...")
try:
# Netmiko retornará a string bruta se não encontrar um template
output_clock = net_connect.send_command("show clock", use_textfsm=True)
print("--- Saída (String Bruta) ---")
print(output_clock)
print(f"Tipo da saída: {type(output_clock)}")
except Exception as e:
print(f"Erro (esperado se template não existir): {e}")
except Exception as e:
print(f"Erro durante a execução: {e}")
finally:
net_connect.disconnect()
print("\nConexão fechada.")
Estrutura da Saída Parseada:
A saída parseada com TextFSM geralmente é uma lista de dicionários, onde cada dicionário representa uma linha da tabela na saída do comando. As chaves do dicionário correspondem aos cabeçalhos definidos no template TextFSM. Para alguns comandos (como show version), a saída pode ser uma lista contendo um único dicionário.
Se Netmiko não encontrar um template correspondente para o comando e plataforma, ele retornará a saída como uma string bruta, como se use_textfsm=False tivesse sido usado.
Benefícios do Parsing
- Transforma texto em dados estruturados (listas, dicionários).
- Facilita a extração de informações específicas.
- Permite validações e comparações baseadas em dados.
- Torna os scripts mais robustos e menos dependentes de formatação exata da CLI.
Tratamento de Erros
Ao trabalhar com automação de redes, é crucial implementar um bom tratamento de erros. Netmiko levanta exceções específicas que podem ajudar a diagnosticar problemas.
Exceções Comuns do Netmiko
NetmikoTimeoutException: Ocorre quando a conexão ou a execução de um comando excede o tempo limite esperado. Pode indicar problemas de rede, dispositivo lento ou prompt inesperado.NetmikoAuthenticationException: Falha na autenticação (username/password/secret incorretos).ReadTimeout: Ocorre quando a leitura da saída do socket SSH excede o timeout.ConfigInvalidException: (Menos comum) Indica um problema ao entrar no modo de configuração.
Exemplo de Tratamento de Erros
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoTimeoutException, NetmikoAuthenticationException
import socket # Para capturar erros de conexão TCP
def connect_and_run_command(device_info, command):
"""Conecta, executa um comando e trata erros comuns."""
net_connect = None
try:
print(f"\nTentando conectar a {device_info["host"]}...")
net_connect = ConnectHandler(**device_info)
print("Conectado com sucesso.")
print(f"Executando comando: {command}")
output = net_connect.send_command(command)
print("Comando executado com sucesso.")
return output
except NetmikoAuthenticationException:
print(f"ERRO: Falha na autenticação para {device_info["host"]}.")
return None
except NetmikoTimeoutException:
print(f"ERRO: Timeout ao conectar ou executar comando em {device_info["host"]}.")
return None
except socket.error as e: # Erros de conexão TCP (ex: host não alcançável)
print(f"ERRO: Erro de conexão TCP para {device_info["host"]}: {e}")
return None
except Exception as e: # Captura outras exceções gerais
print(f"ERRO: Ocorreu um erro inesperado com {device_info["host"]}: {e}")
return None
finally:
# Garante que a conexão seja fechada se foi estabelecida
if net_connect:
net_connect.disconnect()
print(f"Conexão com {device_info["host"]} fechada.")
# Dispositivo com credenciais erradas
router_bad_auth = {
"device_type": "cisco_ios",
"host": "192.168.1.1",
"username": "wrong_user",
"password": "wrong_pass",
}
# Dispositivo inacessível
router_unreachable = {
"device_type": "cisco_ios",
"host": "10.255.255.1", # IP provavelmente inexistente
"username": "admin",
"password": "cisco123",
}
# Testando com erro de autenticação
print("--- Teste com Autenticação Inválida ---")
connect_and_run_command(router_bad_auth, "show version")
# Testando com host inacessível
print("\n--- Teste com Host Inacessível ---")
connect_and_run_command(router_unreachable, "show version")
# Testando com dispositivo válido (assumindo que router_ios está correto)
print("\n--- Teste com Dispositivo Válido ---")
output_valid = connect_and_run_command(router_ios, "show ip arp")
if output_valid:
print("--- Saída Show IP ARP ---")
print(output_valid)
Boas Práticas de Tratamento de Erros
- Use blocos
try...except...finallypara garantir a limpeza (desconexão). - Capture exceções específicas (
NetmikoAuthenticationException,NetmikoTimeoutException) antes de exceções genéricas (Exception). - Forneça mensagens de erro claras e informativas.
- Considere registrar erros em arquivos de log para análise posterior.
- Pense em lógicas de retentativa para erros transitórios (como timeouts curtos), mas com cuidado para não criar loops infinitos.
Recursos Avançados
Netmiko oferece outros recursos úteis para cenários mais complexos.
Transferência de Arquivos (SCP)
Netmiko pode usar SCP (Secure Copy Protocol) para transferir arquivos de e para dispositivos de rede, se o SCP estiver habilitado no dispositivo.
from netmiko import ConnectHandler, file_transfer
# ... (conexão estabelecida como net_connect) ...
if net_connect:
try:
# Verificando se SCP está habilitado (exemplo para IOS)
scp_check = net_connect.send_command("show running-config | include ip scp server enable")
if "ip scp server enable" not in scp_check:
print("AVISO: SCP pode não estar habilitado no dispositivo.")
# Poderia tentar habilitar aqui com send_config_set
# Transferindo um arquivo PARA o dispositivo
source_file = "extra_config.txt" # Arquivo local criado anteriormente
dest_file = "extra_config_uploaded.txt" # Nome no dispositivo
print(f"\nTransferindo {source_file} para {dest_file} no dispositivo...")
transfer_status = file_transfer(
net_connect,
source_file=source_file,
dest_file=dest_file,
file_system="flash:", # Sistema de arquivos destino (varia por OS)
direction="put",
overwrite_file=True
)
print(f"Status da transferência (upload): {transfer_status}")
if transfer_status["file_exists"]:
print(f"Arquivo {dest_file} existe no dispositivo.")
# Transferindo um arquivo DO dispositivo
source_file_remote = "startup-config" # Arquivo no dispositivo
dest_file_local = f"{net_connect.base_prompt}_startup_config_downloaded.cfg"
print(f"\nTransferindo {source_file_remote} do dispositivo para {dest_file_local}...")
transfer_status_get = file_transfer(
net_connect,
source_file=source_file_remote,
dest_file=dest_file_local,
file_system="nvram:", # Sistema de arquivos origem
direction="get"
)
print(f"Status da transferência (download): {transfer_status_get}")
if transfer_status_get["file_exists"]:
print(f"Arquivo {dest_file_local} baixado com sucesso.")
except Exception as e:
print(f"Erro durante transferência de arquivo: {e}")
finally:
net_connect.disconnect()
print("\nConexão fechada.")
Execução Paralela
Para automatizar tarefas em muitos dispositivos, a execução sequencial (um após o outro) pode ser muito lenta. Bibliotecas como concurrent.futures (integrada no Python) ou multiprocessing podem ser usadas com Netmiko para executar tarefas em paralelo.
Nota: A implementação detalhada de execução paralela está além do escopo deste módulo introdutório, mas é um conceito importante para automação em escala. Frameworks como Nornir são projetados especificamente para facilitar a automação paralela.
Outros Recursos
- SSH com Chaves: Netmiko suporta autenticação baseada em chaves SSH (
use_keys=True,key_file=...). - Proxy SSH: Conexão através de um servidor intermediário (jump host) usando
ssh_config_fileou bibliotecas comoparamiko-proxy. - Integração com Ansible: Netmiko pode ser usado dentro de módulos Ansible customizados.
Exercícios Práticos
Exercício 1: Coletor de Informações de Vizinhança
Crie um script Python que use Netmiko para se conectar a um dispositivo Cisco e colete informações sobre seus vizinhos CDP e LLDP. Use TextFSM para parsear a saída.
- Conecte-se ao dispositivo usando Netmiko.
- Execute
show cdp neighbors detaileshow lldp neighbors detail. - Use
use_textfsm=Truepara obter a saída parseada. - Imprima as informações dos vizinhos de forma organizada (Nome do Vizinho, IP, Plataforma, Interface Local, Porta Remota).
- Trate o caso de comandos não suportados ou sem saída.
Solução do Exercício 1:
#!/usr/bin/env python3
# Coletor de Vizinhança CDP/LLDP
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoTimeoutException, NetmikoAuthenticationException
import argparse
import sys
import pprint
def get_args():
"""Processa os argumentos da linha de comando."""
parser = argparse.ArgumentParser(description=\'Coletor de vizinhança CDP/LLDP com Netmiko.\')
parser.add_argument("-d", "--device_type", default="cisco_ios", help="Tipo de dispositivo Netmiko (padrão: cisco_ios)")
parser.add_argument("-H", "--hostname", required=True, help="Hostname ou IP do dispositivo")
parser.add_argument("-u", "--username", required=True, help="Nome de usuário")
parser.add_argument("-p", "--password", required=True, help="Senha")
parser.add_argument("-s", "--secret", default="", help="Senha do modo enable (opcional)")
return parser.parse_args()
def get_neighbors(device_info):
"""Coleta e imprime informações de vizinhos CDP e LLDP."""
neighbors = {"cdp": [], "lldp": []}
net_connect = None
try:
print(f"Conectando a {device_info["host"]}...")
net_connect = ConnectHandler(**device_info)
print("Conectado.")
# Coletando CDP Neighbors
try:
print("Coletando CDP neighbors detail...")
cdp_output = net_connect.send_command("show cdp neighbors detail", use_textfsm=True)
if isinstance(cdp_output, list):
neighbors["cdp"] = cdp_output
print(f"Encontrados {len(cdp_output)} vizinhos CDP.")
else:
print("Não foi possível parsear a saída CDP ou comando não suportado.")
except Exception as e:
print(f"Erro ao coletar CDP: {e}")
# Coletando LLDP Neighbors
try:
print("Coletando LLDP neighbors detail...")
lldp_output = net_connect.send_command("show lldp neighbors detail", use_textfsm=True)
if isinstance(lldp_output, list):
neighbors["lldp"] = lldp_output
print(f"Encontrados {len(lldp_output)} vizinhos LLDP.")
else:
print("Não foi possível parsear a saída LLDP ou comando não suportado.")
except Exception as e:
print(f"Erro ao coletar LLDP: {e}")
return neighbors
except (NetmikoTimeoutException, NetmikoAuthenticationException) as e:
print(f"ERRO de Conexão/Autenticação: {e}")
return None
except Exception as e:
print(f"ERRO Inesperado: {e}")
return None
finally:
if net_connect:
net_connect.disconnect()
print("Conexão fechada.")
def print_neighbor_summary(neighbors):
"""Imprime um resumo das informações dos vizinhos."""
if not neighbors:
return
print("\n--- Resumo dos Vizinhos ---")
# Imprimindo Vizinhos CDP
if neighbors.get("cdp"):
print("\nVizinhos CDP:")
for neighbor in neighbors["cdp"]:
dest_host = neighbor.get("destination_host", "N/A")
mgmt_ip = neighbor.get("management_ip", "N/A")
platform = neighbor.get("platform", "N/A")
local_intf = neighbor.get("local_port", "N/A")
remote_intf = neighbor.get("remote_port", "N/A")
print(f" - Host: {dest_host}")
print(f" IP: {mgmt_ip}, Plataforma: {platform}")
print(f" Interface Local: {local_intf}, Porta Remota: {remote_intf}")
else:
print("\nNenhum vizinho CDP encontrado ou parseado.")
# Imprimindo Vizinhos LLDP
if neighbors.get("lldp"):
print("\nVizinhos LLDP:")
# O template LLDP pode ter uma estrutura ligeiramente diferente
# Ajuste conforme a saída real do template ntc-templates
for neighbor in neighbors["lldp"]:
dest_host = neighbor.get("neighbor", "N/A") # Pode variar
# LLDP detail pode não ter IP direto, pode estar em neighbor_mgmt_ip
mgmt_ip = neighbor.get("neighbor_mgmt_ip", "N/A")
platform = neighbor.get("neighbor_sys_desc", "N/A") # Descrição pode conter plataforma
local_intf = neighbor.get("local_intf", "N/A")
remote_intf = neighbor.get("remote_port_id", "N/A")
print(f" - Host: {dest_host}")
print(f" IP: {mgmt_ip}, Plataforma: {platform[:50]}...") # Limita tamanho da descrição
print(f" Interface Local: {local_intf}, Porta Remota: {remote_intf}")
else:
print("\nNenhum vizinho LLDP encontrado ou parseado.")
def main():
args = get_args()
device_info = {
"device_type": args.device_type,
"host": args.hostname,
"username": args.username,
"password": args.password,
"secret": args.secret if args.secret else None,
}
neighbor_data = get_neighbors(device_info)
if neighbor_data:
print_neighbor_summary(neighbor_data)
if __name__ == "__main__":
main()
Exercício 2: Aplicador de Configuração Padronizada
Crie um script que aplique um conjunto padrão de configurações (ex: NTP, SNMP, logging) a um dispositivo Cisco usando Netmiko.
- Defina uma lista de comandos de configuração padrão.
- Conecte-se ao dispositivo usando Netmiko.
- Use
send_config_set()para aplicar os comandos. - Verifique se alguns dos comandos foram aplicados (opcional, usando
send_command). - Salve a configuração usando
save_config(). - Inclua tratamento de erros.
Solução do Exercício 2:
#!/usr/bin/env python3
# Aplicador de Configuração Padrão
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoTimeoutException, NetmikoAuthenticationException
import argparse
import sys
def get_args():
"""Processa os argumentos da linha de comando."""
parser = argparse.ArgumentParser(description=\'Aplicador de configuração padrão com Netmiko.\')
parser.add_argument("-d", "--device_type", default="cisco_ios", help="Tipo de dispositivo Netmiko (padrão: cisco_ios)")
parser.add_argument("-H", "--hostname", required=True, help="Hostname ou IP do dispositivo")
parser.add_argument("-u", "--username", required=True, help="Nome de usuário")
parser.add_argument("-p", "--password", required=True, help="Senha")
parser.add_argument("-s", "--secret", default="", help="Senha do modo enable (opcional)")
return parser.parse_args()
def apply_standard_config(device_info, standard_commands):
"""Aplica uma lista de comandos de configuração padrão."""
net_connect = None
success = False
try:
print(f"Conectando a {device_info["host"]}...")
net_connect = ConnectHandler(**device_info)
print("Conectado.")
print("Aplicando configuração padrão...")
config_output = net_connect.send_config_set(standard_commands)
print("--- Saída da Aplicação ---")
print(config_output)
# Verificação simples (exemplo: verificar servidor NTP)
print("\nVerificando configuração NTP...")
ntp_check = net_connect.send_command("show run | include ntp server")
if "ntp server 10.1.1.1" in ntp_check:
print("Verificação NTP OK.")
else:
print("AVISO: Verificação NTP falhou ou comando não encontrado.")
print("\nSalvando configuração...")
save_output = net_connect.save_config()
print(save_output)
if "complete" in save_output.lower() or "ok" in save_output.lower() or "[ok]" in save_output.lower():
success = True
print("Configuração padrão aplicada e salva com sucesso.")
else:
print("AVISO: Comando save pode não ter sido bem-sucedido.")
except (NetmikoTimeoutException, NetmikoAuthenticationException) as e:
print(f"ERRO de Conexão/Autenticação: {e}")
except Exception as e:
print(f"ERRO Inesperado: {e}")
finally:
if net_connect:
net_connect.disconnect()
print("Conexão fechada.")
return success
def main():
args = get_args()
device_info = {
"device_type": args.device_type,
"host": args.hostname,
"username": args.username,
"password": args.password,
"secret": args.secret if args.secret else None,
}
# Comandos padrão a serem aplicados
standard_commands = [
"ntp server 10.1.1.1 prefer",
"ntp server 10.1.1.2",
"logging host 10.2.2.2 transport udp port 514",
"logging trap warnings",
"snmp-server community StandardRO RO",
"snmp-server community StandardRW RW",
"snmp-server location DataCenter-A",
"snmp-server contact NetworkTeam",
"service timestamps debug datetime msec localtime show-timezone",
"service timestamps log datetime msec localtime show-timezone",
"ip domain-name mycompany.local",
"ip name-server 8.8.8.8 8.8.4.4"
]
apply_standard_config(device_info, standard_commands)
if __name__ == "__main__":
main()
Avaliação do Módulo
Quiz de Conhecimentos
Responda às seguintes perguntas para testar seu conhecimento sobre os conceitos apresentados neste módulo:
- Qual é a principal vantagem do Netmiko sobre o Paramiko para automação de redes?
- Qual chave no dicionário de conexão do Netmiko especifica o tipo de dispositivo (ex: Cisco IOS)?
- Qual método do Netmiko é usado para enviar comandos show?
- Qual método do Netmiko é usado para enviar comandos de configuração?
- O que o argumento
secretno dicionário de conexão do Netmiko permite? - Qual biblioteca e técnica o Netmiko usa para parsear saídas de CLI em dados estruturados?
- Qual argumento deve ser passado para
send_command()para habilitar o parsing automático? - Qual é o formato típico da saída retornada por
send_command()quandouse_textfsm=Truee um template é encontrado? - Cite duas exceções específicas que podem ser levantadas pelo Netmiko.
- Qual método do Netmiko é usado para salvar a configuração no dispositivo?
Respostas do Quiz:
- Vantagem do Netmiko sobre Paramiko: Netmiko simplifica a interação com a CLI, tratando automaticamente detecção de prompt, paginação, entrada no modo enable e oferecendo métodos específicos para comandos show e config, além de suporte multi-vendor mais direto.
- Chave para tipo de dispositivo:
device_type(ex:"device_type": "cisco_ios"). - Método para comandos show:
send_command(). - Método para comandos de configuração:
send_config_set()ousend_config_from_file(). - Argumento
secret: Permite que o Netmiko tente entrar automaticamente no modo privilegiado (enable) após a conexão. - Biblioteca e técnica de parsing: Usa a biblioteca TextFSM e arquivos de template (como os do ntc-templates).
- Argumento para habilitar parsing:
use_textfsm=True. - Formato da saída parseada: Geralmente uma lista de dicionários.
- Exceções Netmiko:
NetmikoTimeoutException,NetmikoAuthenticationException. - Método para salvar configuração:
save_config().
Projeto Prático Final
Desenvolva um script Python abrangente que realize uma auditoria básica em uma lista de dispositivos Cisco. O script deve:
- Ler a lista de dispositivos (IP, tipo, credenciais) de um arquivo CSV.
- Para cada dispositivo:
- Conectar usando Netmiko.
- Coletar: versão do IOS, hostname, uptime, lista de interfaces IP, status das interfaces, vizinhos CDP/LLDP.
- Usar TextFSM sempre que possível para obter dados estruturados.
- Verificar se a configuração em execução difere da configuração salva (
show archive config differencesou similar). - Verificar se existem interfaces em estado “err-disabled”.
- Gerar um relatório resumido (pode ser texto ou JSON) indicando:
- Dispositivos auditados com sucesso.
- Dispositivos com falha na conexão/auditoria.
- Dispositivos com configurações não salvas.
- Dispositivos com interfaces em “err-disabled”.
- Implementar execução paralela (usando
concurrent.futures) para acelerar a auditoria em múltiplos dispositivos. - Incluir tratamento de erros robusto e logging básico.
Referências e Recursos Adicionais
Documentação Oficial
Livros Recomendados
- Kirk Byers – “Python for Network Engineers” (Foca bastante em Netmiko)
- Eric Chou – “Mastering Python Networking” (Capítulos sobre Netmiko e Parsing)
- Jason Edelman, Scott S. Lowe, Matt Oswalt – “Network Programmability and Automation”
Recursos Online e Tutoriais
- David Bombal: Python for Network Engineers (Playlist no YouTube)
- Network to Code Blog
- Coding Networker: Netmiko Tutorials
- Real Python: Speed Up Your Python Program With Concurrency (para execução paralela)