Automatize sua rede com pyATS (DEMO) Pt2

5 minute read

No artigo de hoje, iremos dar continuidade no artigo anterior sobre como utilizar o pyATS e o Genie para monitorar serviços na rede. Pegue seu café, se acomode e vamos nessa.

Iremos utilizar o lab mostrado na imagem abaixo. Este lab encontra-se no meu repositório do GitHub junto com o código usado neste artigo.

alt

No arquivo testbed.yaml, irei apenas alocar os dados de conexão do host CORE-SW2. O script que iremos escrever irá capturar registros da feature BGP.

devices:
  CORE-SW2:
    type: switch
    os: ios
    platform: iosl2
    alias: sw1_dev
    tacacs:
      login_prompt: 'login:'
      password_prompt: 'Password:'
      username: teste
    passwords:
      tacacs: teste
    connections:
      cli:
        protocol: ssh
        ip: 192.168.36.131
        port: 22

Para explorarmos a feature de BGP para verificar o status de vizinhança do protocolo e poder validar se a vizinhança BGP está estabelecida como desejado.

A estrutura do caso de teste inclui as seguintes etapas:

  • Configuração comum
  • Casos de teste

Configuração comum

Para essa função, iremos apenas fazer a conexão com o device ou grupo de devices que queremos validar o status do BGP.

Casos de teste

Nessa etapa é a onde acontece a mágica, iremos aprender a feature de BGP em formato de dicionário python. Dessa forma será possível capturar palavras chaves para manipular dentro do código.

Entendendo o código

Para iniciarmos, devemos importar os módulos e as bibliotecas que iremos utilizar no código:

# #####################################################################
# ####                       arquivo pyATS.py                       ###
# #####################################################################

#!/bin/env python

# Módulo para capturar log durante a execução do script
import logging
import json

# Irá construir a tabela onde será alocado dados da feature BGP
from tabulate import tabulate

# Necessário para o parâmetro aetest
from ats import aetest
from ats.log.utils import banner

# Importando módulos Genie
from genie.conf import Genie
from genie.abstract import Lookup

# Importando a library Genie
from genie.libs import ops 

log = logging.getLogger(__name__)

Depois de alocar os módulos e as bibliotecas que iremos utilizar no código, agora devemos definir a estrutura da etapa de configuração comum:

class common_setup(aetest.CommonSetup):
    """ Common Setup section """

    # CommonSetup pode haver diversas funções alocadas dentro da classe.
    
    # Definindo conexão com cada device descritos na mesa de teste que seria o arquivo testbed.yaml.
    # No caso, temos apenas um device descrito na mesa de teste.
    @aetest.subsection
    def connect(self, testbed):
        genie_testbed = Genie.init(testbed)
        self.parent.parameters['testbed'] = genie_testbed
        device_list = []
        for device in genie_testbed.devices.values():
            log.info(banner(
                "Connect to device '{d}'".format(d=device.name)))
            try:
                device.connect()
            except Exception as e:
                self.failed("Failed to establish connection to '{}'".format(
                    device.name))

            device_list.append(device)

        # Passando a lista de dispositivos para a variável "dev"
        self.parent.parameters.update(dev=device_list)

A classe acima irá servir apenas para estabelecer conexão com o dispositivo. Agora iremos montar a estrutura de casos de teste, é nessa estrutura que iremos manipular os valores que queremos utilizar para aprender e validar a vizinhança BGP dentro do dispositivo CORE-SW2:

class BGP_Neighbors_Established(aetest.Testcase):
    """ Abrindo sessão de test case """

    # First test section
    @ aetest.test
    def learn_bgp(self):
        """ Montando estrutura """
        
        # all_bgp_sessions se refere ao módulo que faz parte da library pyATS
        self.all_bgp_sessions = {}
        for dev in self.parent.parameters['dev']:
            log.info(banner("Coletando informações da feature BGP {}".format(
                dev.name)))
            abstract = Lookup.from_device(dev)
            bgp = abstract.ops.bgp.bgp.Bgp(dev)
            bgp.learn()
            self.all_bgp_sessions[dev.name] = bgp.info

    @ aetest.test
    def check_bgp(self):
        """ Manipulando dados capturados da feature """

        failed_dict = {}
        mega_tabular = []
        for device, bgp in self.all_bgp_sessions.items():
            # Os valores abaixo definidos como string se refere aos parâmetros que estão alocados dentro dos comandos do BGP
            # A ideia de aprender a feature BGP é fazer com que todos os parâmetros das saída dos comandos 
            relacionados ao BGP sejam armazenados dentro da variável dev.name
            default = bgp['instance']['default']['vrf']['default']
            neighbors = default['neighbor']
            for nbr, props in neighbors.items():
                state = props.get('session_state')
                if state:
                    tr = []
                    tr.append(device)
                    tr.append(nbr)
                    tr.append(state)
                    if state == 'established' or state == 'Established':
                        tr.append('Passed')
                    else:
                        failed_dict[device] = {}
                        failed_dict[device][nbr] = props
                        tr.append('Failed')

                mega_tabular.append(tr)

        log.info(tabulate(mega_tabular,
                          headers=['Device', 'Peer',
                                   'State', 'Pass/Fail'],
                          tablefmt='orgtbl'))

        if failed_dict:
            log.error(json.dumps(failed_dict, indent=3))
            self.failed("Testbed has BGP Neighbors that are not established")

        else:
            self.passed("All BGP Neighbors are established")

A estrutura abaixo irá desconectar do dispositivo depois de ser validado o status de vizinhança:

class common_cleanup(aetest.CommonCleanup):
    """ Common Cleanup """
    @aetest.subsection
    def clean_everything(self):
        """ Common Cleanup Subsection """
        log.info("Aetest Common Cleanup ")

