GPON

Netmiko com Python: automação de OLT Huawei via SSH

Janela de código Python com Netmiko ligada por SSH a três OLTs Huawei com portas acesas

Netmiko é uma das bibliotecas Python mais usadas para conversar por SSH com equipamento de rede, e funciona com a OLT Huawei MA5800. O problema é que a OLT não se comporta como um roteador comum: pagina saída longa com ---- More, pergunta { <cr>||<K> } no meio do comando, muda o prompt a cada interface gpon e aceita poucas sessões ao mesmo tempo. Um script que ignora isso trava, devolve saída pela metade ou tira o acesso de quem está operando.

Eu mantenho um sistema próprio que conversa com OLTs MA5800 por SSH e Telnet, escrito direto sobre Paramiko. As lições deste guia saíram dele: preparar a sessão, não dividir a mesma conexão entre threads, respeitar o limite de VTY e começar só lendo. Para o lado do Netmiko, conferi o comportamento no código-fonte da versão 4.8.0, que é a referência dos exemplos abaixo.

Resposta rápida: Netmiko na OLT Huawei

  1. Instale com pip install netmiko num ambiente virtual. A versão 4.8.0 pede Python 3.10 ou mais novo.
  2. Use device_type="huawei_olt" (ou huawei_smartax, que é a mesma classe). Não use huawei, que é para roteador e switch VRP.
  3. Depois de conectar, rode enable, scroll 512 e screen-width 256 para tirar a paginação e a quebra de linha.
  4. Para comandos que mudam o prompt, como interface gpon 0/1, passe expect_string.
  5. Uma sessão por OLT por thread, poucas threads e só comandos display até o script provar que é confiável.

Instalar o Netmiko

Use um ambiente virtual, separado do Python do sistema. Num servidor Linux:

python3 -m venv .venv
source .venv/bin/activate
pip install netmiko
python -c "import netmiko; print(netmiko.__version__)"

A última linha só confirma a versão instalada. O Netmiko traz junto o Paramiko, que faz o SSH, e o TextFSM com os templates do ntc-templates, que servem para outros fabricantes. Para a saída da OLT Huawei eu prefiro uma expressão regular própria, como você vai ver mais abaixo, porque assim sei exatamente o que está sendo lido.

Guarde usuário e senha da OLT em variáveis de ambiente ou num cofre de segredos, nunca no código. Os exemplos leem OLT_USER e OLT_PASS do ambiente.

Qual device_type usar na OLT Huawei

O device_type escolhe a classe que o Netmiko usa para preparar a sessão e reconhecer o prompt. No arquivo ssh_dispatcher.py da versão 4.8.0, estes são os nomes da Huawei que interessam aqui:

device_typeClasse no NetmikoQuando usar
huawei_oltHuaweiSmartAXSSHOLT MA5800 e MA5600 por SSH. É o que este guia usa.
huawei_smartaxHuaweiSmartAXSSHMesma classe, outro nome.
huawei_olt_telnetHuaweiSmartAXSSH (sobre Telnet)Só se a OLT não tiver SSH habilitado. A senha viaja em texto puro.
huawei_smartaxmmiHuaweiSmartAXSSHMMIEntra no modo MMI, feito para máquina. Teste em laboratório antes, porque a saída muda de formato.
huawei / huawei_vrpHuaweiSSHRoteador e switch VRP. Errado para OLT: o preparo da sessão é outro.

Quem pega um exemplo pronto de roteador e só troca o IP costuma deixar huawei. A conexão até abre, mas o preparo da sessão é o do VRP, e a OLT responde de um jeito que a classe não espera. O sintoma é o script parado até estourar o tempo.

O que o Netmiko faz quando conecta

Ao abrir a sessão com huawei_olt, a classe HuaweiSmartAXSSH faz quatro coisas antes de devolver o controle ao seu script:

  • Se a OLT mostrar um aviso pós-login que pede YES/NO, responde yes.
  • Manda undo smart, que desliga o pedido de { <cr>||<K> } no fim dos comandos. Sem isso, cada display com parâmetros opcionais fica esperando um Enter a mais.
  • Manda infoswitch cli OFF em modo enable, para a OLT não jogar mensagens de depuração no meio da saída.
  • Volta ao modo usuário e manda scroll para desligar a paginação.

No meu sistema, a sequência de preparo da MA5800 é esta, igual para SSH e Telnet:

enable
scroll 512
screen-width 256
undo smart
undo interactive
config

A diferença que importa está em scroll 512 e screen-width 256. O scroll sem número, que o Netmiko manda, depende do comportamento padrão da versão do firmware. Com scroll 512 eu sei o que esperar. O screen-width 256 evita que linhas longas, como as da tabela de ONUs, quebrem no meio e estraguem o parser. O undo interactive desliga confirmações interativas, e o config no fim deixa a sessão pronta para comandos de interface.

