Um artigo sobre como trabalhar com Junos PyEZ - “Microframework Python que permite gerenciar e automatizar dispositivos que executam o Junos OS” automação e gerenciamento, tudo o que amamos. Escrever o script descrito neste artigo teve vários objetivos: aprender Python e automatizar tarefas de coleta de informações ou alteração de configurações em equipamentos que executam o Junos OS. A escolha desta combinação específica de Python + Junos PyEZ foi feita devido à baixa barreira de entrada na linguagem de programação Python e à facilidade de uso da biblioteca Junos PyEZ, que não requer conhecimento especializado de Junos OS.
Tarefa
Auditoria de sub-redes ipv4 gratuitas pertencentes à empresa. O critério para que uma sub-rede seja livre é a ausência de uma entrada sobre ela nas rotas do switch que atua como um roteador executando o Junos OS.
Implementação
Python + Junos PyEZ, embora houvesse a tentação de fazer isso por meio de paramiko e ssh.exec_command,
A instalação da versão atual do Junos PyEZ do PyPI é feita com o seguinte comando:
$ pip install junos-eznc
Você também pode instalar a partir do branch principal do projeto no GitHub com o seguinte comando:
$ pip install git+https://github.com/Juniper/py-junos-eznc.git
E mais uma opção via
$ pip install -r requirements.txt
Este comando instalará bibliotecas que estão faltando no sistema e são necessárias para a operação. Na minha versão requisitos.txt Existem apenas dois deles, as versões mais recentes são indicadas no momento da escrita do script:
junos-eznc
netaddr
Por padrão, o script usa o nome do usuário atual no sistema; você pode fazer login com o nome de outro usuário usando a chave show_route.py -u getpass.getpass pega a senha do stdin para que a senha não permaneça no sistema. Para se conectar ao equipamento, você também precisará inserir o nome do host ou endereço IP quando solicitado. Todos os dados necessários para autorização no dispositivo foram recebidos.
Junos PyEZ oferece suporte à conexão com equipamentos que executam o Junos OS usando o console, telnet ou netconf via ssh. O artigo discute a última opção.
Para conectar ao equipamento, utilize a classe Device do módulo jnpr.junos
with jnpr.junos.Device(host=router,
user=args.name,
passwd=password) as dev:
Uma solicitação é feita para todas as rotas conhecidas pelo roteador por meio de chamada de procedimento remoto ou chamada de procedimento remoto, o que for mais conveniente.
data = dev.rpc.get_route_information()
Comando semelhante no Junos OS
user@router> show route | display xml
Ao adicionar rpc ao final do comando, obtemos uma tag de solicitação e podemos combiná-la com o nome do método RPC, desta forma podemos descobrir outros nomes de interesse. Vale ressaltar que a sintaxe para escrever a tag request é diferente do nome do método, ou seja, deve-se substituir os hífens por sublinhados.
user@router> show route | display xml rpc
<rpc-reply >route_list = data.xpath("//rt-destination/text()")
O restante da parte foi envolvido em um loop while, para não repetir a solicitação ao roteador caso fosse necessário fazer check-in em outra sub-rede daquelas que o roteador já conhece. Vale ressaltar que o roteador no qual estou fazendo a solicitação conhece rotas apenas pelo OSPF, então para um roteador de borda é melhor alterar um pouco a solicitação para diminuir o tempo de execução do script
data = dev.rpc.get_ospf_route_information()
Agora vamos dar uma olhada no conteúdo do loop while
Inicialmente, o usuário será solicitado a inserir uma sub-rede com máscara e no máximo três octetos da rede da mesma sub-rede, isso é necessário para definir o intervalo de pesquisa. Não gosto muito dessa implementação de especificação de critérios e intervalo de pesquisa, mas até agora não encontrei uma solução melhor. A seguir, da lista resultante de sub-redes route_list, usando uma variável contendo no máximo três octetos, seleciono as sub-redes que me interessam
tmp = re.search(r'^%sS*' % subnet_search, route_list[i])
Através do IPNetwork, módulo netaddr, recebo sub-redes na forma de uma lista de endereços IPv4
range_subnet = netaddr.IPNetwork(tmp.group(0))
Usando IPNetwork, obtenho um intervalo de endereços de uma rede inserida pelo usuário com uma máscara e gero uma lista de todos os endereços desse intervalo para comparação com a lista de endereços ocupados.
for i in set(net_list).difference(set(busyip)):
freeip.append(i)
Eu exibo a lista resultante de endereços gratuitos na forma de sub-redes
print(netaddr.IPSet(freeip))
Abaixo segue o script completo, testado em switches usados como roteador, modelos ex4550, ex4600
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import getpass
import netaddr
import re
import sys
import jnpr.junos
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--user',
action='store',
dest='name',
help='Enter login from tacacs if it differs from the '
'username in the system.')
args = parser.parse_args()
if not args.name:
args.name = getpass.getuser() # Return the “login name” of the user.
router = input("Full routers name: ")
password = getpass.getpass("Password: ")
try:
# Authenticates to a device running Junos, for get information about routs
# into xml format and selects by tag.
route_list = []
with jnpr.junos.Device(host=router,
user=args.name,
passwd=password) as dev:
data = dev.rpc.get_route_information()
route_list = data.xpath("//rt-destination/text()")
except (jnpr.junos.exception.ConnectRefusedError,
jnpr.junos.exception.ConnectUnknownHostError) as err:
print("Equipment name or password wrong.")
sys.exit(1)
while True:
subnet = input("Net with mask: ")
subnet_search = input("Input no more three octet: ")
# Gets a list of busy IP addresses from the received subnets.
busyip = []
for i in range(len(route_list)):
tmp = re.search(r'^%sS*' % subnet_search, route_list[i])
if tmp:
range_subnet = netaddr.IPNetwork(tmp.group(0))
for ip in range_subnet:
busyip.append("%s" % ip)
range_subnet = netaddr.IPNetwork(subnet)
# Gets list ip adresses from subnetworks lists.
net_list = []
for ip in range_subnet:
net_list.append("%s" % ip)
# Сomparing lists.
freeip = []
for i in set(net_list).difference(set(busyip)):
freeip.append(i)
print(netaddr.IPSet(freeip))
request = input("To run request again enter yes or y, "
"press 'enter', complete request: ")
if request in ("yes", "y"):
continue
else:
print('Bye')
break
Fonte: habr.com