if __name__ == '__main__':  
    aetest.main()

Estrutura totalmente montada e pronta para trabalhar em cima do arquivo de mesa (testbed.yaml). O script acima irá rodar dentro de um arquivo python, dentro da estrutura do pyATS esse arquivo é denominado de job file. Você pode dar o nome que quiser para este arquivo. Abaixo é mostrado a estrutura do arquivo:

# #####################################################################
# ####                       arquivo job  .py                       ###
# #####################################################################

import os
from ats.easypy import run

# A função abaixo irá rodar o script pyTAS.py
def main():
    # Find the location of the script in relation to the job file
    pwd = os.path.dirname(__file__)
    bgp_tests = os.path.join(pwd, 'pyTAS.py')
    # Execute the testscript
    # run(testscript=testscript)
    run(testscript=bgp_tests)

Até aqui está tudo ok, agora iremos rodar o arquivo job.py:

thiago@thiago-ThinkPad:~/Documentos/Code/Python/Networking/IOS/network-automation/pyats$ pyats run job job.py --testbed-file testbed.yaml

Report das tasks executadas:

2020-05-18T14:17:31: %EASYPY-INFO: Overall Stats
2020-05-18T14:17:31: %EASYPY-INFO:     Passed     : 3
2020-05-18T14:17:31: %EASYPY-INFO:     Passx      : 0
2020-05-18T14:17:31: %EASYPY-INFO:     Failed     : 0
2020-05-18T14:17:31: %EASYPY-INFO:     Aborted    : 0
2020-05-18T14:17:31: %EASYPY-INFO:     Blocked    : 0
2020-05-18T14:17:31: %EASYPY-INFO:     Skipped    : 0
2020-05-18T14:17:31: %EASYPY-INFO:     Errored    : 0
2020-05-18T14:17:31: %EASYPY-INFO: 
2020-05-18T14:17:31: %EASYPY-INFO:     TOTAL      : 3
2020-05-18T14:17:31: %EASYPY-INFO: 
2020-05-18T14:17:31: %EASYPY-INFO: Success Rate   : 100.00 %
2020-05-18T14:17:31: %EASYPY-INFO: 
2020-05-18T14:17:31: %EASYPY-INFO: +------------------------------------------------------------------------------+
2020-05-18T14:17:31: %EASYPY-INFO: |                             Task Result Summary                              |
2020-05-18T14:17:31: %EASYPY-INFO: +------------------------------------------------------------------------------+
2020-05-18T14:17:31: %EASYPY-INFO: Task-1: pyATS.common_setup                                                PASSED
2020-05-18T14:17:31: %EASYPY-INFO: Task-1: pyATS.BGP_Neighbors_Established                                   PASSED
2020-05-18T14:17:31: %EASYPY-INFO: Task-1: pyATS.common_cleanup                                              PASSED
2020-05-18T14:17:31: %EASYPY-INFO: 
2020-05-18T14:17:31: %EASYPY-INFO: +------------------------------------------------------------------------------+
2020-05-18T14:17:31: %EASYPY-INFO: |                             Task Result Details                              |
2020-05-18T14:17:31: %EASYPY-INFO: +------------------------------------------------------------------------------+
2020-05-18T14:17:31: %EASYPY-INFO: Task-1: pyATS
2020-05-18T14:17:31: %EASYPY-INFO: |-- common_setup                                                          PASSED
2020-05-18T14:17:31: %EASYPY-INFO: |   `-- connect                                                           PASSED
2020-05-18T14:17:31: %EASYPY-INFO: |-- BGP_Neighbors_Established                                             PASSED
2020-05-18T14:17:31: %EASYPY-INFO: |   |-- learn_bgp                                                         PASSED
2020-05-18T14:17:31: %EASYPY-INFO: |   `-- check_bgp                                                         PASSED
2020-05-18T14:17:31: %EASYPY-INFO: `-- common_cleanup                                                        PASSED
2020-05-18T14:17:31: %EASYPY-INFO:     `-- clean_everything                                                  PASSED
2020-05-18T14:17:31: %EASYPY-INFO: Sending report email...
2020-05-18T14:17:31: %EASYPY-INFO: Missing SMTP server configuration, or failed to reach/authenticate/send mail. Result notification email failed to send.
2020-05-18T14:17:31: %EASYPY-INFO: Done!

Report da validação do status do BGP:

2020-05-18T14:17:31: %AETEST-INFO: The result of section learn_bgp is => PASSED
2020-05-18T14:17:31: %AETEST-INFO: +------------------------------------------------------------------------------+
2020-05-18T14:17:31: %AETEST-INFO: |                          Starting section check_bgp                          |
2020-05-18T14:17:31: %AETEST-INFO: +------------------------------------------------------------------------------+
2020-05-18T14:17:31: %SCRIPT-INFO: | Device   | Peer         | State       | Pass/Fail   |
2020-05-18T14:17:31: %SCRIPT-INFO: |----------+--------------+-------------+-------------|
2020-05-18T14:17:31: %SCRIPT-INFO: | CORE-SW2 | 189.77.20.30 | Established | Passed      |

2020-05-18T14:17:31: %AETEST-INFO: Passed reason: All BGP Neighbors are established
2020-05-18T14:17:31: %AETEST-INFO: The result of section check_bgp is => PASSED
2020-05-18T14:17:31: %AETEST-INFO: The result of testcase BGP_Neighbors_Established is => PASSED

Ainda no mesmo diretório, ao executar o comando pyats logs view você consegue fazer o monitoramento em tempo real do serviço pela web:

alt

E aí, o que achou dessas ferramentas? Comente aqui embaixo, vai ser legal contar com sua presença por aqui.

Leave a Comment