Primeiro script: conectar e ler a placa

O primeiro teste com o Netmiko deve ser só leitura e numa OLT de laboratório, se você tiver uma:

import os
from netmiko import ConnectHandler

OLT = {
    "device_type": "huawei_olt",
    "host": "192.0.2.10",
    "username": os.environ["OLT_USER"],
    "password": os.environ["OLT_PASS"],
    "conn_timeout": 15,
    "session_log": "sessao-olt.log",
}

with ConnectHandler(**OLT) as conn:
    print(conn.find_prompt())
    conn.enable()
    print(conn.send_command("display board 0"))
  • ConnectHandler(**OLT) dentro de with: a conexão é fechada no fim do bloco, mesmo se der erro. Na saída, a classe manda quit e responde y à pergunta Are you sure to log out?.
  • conn_timeout: tempo máximo para abrir o TCP. OLT atrás de link ruim pede mais que o padrão de 10 segundos.
  • session_log: grava tudo o que passou pela sessão. O Netmiko troca a senha por asteriscos no arquivo, mas trate o log como dado sensível mesmo assim, porque ele tem a configuração da OLT.
  • find_prompt(): mostra o prompt atual. Se aparecer com >, você está no modo usuário.
  • enable() e send_command(): entram no modo privilegiado e rodam o comando, esperando o prompt voltar.

Se o display board 0 voltar com as placas, a conexão está certa. Outros comandos de leitura que valem para o primeiro teste estão nos comandos básicos da OLT Huawei MA5800.

Paginação e o —- More no meio da saída

A paginação é a causa mais comum de script travado em OLT Huawei. Sem desligar, a OLT para a cada tela com ---- More e espera uma tecla. O send_command do Netmiko espera o prompt, que não vem, e o script termina em ReadTimeout com metade da saída.

with ConnectHandler(**OLT) as conn:
    conn.enable()
    conn.send_command("scroll 512")
    conn.send_command("screen-width 256")
    conn.config_mode()
    saida = conn.send_command("display ont info 0 all", read_timeout=180)
    if "---- More" in saida:
        raise RuntimeError("paginação ativa: confira o scroll")

Três detalhes neste bloco. Primeiro, o scroll 512 e o screen-width 256 repetem o preparo que eu uso, para não depender do padrão do firmware. Segundo, o read_timeout=180: o padrão do send_command é de 10 segundos, pouco para listar todas as ONUs de uma OLT cheia. Terceiro, a checagem do ---- More na saída: se ele aparecer, alguma coisa no preparo falhou, e é melhor parar com erro do que gravar dado incompleto.

No meu sistema a leitura sem Netmiko faz o contrário: quando aparece ---- More, o código manda um espaço e continua lendo até a OLT ficar alguns segundos em silêncio. Funciona, mas é mais frágil do que desligar a paginação na origem.

Modos, prompts e a interface gpon

O send_command descobre o prompt atual antes de mandar o comando e espera ele voltar depois. Quando o comando muda o prompt, como interface gpon 0/1 (o prompt passa a terminar em (config-if-gpon-0/1)#), o prompt esperado nunca aparece. A saída é dizer ao Netmiko o que esperar:

conn.config_mode()
conn.send_command("interface gpon 0/1", expect_string=r"\)#")
optico = conn.send_command("display ont optical-info 0 1")
conn.send_command("quit", expect_string=r"\)#")

O expect_string=r"\)#" casa com qualquer prompt de modo de configuração, seja (config)# ou (config-if-gpon-0/1)#. O config_mode() já sabe lidar com o config da OLT, porque a classe espera )# depois dele. Dentro da interface, o display ont optical-info roda normalmente, e o quit volta para o modo config. Para entender o que ler nessa saída, veja o guia de diagnóstico de ONU com LOS.

Um erro comum é tentar rodar display ont info by-sn de dentro da interface gpon: a OLT responde Parameter error. Esse comando roda fora da interface. Por isso é bom o script saber em que modo está antes de cada comando.

Coletar display ont info 0 all e transformar em dados

O display ont info 0 all lista todas as ONUs provisionadas na OLT. É o comando que meu sistema usa para sincronizar o inventário quando a leitura por SNMP falha. Um trecho de saída, como exemplo e resumido:

  -----------------------------------------------------------------------
  F/S/P   ONT         SN         Control     Run      Config   Match
          ID                     flag        state    state    state
  -----------------------------------------------------------------------
  0/ 1/0    1  48575443A1B2C3D4  active      online   normal   match
  0/ 1/0    2  48575443A1B2C3E5  active      offline  initial  initial
  0/ 1/1    0  48575443A1B2C3F6  active      online   normal   match
  -----------------------------------------------------------------------
ColunaExemploO que significa
F/S/P0/ 1/0Frame, slot e porta. Repare no espaço depois da barra.
ONT ID1Número da ONU na porta PON.
SN48575443A1B2C3D4Serial em 16 caracteres hexadecimais.
Control flagactivedeactive é ONU desativada pela OLT.
Run stateonline / offlineEstado atual.
Config statenormalfailed indica perfil que não foi aplicado.

O parser abaixo lê só as linhas que começam com F/S/P e têm um serial de 16 caracteres hexadecimais. Essa exigência descarta o cabeçalho, as linhas de traços e a segunda tabela que a OLT imprime com as descrições das ONUs:

import re

LINHA = re.compile(
    r"\s*(\d+)/\s*(\d+)/\s*(\d+)\s+(\d+)\s+([0-9A-Fa-f]{16})"
    r"\s+(\S+)\s+(\S+)\s+(\S+)"
)


def parse_onts(raw):
    onts = []
    for linha in raw.splitlines():
        m = LINHA.match(linha)
        if not m:
            continue
        onts.append({
            "fsp": f"{m[1]}/{m[2]}/{m[3]}",
            "ont_id": int(m[4]),
            "sn": m[5].upper(),
            "control": m[6],
            "run_state": m[7],
            "config_state": m[8],
        })
    return onts

O \s* depois de cada barra existe por causa do 0/ 1/0: a OLT alinha o slot com um espaço. Sem isso, metade das linhas não casa. O resultado é uma lista de dicionários, pronta para virar CSV, ir para um banco ou alimentar um alerta de ONUs offline.

Se a ideia é acompanhar estado de ONU o tempo todo, SNMP pesa menos para a OLT do que abrir SSH a cada minuto. Veja como monitorar a OLT Huawei no Zabbix por SNMP. O Netmiko fica melhor para coleta sob demanda, auditoria e conferência de configuração.

Várias OLTs: uma sessão por thread e limite de VTY

Com uma OLT funciona, a vontade é rodar em todas ao mesmo tempo. Aqui moram os dois erros mais caros que já vi em automação de OLT.

O primeiro é dividir a mesma conexão entre threads. Meu sistema tinha uma sessão por OLT, e dois provisionamentos ao mesmo tempo escreviam no mesmo socket: os comandos de um entravam no meio dos comandos do outro. A correção foi amarrar cada sessão à thread que a pegou. Com o Netmiko vale a mesma regra: cada objeto ConnectHandler pertence a uma thread só.

O segundo é abrir sessões demais. A MA5800 aceita um número limitado de usuários conectados no VTY, e o seu script disputa essas vagas com a equipe do NOC. No meu sistema o teto é de três sessões por OLT, com fila de espera em vez de abrir mais uma.

Diagrama de um script Python com Netmiko abrindo no máximo uma sessão SSH por OLT a partir de um pool de threads
O pool limita quantas OLTs são lidas ao mesmo tempo. Cada thread tem a própria sessão.
import os
from concurrent.futures import ThreadPoolExecutor, as_completed
from netmiko import (ConnectHandler, NetmikoAuthenticationException,
                     NetmikoTimeoutException, ReadTimeout)

OLTS = [f"192.0.2.{i}" for i in range(10, 15)]
BASE = {
    "device_type": "huawei_olt",
    "username": os.environ["OLT_USER"],
    "password": os.environ["OLT_PASS"],
    "conn_timeout": 15,
}


def coletar(host):
    with ConnectHandler(**BASE, host=host) as conn:
        conn.enable()
        conn.config_mode()
        raw = conn.send_command("display ont info 0 all",
                                read_timeout=180)
    return host, parse_onts(raw)


FALHAS = (NetmikoTimeoutException, NetmikoAuthenticationException,
          ReadTimeout)

with ThreadPoolExecutor(max_workers=4) as pool:
    tarefas = {pool.submit(coletar, h): h for h in OLTS}
    for t in as_completed(tarefas):
        try:
            host, onts = t.result()
            offline = [o for o in onts if o["run_state"] == "offline"]
            print(host, len(onts), "ONUs,", len(offline), "offline")
        except FALHAS as e:
            print(tarefas[t], "falhou:", type(e).__name__)
  • ThreadPoolExecutor(max_workers=4): no máximo quatro OLTs ao mesmo tempo, uma sessão em cada. O resto espera na fila.
  • coletar() abre, lê e fecha dentro da própria thread. Nenhuma conexão sai dela.
  • FALHAS: as três exceções do Netmiko que mais aparecem. Timeout de conexão, senha errada e ReadTimeout de comando. Uma OLT fora do ar não derruba a coleta das outras.

A função parse_onts é a do bloco anterior. Num script de verdade, as duas ficam no mesmo arquivo ou num módulo importado.

Só leitura primeiro

Um laço com ont delete errado apaga clientes em segundos, em todas as OLTs da lista. Por isso, todo script novo começa só com comandos de consulta, e eu deixo isso garantido no código, não na boa vontade de quem roda:

PERMITIDOS = ("display ", "interface gpon ", "quit")


def rodar(conn, cmd, **kw):
    if not cmd.startswith(PERMITIDOS):
        raise ValueError(f"comando bloqueado: {cmd}")
    return conn.send_command(cmd, **kw)

A função rodar() recusa qualquer comando fora da lista. É uma trava simples, mas evita o pior caso: um comando de escrita montado por engano a partir de um dado mal lido. Quando chegar a hora de escrever na OLT, faça uma ONU por vez, leia o resultado antes do próximo passo e mantenha um modo de simulação que só imprime os comandos.

Para a configuração da OLT, o caminho mais seguro de começar a automatizar é o backup. O script de backup da OLT Huawei mostra a parte da OLT; o Netmiko pode ser a peça que dispara e confere esse processo.

Erros comuns e como resolver

SintomaCausa provávelCorreção
ReadTimeout num display longoPaginação ligada ou read_timeout curtoscroll 512 no preparo e read_timeout maior
ReadTimeout logo após interface gponPrompt mudou e o Netmiko esperava o antigoexpect_string=r"\)#"
NetmikoAuthenticationExceptionUsuário ou senha errados, ou usuário bloqueado na OLTTeste o login à mão com o mesmo usuário
NetmikoTimeoutExceptionPorta 22 fechada, ACL ou OLT fora do arTeste o SSH à mão a partir do mesmo servidor
Linhas cortadas no meioLargura do terminalscreen-width 256
Saída de um comando dentro de outroMesma conexão usada por duas threadsUma conexão por thread
OLT recusa novas sessõesVagas de VTY ocupadasMenos workers e fechar sessões com with

Quando o erro não é óbvio, abra o arquivo do session_log. Ele mostra exatamente o que a OLT respondeu, inclusive perguntas que o script não previa. Na maioria das vezes o problema aparece ali em poucos segundos. A documentação de cada parâmetro está no repositório oficial do Netmiko.

Perguntas frequentes

Qual device_type do Netmiko usar na OLT Huawei MA5800?

Use huawei_olt ou huawei_smartax, que apontam para a mesma classe, HuaweiSmartAXSSH. O huawei é para roteador e switch VRP e não prepara a sessão do jeito que a OLT precisa.

O Netmiko funciona por Telnet na OLT Huawei?

Funciona, com device_type="huawei_olt_telnet". Use só quando a OLT não tiver SSH, porque o Telnet manda a senha em texto puro pela rede.

Por que meu script trava no display ont info?

Quase sempre é a paginação. A OLT parou no ---- More esperando uma tecla e o Netmiko ficou esperando o prompt. Rode scroll 512 depois de conectar e aumente o read_timeout para OLTs com muitas ONUs.

Quantas sessões SSH posso abrir na mesma OLT?

Depende da configuração de VTY da sua OLT, e a equipe do NOC também usa essas vagas. Eu trabalho com no máximo três sessões automáticas por OLT e fila de espera para o resto.

Netmiko ou SNMP para monitorar ONUs?

SNMP para monitoramento contínuo, porque pesa menos na OLT e o Zabbix já sabe lidar. Netmiko para coletas sob demanda, auditoria e tudo o que só existe no CLI.

Conclusão

O Netmiko resolve a parte chata do SSH com a OLT Huawei: login, aviso pós-login, undo smart, prompt e saída limpa. O resto é com você: o device_type certo, scroll 512 e screen-width 256, expect_string quando o prompt muda, um parser que saiba do 0/ 1/0 e um pool que não divida sessão nem esgote o VTY.

Comece com um script que só lê, rode numa OLT por vez e guarde o session_log dos primeiros testes. Quando a leitura estiver confiável, a escrita vem com bem menos susto.

Compartilhar:

Sobre o autor

Ivan Alves

Especialista em redes de provedores (ISP)

Ivan Alves trabalha há mais de 15 anos com redes de provedores de internet, do NOC ao backbone e à borda BGP. Coordena infraestrutura e tecnologia em um provedor regional e escreve aqui os tutoriais que gostaria de ter encontrado quando começou: comandos testados, cenário explicado e o porquê de cada linha.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *