Junos PyEZ a partir de um exemplo de tarefa para encontrar sub-redes ipv4 livres

Artigo sobre o trabalho com Junos PyEZ — “microframework Python que permite gerenciar e automatizar dispositivos que executam o Junos OS” automação e gerenciamento, tudo como gostamos. A redação do script descrito neste artigo tinha vários objetivos — estudar Python e automatizar tarefas de coleta de informações ou alteração de configuração em equipamentos sob gerenciamento do Junos OS. A escolha dessa combinação 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 conhecimentos especializados em Junos OS.

Tarefa

Auditoria de sub-redes livres ipv4 pertencentes à empresa. O critério para determinar que a sub-rede está livre é a ausência de registro dela nas rotas no switch atuando como roteador sob Junos OS.

Implementação

Python + Junos PyEZ, embora houvesse a tentação de fazer através de paramiko e ssh.exec_command, como consequência, será necessário configurar o protocolo de gerenciamento de dispositivos de rede netconf no equipamento em questão. O netconf funciona com equipamentos por meio de chamadas de procedimento remoto (remote procedure call RPC) e utiliza XML, no exemplo em consideração, para fornecer as informações obtidas.

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 da 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 através de

$ pip install -r requirements.txt 

este comando instalará as bibliotecas que estão faltando no sistema e são necessárias para o funcionamento. Na minha versão, requirements.txt são apenas duas, as versões estão especificadas como as mais recentes no momento da redação do script:

junos-eznc
netaddr

O script por padrão pega o nome do usuário atual no sistema, para logar-se como outro usuário, pode-se usar a chave show_route.py -u ; getpass.getpass pega a senha do stdin assim a senha não fica armazenada no sistema. Para conectar ao equipamento, também será necessário inserir, quando solicitado, seu hostname ou endereço IP. Todos os dados necessários para a autenticação no dispositivo foram obtidos.

Junos PyEZ suporta conexão com equipamentos que executam o Junos OS usando console, telnet ou netconf via ssh. Neste artigo, o último método é abordado.

Para conectar ao equipamento, utiliza-se 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 procedimentos remotos, como for mais conveniente.

data = dev.rpc.get_route_information()

Comando equivalente no Junos OS

user@router> show route | display xml

Adicionando o rótulo rpc ao final do comando, teremos a tag de solicitação e podemos corresponder com o nome do método RPC, dessa forma é possível descobrir outros nomes de interesse. É importante notar que a sintaxe da tag de solicitação difere do nome do método, ou seja, devemos substituir os sinais de traço por sublinhados.

user@router> show route | display xml rpc
route_list = data.xpath("//rt-destination/text()")

O restante foi encapsulado em um loop while para não executar novamente a solicitação ao roteador, caso precise verificar em outra sub-rede das quais o roteador já conhece. Vale mencionar que o roteador que estou consultando conhece as rotas apenas por OSPF, portanto, para o roteador de borda, é melhor alterar um pouco a solicitação para reduzir o tempo de execução do script.

data = dev.rpc.get_ospf_route_information()

Agora vamos analisar o conteúdo do loop while

No início, o usuário será solicitado a inserir a sub-rede com a máscara e no máximo três octetos da rede dessa mesma sub-rede, isso é necessário para definir o intervalo de busca. Não gosto muito dessa implementação para definir o critério e o intervalo de busca, mas até agora não encontrei uma solução melhor. Em seguida, da lista de sub-redes route_list obtida, uso a variável contendo no máximo três octetos para selecionar as sub-redes de interesse.

tmp = re.search(r'^%sS*' % subnet_search, route_list[i])

Através de IPNetwork, do módulo netaddr, obtenho sub-redes na forma de uma lista de endereços ipv4.

range_subnet = netaddr.IPNetwork(tmp.group(0))

Usando IPNetwork da rede inserida pelo usuário com a máscara, obtenho o intervalo de endereços e formo 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)

Exibo a lista obtida de endereços disponíveis na forma de sub-redes.

print(netaddr.IPSet(freeip))

Abaixo está o script completo, testado em switches usados como roteadores, 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

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster