Telnet com Python em OLTs

Aprenda a manipular uma conexão telnet via Python para automatizar processos na sua OLT

Giácomo Quinalia
6 min readApr 23, 2021

INTRODUÇÃO

Neste artigo, vou te mostrar como utilizar o python para manipular o protocolo telnet e automatizar processos na OLT. Para este artigo, vou utilizar a telnetlib do python 3.9 e uma OLT Huawei MA5800, onde irei acessá-la, enviar comandos e receber suas respostas.

Ao final do artigo, abra sua mente e pense em criar scripts, APIs e até sistemas para automatizar processos como provisionamento e desprovisionamento de ONUs e muito mais.

Apesar do foco deste artigo ser em OLTs, você pode utilizar os conhecimentos ensinados aqui para aplicar em qualquer outro equipamento que desejar e que possua um cliente telnet.

1. Acessando e se autenticando na OLT

import telnetlib

host = '10.11.104.2'
port = 23

try:
telnet = telnetlib.Telnet(host, port, timeout=5)
except Exception:
print("Erro ao realizar a conexao!")

Primeiro importamos a telnetlib para manipularmos o protocolo telnet através de seus recursos.

Depois, informamos o host do equipamento, no caso o da OLT, e a porta telnet a ser utilizada, nesse caso, a porta padrão 23.

Por fim, dentro de um bloco “try” para tratarmos os erros, utilizamos “telnetlib.Telnet” para iniciar a conexão, com os parâmetros host e port, e o parâmetro opcional “timeout”, que encerra a conexão após o tempo especificado em segundos.

Na maioria dos casos, você vai precisar se autenticar no equipamento com os dados de usuário e senha, como no caso da OLT, então:

user = b'giacomo\n'
password = b'segredo\n'

telnet.read_until(b'name:')
telnet.write(user)

telnet.read_until(b'password:')
telnet.write(password)

Informamos o usuário e senha nas variáveis user e password. Note que será necessária a utilização da letra “b” antes das aspas da string a ser informada, para que o valor da string seja convertido para bytes, de forma que a OLT ou equipamento alvo possa entender o dado. Isso também será feito para ler os dados de resposta da OLT.

Também é importante sempre enviar o comando “\n” para que seja quebrada uma linha, como se estivessemos teclando “enter”, dessa forma o comando será executado e não só “digitado na tela”.

Opcionalmente, você pode usar a função “encode”, para converter a string para bytes, e “decode” para converter bytes em strings (veremos o “decode” mais tarde). Exemplo do encode:

user = 'giacomo\n'.encode('utf-8') # utf-8 parametro opcional
password = 'segredo\n'.encode('utf-8')

Agora, precisamos enviar e receber os dados, para isso, utilizaremos as funções:

  • read_until: read significa “ler”, until significa “até”, por tanto, vamos ler os dados até uma condição de parada ( uma string a ser lida), que será passada como parâmetro. Nesse caso, vamos ler até encontrarmos o texto “name:” na resposta.
  • write: Com o write, podemos enviar os dados para a OLT ou equipamento alvo, assim executando comandos.

No exemplo, estamos lendo a resposta até encontrarmos “name:” e enviando o usuário da OLT para autenticação. Depois, lemos até “password:” e enviamos o valor da senha.

Nesse momento, você já está conectado e autenticado na OLT. Agora vamos aprender a executar comandos.

2. Executando comandos

Basta utilizarmos a função write para enviar os comandos. Vamos executar o comando “display ont autofind all”:

telnet.write(b'enable\n')
telnet.write(b'config\n')
telnet.write(b'display ont autofind all\n\n')

Primeiro executamos dois comandos, “enable” e ”config”, para depois executar o comando que queremos de fato. Com isso, note duas coisas:

  1. A conexão “espera” que seja pedido algo para que o próximo comando seja executado. Então você pode enviar quantos comandos quiser, mas não vão ser lidos, apenas executados.
  2. No último comando que mandamos para a OLT, enviamos dois “\n”, isso por que se estivessemos na OLT, precisariamos teclar “enter” duas vezes, uma para executar o comando e outra para confirmação.

Caso você tenha muitos comandos a serem executados, você pode fazer algo como isso:

comandos = [
'enable',
'config',
'display ont autofind all\n',
]
for comando in comandos:
cmd = f'{comando}\n'.encode('utf-8')
telnet.write(cmd)

Colocamos os comandos em uma lista e, com um loop, para cada comando na lista, convertemos o valor da string em bytes e adicionamos o “\n” ao final para que seja executado, em seguida enviamos para a OLT.

Veja que no ultimo comando existe um “\n”, isso por que precisamos de dois “enters”, então adicionei um no comando e o outro será concatenado após passar pelo encode.

3. Lendo os dados

Para ver o que está acontecendo na OLT ou equipamento alvo, você pode utilizar o “read_until” de duas formas:

  1. Ler até um texto inserido
...

telnet.write(b'querocafe')

data = telnet.read_until(b'querocafe', timeout=5).decode('utf-8')

Utilizando este método, após executar todos os comandos necessários, enviamos o texto “querocafe” para a OLT (nesse caso não precisamos do “\n”), em seguida pedimos para a conexão ler até esse texto. A conexão então lerá desde o primeiro byte de informação disponível após a autenticação até encontrar “querocafe”.

Se por algum motivo a conexão não encontrar o texto que queremos, ela irá parar de ler após 5 segundos, tempo que definimos no parâmetro timeout. Então, tratá tudo o que conseguiu ler durante 5 segundos. Caso contrário, com o texto encontrado, fizemos um “decode” para converter os bytes em uma string legível para seres humanos e atribuímos a variável “data”.

Supondo que o log será mostrado para um usuário, podemos excluir a linha que contém o texto “querocafe”, desse forma:

...

lines = data.splitlines()
lines.pop(-1)

log = '\n'.join(lines)

print(log)

2. Ler até um texto existente

Dessa forma vamos enviar um comando e, entre a execução de cada um deles, vamos ler até um texto que sabemos que sempre vai estar na OLT. A vantagem desse método é que você pode debugar o que aconteceu entre um comando e outro. Porém, você precisa saber qual o resultado final da execução do comando. Exemplo:

giacomo@ubuntu:~$ ls
bin Documents Music Public Templates Videos
Desktop Downloads Pictures snap
giacomo@ubuntu:~$

Nesse caso, sabemos que após o comando “ls” ser executado, podemos executar outro comando quando o texto “giacomo@ubuntu:~$” aparecer novamente. O mesmo se aplica para a OLT, então:

comandos = [
('enable', 'OLT-HUAWEI#')
('config', 'OLT-HUAWEI(config)#'),
('display ont autofind all\n', 'OLT-HUAWEI(config)#'),
]

log = ''

for comando, fim in comandos:
cmd = f'{comando}\n'.encode('utf-8')
telnet.write(cmd)
result = telnet.read_until(fim)
log += result

if DEBUG and 'Failure' in result:
break

print(log)

Substitua OLT-HUAWEI com o nome da sua OLT

No exemplo acima, ao invés de informar apenas o comando, precisamos informar também o fim dele. Fizemos isso utilizando uma “tupla”, onde o primeiro valor é o comando e o segundo, seu fim.

Declaramos a variável “log” para armazenar os resultados da execução de cada comando.

Em seguida, no “for”, para cada comando/fim dentro da lista de comandos, enviamos o comando e lemos a resposta até o fim do comando, atribuíndo o resultado à variável “result” e concatenando com a variável “log”.

Por último, se a variável DEBUG for “True” e existir a palavra “Failure” no resultado que acabamos de conseguir, iremos fazer algo, nesse caso, parei a execução do loop para que não sejam executados mais comandos e o log seja mostrado para o usuário verificar o erro.

Para fechar a conexão utilize: telnet.close()

COMANDOS ÚTEIS

Para automatizar o processo de provisionamento e desprovisionamento, deixo o array de comandos necessários com os valores variáveis.

  • Copie e cole em um editor de texto para melhor vizualização como o Visual Studio Code, Sublime Text 3, Atom ou Pycharm.
  • Obtenha os valores de forma variável.

Provisionamento em modo bridge:

  • Método 1 do item 3 (Ler até um texto inserido)
commands = [
'enable',
'scroll 512',
'config',
'interface gpon 0/{slot}',
f'ont add {pon} {_id} sn-auth {serial} omci ont-lineprofile-name {line_profile} ont-srvprofile-name {name_srv_profile} desc {desc}\n',
f'ont port native-vlan {pon} {_id} eth 1 vlan {vlan} priority 0',
'quit',
f'service-port vlan {vlan} gpon 0/{slot}/{pon} ont {_id} gemport {gemport} multi-service user-vlan {user_vlan} tag-transform translate\n',
f'interface gpon 0/{slot}',
f'ont reset {pon} {_id}',
'y',
]
  • Método 2 do item 3 (Ler até um texto existente)
commands = [
('enable', '%s#' % olt),
('scroll 512', '%s#' % olt),
('config', f'{olt}(config)#'),
(f'interface gpon 0/{slot}', f'{olt}(config-if-gpon-0/{slot})#'),
f'ont add {pon} {_id} sn-auth {serial} omci ont-lineprofile-name {line_profile} ont-srvprofile-name {name_srv_profile} desc {desc}\n', f'{olt}(config-if-gpon-0/{slot})#'),
(f'ont port native-vlan {pon} {_id} eth 1 vlan {vlan} priority 0', f'{olt}(config-if-gpon-0/{slot})#'),
('quit', f'{olt}(config)#'),
(f'service-port vlan {vlan} gpon 0/{slot}/{pon} ont {_id} gemport {gemport} multi-service user-vlan {user_vlan} tag-transform translate\n', f'{olt}(config)#'),
(f'interface gpon 0/{slot}', f'{olt}(config-if-gpon-0/{slot})#'),
(f'ont reset {pon} {_id}', '(y/n)[n]: '),
('y', f'{olt}(config-if-gpon-0/{slot})#')
]

Desprovisionamento:

  • Método 1 do item 3 (Ler até um texto inserido)
commands = [
f'undo service-port port 0/{slot}/{pon} ont {_id}\n',
'y',
f'interface gpon 0/{slot}',
f'ont delete {pon} {_id}'
]
  • Método 2 do item 3 (Ler até um texto existente)
commands = [
(f'undo service-port port 0/{slot}/{pon} ont {_id}\n', '(y/n)[n]:'),
('y', f'{olt}(config)#'),
(f'interface gpon 0/{slot}', f'{olt}(config-if-gpon-0/{slot})#'),
(f'ont delete {pon} {_id}', f'{olt}(config-if-gpon-0/{slot})#')
]

Veja tambem: SSH com Python em OLTs (em breve).

--

--