Como usar a biblioteca de solicitações para Python em dispositivos que executam o Junos OS
A biblioteca de solicitações para Python está disponível em determinados dispositivos que executam o Junos OS que oferecem suporte ao pacote de extensões Python. Você pode usar o requests
módulo em scripts Python para enviar solicitações de HTTP/1.1. Em dispositivos que executam o Junos OS com automação aprimorada, você também pode usar o requests
módulo no modo interativo Python. A biblioteca De solicitações fornece métodos adicionais para dar suporte a implantações iniciais, bem como para realizar mudanças de rotina de monitoramento e configuração em dispositivos que executam o Junos OS. Para obter informações sobre o requests
módulo e suas funções, consulte a documentação de solicitações em http://docs.python-requests.org/.
Emissão de solicitações
Você pode usar o requests
módulo em scripts Python na caixa para enviar solicitações HTTP/1.1. Para fazer uma solicitação, importe o módulo em seu script e ligue para a função correspondente à solicitação desejada. O módulo oferece suporte a HTTP GET
e POST
solicitações, bem como HEAD
, DELETE
e PUT
solicitações. A solicitação devolve um objeto de resposta contendo a resposta do servidor. Por padrão, as solicitações são feitas usando a instância de roteamento padrão.
A biblioteca de solicitações pode ser usada para executar RPCs em dispositivos que executam o Junos OS que oferecem suporte ao serviço REST API. O dispositivo alvo deve ser configurado com as declarações apropriadas no nível de [edit system services rest]
hierarquia para habilitar os comandos Junos OS em HTTP ou HTTPS usando REST.
Por exemplo, o script op a seguir executa uma solicitação GET que executa o get-software-information
RPC em um dispositivo remoto que executa o Junos OS que tem o serviço REST API sobre HTTP configurado na porta padrão (3000). O script imprime o código de status de resposta e, se o código de status indica sucesso, ele imprime o conteúdo de resposta.
from junos import Junos_Context import jcs import requests user = Junos_Context['user-context']['user'] password = jcs.get_secret('Enter user password: ') r = requests.get('http://198.51.100.1:3000/rpc/get-software-information', auth=(user, password)) print (r.status_code) if (r.status_code == requests.codes.ok): print (r.text)
user@host> op show-version.py Enter user password: 200 <software-information> <host-name>router1</host-name> <product-model>mx240</product-model> <product-name>mx240</product-name> <junos-version>18.3R1.8</junos-version> <package-information> <name>os-kernel</name> ...
Para recuperar apenas os cabeçalhos, você pode enviar uma solicitação simples HEAD
.
r = requests.head('http://198.51.100.1:3000/rpc/get-software-information', auth=(user, password)) print (r.headers) print (r.headers['content-type'])
user@host> op request-headers.py Enter user password: {'Date': 'Tue, 02 Apr 2019 18:30:58 GMT', 'Connection': 'close', 'Content-Type': 'application/xml; charset=utf-8', 'Server': 'lighttpd/1.4.48'} application/xml; charset=utf-8
Se uma solicitação GET exigir parâmetros adicionais, você pode incluir o params
argumento e fornecer um cookies ou uma lista de tuples ou bytes para enviar a cadeia de consulta, ou você pode passar em pares de chave/valor como parte do URL. Da mesma forma, você pode fornecer cabeçalhos personalizados, incluindo o headers
argumento e um níquete de cabeçalhos HTTP.
A solicitação a seguir executa o get-interface-information
RPC com a opção terse para a interface determinada e retorna a resposta em formato de texto:
headers={'content-type': 'application/xml', 'Accept': 'text/plain'} params={'interface-name':'ge-2/0/1', 'terse':''} r = requests.get('http://198.51.100.1:3000/rpc/get-interface-information', auth=(user, password), headers=headers, params=params)
O exemplo a seguir fornece os argumentos como pares de chave/valor na URL:
headers={'content-type': 'application/xml', 'Accept': 'text/plain'} rpc = 'get-interface-information?interface-name=ge-2/0/1&terse=' r = requests.get('http://198.51.100.1:3000/rpc/' + rpc, auth=(user, password), headers=headers)
Para executar vários RPCs na mesma solicitação, inicie uma solicitação HTTP POST e defina o data
parâmetro para fazer referência aos RPCs a executar. Veja seções executando RPCs operacionais e gerenciando a configuração para exemplos que executam vários RPCs.
Execução de RPCs operacionais
Você pode usar o requests
módulo para executar RPCs a partir da API Junos XML em um dispositivo remoto que executa o Junos OS que tem o serviço REST API habilitado.
O script de operação a seguir usa o requests
módulo para executar o RPC equivalente ao show interfaces ge-2/0/1 terse
comando do modo operacional no dispositivo alvo:
from junos import Junos_Context import jcs import requests user = Junos_Context['user-context']['user'] password = jcs.get_secret('Enter user password: ') r = requests.get('http://198.51.100.1:3000/rpc/get-interface-information', auth=(user, password), params={'interface-name':'ge-2/0/1','terse':''}) print(r.text)
O script de operação a seguir envia uma solicitação POST que executa vários RPCs no dispositivo alvo. O data
parâmetro faz referência aos RPCs a serem executados, que são definidos em uma cadeia multiline para leitura.
from junos import Junos_Context import jcs import requests user = Junos_Context['user-context']['user'] password = jcs.get_secret('Enter user password: ') headers={'content-type': 'application/xml', 'Accept': 'text/plain'} payload=""" <get-software-information/> <get-interface-information> <interface-name>ge-2/0/1</interface-name> </get-interface-information>""" r = requests.post('http://198.51.100.1/rpc/', auth=(user, password), headers=headers, data=payload) if (r.status_code == requests.codes.ok): print (r.text)
Você também pode criar um script de operação genérico para o qual o usuário fornece as variáveis necessárias e o script constrói e executa a solicitação. Considere a seguinte configuração de script de operação, que configura os argumentos de host
linha de comando erpc_args
, rpc
para o script de operação requests-rpc.py:
[edit system scripts] op { file requests-rpc.py { arguments { host { description "host:port to which to connect"; } rpc { description "base RPC to execute on target device"; } rpc_args { description "dictionary of RPC arguments to use"; } } } } language python;
O script de operação de amostra a seguir se conecta a um dispositivo remoto que executa o Junos OS, que foi configurado com as declarações apropriadas no nível de hierarquia para permitir que os [edit system services rest]
comandos do Junos OS via HTTP usem REST. O script solicita a senha da conexão e se conecta ao host e à porta fornecidos através do host
argumento. Em seguida, o script usa o requests
módulo para enviar uma solicitação GET executando o RPC que foi fornecido através dos argumentos da linha de comando.
A partir do Junos OS Release 21.2R1 e junos OS Evolved Release 21.2R1, quando o dispositivo passa argumentos de linha de comando para um script de operação Python, ele prefixa um único hífen (-) para nomes de argumentos de um único personagem e prefixa dois hífens (--) para nomes de argumentos de vários caracteres. Em versões anteriores, os dispositivos prefixam um único hífen (-) em todos os nomes de argumentos.
# Junos OS Release 21.1 and earlier from junos import Junos_Context from ast import literal_eval import jcs import argparse import requests ## Argument list as configured in [edit system scripts op] arguments = { 'host': 'host:port to which to connect', 'rpc' : 'base RPC to execute on target device', 'rpc_args' : 'dictionary of RPC arguments to use' } ## Retrieve script arguments (Junos OS Release 21.1 and earlier) parser = argparse.ArgumentParser(description='This is a demo script.') for key in arguments: if key == 'rpc_args': parser.add_argument(('-' + key), help=arguments[key]) else: parser.add_argument(('-' + key), required=True, help=arguments[key]) args = parser.parse_args() ## Convert rpc_args to a dictionary if args.rpc_args is not None: args.rpc_args = literal_eval(args.rpc_args) ## Retrieve username and prompt for password for connecting to target device user = Junos_Context['user-context']['user'] password = jcs.get_secret('Enter user password: ') ## Execute RPC if args.rpc_args is None: r = requests.get('http://' + args.host + '/rpc/' + args.rpc, auth=(user, password)) else: r = requests.get('http://' + args.host + '/rpc/' + args.rpc, auth=(user, password), params=args.rpc_args) ## Print RPC contents if HTTP status code indicates success if (r.status_code == requests.codes.ok): print (r.text) else: print (r.status_code)
Quando você executa o script, ele executa o RPC com as opções especificadas no dispositivo remoto e imprime a resposta à saída padrão.
user@host> op requests-rpc.py host 198.51.100.1:3000 rpc get-interface-information rpc_args {'interface-name':'ge-2/0/1','terse':''} Enter user password: <interface-information xmlns="http://xml.juniper.net/junos/18.3R1/junos-interface" xmlns:junos="http://xml.juniper.net/junos/*/junos" junos:style="terse"> <physical-interface> <name>ge-2/0/1</name> <admin-status>up</admin-status> <oper-status>up</oper-status> </physical-interface> </interface-information>
Gerenciando a configuração
Você pode usar o requests
módulo para recuperar ou alterar a configuração em um dispositivo que executa o Junos OS que tem o serviço REST API habilitado.
O script de operação a seguir recupera a [edit system]
hierarquia da configuração do candidato usando uma solicitação POST:
from junos import Junos_Context import jcs import requests user = Junos_Context['user-context']['user'] password = jcs.get_secret('Enter user password: ') headers = { 'content-type' : 'application/xml' } payload = '<get-configuration><configuration><system/></configuration></get-configuration>' r = requests.post('http://198.51.100.1:3000/rpc/', auth=(user, password), data=payload, headers=headers) print (r.content)
As solicitações de HTTP POST também permitem que você execute vários RPCs em uma única solicitação, por exemplo, para bloquear, carregar, comprometer e desbloquear uma configuração.
O script de operação de amostra a seguir se conecta ao dispositivo remoto e configura um endereço na interface determinada. As operações de bloqueio, carga, compromisso e desbloqueio são definidas separadamente para a leitura, mas os RPCs são concatenados na solicitação.
from junos import Junos_Context import jcs import requests user = Junos_Context['user-context']['user'] password = jcs.get_secret('Enter user password: ') #### lock, load, commit, unlock configuration headers={'content-type': 'application/xml'} lock = '<lock><target><candidate/></target></lock>' load = """<edit-config> <target><candidate></candidate></target> <default-operation>merge</default-operation> <config> <configuration> <interfaces> <interface> <name>ge-2/0/1</name> <unit> <name>0</name> <family> <inet> <address> <name>192.0.2.1/24</name> </address> </inet> </family> </unit> </interface> </interfaces> </configuration> </config> <error-option>stop-on-error</error-option> </edit-config>""" commit = '<commit/>' unlock = '<unlock><target><candidate/></target></unlock>' payload = lock + load + commit + unlock r = requests.post('http://198.51.100.1:3000/rpc/', auth=(user, password), headers=headers, data=payload) print(r.content)
Quando você executa o script de operação, ele devolve os resultados do RPC para o bloqueio, carga, compromisso e desbloqueio das operações. Em alguns dispositivos, a saída de resposta separa as respostas individuais de RPC com linhas de limite que incluem --
uma sequência de limites e um Content-Type
cabeçalho. Outros dispositivos podem incluir apenas o Content-Type
cabeçalho.
user@host> op requests-set-interface.py Enter user password: --harqgehabymwiax Content-Type: application/xml; charset=utf-8 <ok/> --harqgehabymwiax Content-Type: application/xml; charset=utf-8 <load-success/> --harqgehabymwiax Content-Type: application/xml; charset=utf-8 <commit-results xmlns:junos="http://xml.juniper.net/junos/*/junos"> <routing-engine junos:style="normal"> <name>re0</name> <commit-success/> <commit-revision-information> <new-db-revision>re0-1555351754-53</new-db-revision> <old-db-revision>re0-1555033614-52</old-db-revision> </commit-revision-information> </routing-engine> </commit-results> --harqgehabymwiax Content-Type: application/xml; charset=utf-8 <ok/> --harqgehabymwiax--
Usando certificados em solicitações de HTTPS
O mecanismo de autenticação básica de HTTP envia credenciais de usuário como uma cadeia de texto claro codificada pela Base64. Para proteger as credenciais de autenticação contra escutas, recomendamos habilitar o serviço RESTful API em HTTPS, que criptografa a comunicação usando Segurança de Camada de Transporte (TLS) ou Camada de Soquetes Seguros (SSL). Para obter informações sobre a configuração deste serviço, consulte o Junos OS REST API Guide.
Por padrão, a biblioteca de solicitações verifica certificados SSL para solicitações de HTTPS. Você pode incluir os argumentos e cert
argumentos verify
na solicitação para controlar as opções de verificação de SSL. Para obter informações detalhadas sobre essas opções, consulte a documentação de Solicitações.
Quando você usa o Python 2.7 para executar um script que usa o requests
módulo para executar solicitações DE HTTPS, o script gera InsecurePlatformWarning
e SubjectAltNameWarning
alertas.
O script de operação a seguir envia uma solicitação get pelo HTTPS e define o verify
argumento para o caminho de arquivo de um pacote ca ou um diretório contendo certificados CA confiáveis. Os certificados ca especificados são usados para verificar o certificado do servidor.
from junos import Junos_Context import jcs import requests user = Junos_Context['user-context']['user'] password = jcs.get_secret('Enter user password: ') r = requests.get('https://198.51.100.1:3443/rpc/get-software-information', auth=(user, password), verify='path-to-ca-bundle') print (r.status_code) if (r.status_code == requests.codes.ok): print (r.text)
Para especificar um certificado local do lado do cliente, defina o cert
argumento igual ao caminho de um único arquivo contendo a chave privada e o certificado do cliente ou para um tuple contendo os caminhos do certificado de cliente individual e arquivos-chave privados.
r = requests.get('https://198.51.100.1:3443/rpc/get-software-information', auth=(user, password), verify='path-to-ca-bundle', cert=('path-to-client-cert','path-to-client-key')
Especificando a instância de roteamento
Por padrão, as solicitações são executadas usando a instância de roteamento padrão. Você também pode executar solicitações usando a mgmt_junos
instância de gerenciamento ou outra instância de roteamento não padrão. Quando você executa scripts pela infraestrutura do Junos OS, você pode especificar a instância de roteamento chamando a set_routing_instance()
função no script. Determinados dispositivos também têm suporte para especificar a instância de roteamento e executar um script na shell de nível Unix.
Em dispositivos que executam o Junos OS Evolved, a set_routing_instance()
função só oferece suporte usando a instância de roteamento de gerenciamento.
Em um script Python, para executar uma solicitação usando uma instância de roteamento não padrão, incluindo a mgmt_junos
instância:
O script de operação a seguir usa a mgmt_junos
instância de gerenciamento para se conectar ao dispositivo alvo e executar solicitações.
from junos import Junos_Context import jcs import requests user = Junos_Context['user-context']['user'] password = jcs.get_secret('Enter user password: ') jcs.set_routing_instance('mgmt_junos') r = requests.get('http://198.51.100.1:3000/rpc/get-software-information', auth=(user, password)) print (r.text)
Para obter informações sobre o uso da set_routing_instance()
função em scripts Python, consulte set_routing_instance().
Além de especificar a instância de roteamento no script, determinados dispositivos oferecem suporte para especificar a instância de roteamento e executar um script a partir do shell de nível Unix. Em dispositivos que executam o Junos OS com Automação Aprimorada (Versão FreeBSD 7.1 ou posterior), você pode usar o setfib
comando para executar solicitações com a determinada instância de roteamento, incluindo a instância de gerenciamento e outras instâncias de roteamento não padrão.
O script Python a seguir simplesmente executa o get-software-information
RPC em um dispositivo remoto e imprime a resposta:
#!/usr/bin/env python import requests from getpass import getpass user = raw_input ('Enter username: ') password = getpass('Enter password: ') r = requests.get('http://198.51.100.1:3000/rpc/get-software-information', auth=(user, password)) print (r.text)
Para usar setfib
para executar o script usando uma instância de roteamento não padrão em um dispositivo que executa o Junos OS com automação aprimorada:
Encontre o índice de software associado à tabela de roteamento, por exemplo.
No exemplo a seguir, o dispositivo está configurado para usar a instância
mgmt_junos
de gerenciamento dedicada não padrão. O índice da tabela de roteamento é mencionado na saída de comando.user@host> show route forwarding-table extensive table mgmt_junos Routing table: mgmt_junos.inet [Index 36738] Internet: Enabled protocols: Bridging, Destination: default Route type: permanent Route reference: 0 Route interface-index: 0 Multicast RPF nh index: 0 P2mpidx: 0 Flags: none Next-hop type: reject Index: 340 Reference: 1
Para executar o script de operação com a determinada instância de roteamento, use o
setfib
comando para executar o script e fazer referência ao índice. Por exemplo:user@host> start shell % setfib -F36738 python /var/db/scripts/op/request-software-info.py
No exemplo a seguir, o dispositivo está configurado com uma instância de roteamento não padrão, vr1, e o índice da tabela de roteamento vr1.inet é 8:
user@host> show route forwarding-table extensive table vr1 Routing table: vr1.inet [Index 8] Internet: Enabled protocols: Bridging, All VLANs, Destination: default Route type: permanent Route reference: 0 Route interface-index: 0 Multicast RPF nh index: 0 P2mpidx: 0 Flags: sent to PFE Next-hop type: reject Index: 592 Reference: 1
O comando a seguir executa o script de operação usando a instância de roteamento vr1:
% setfib -F8 python /var/db/scripts/op/request-software-info.py
Desempenho das operações de ZTP
O provisionamento zero touch (ZTP) permite que você provisione novos dispositivos da Juniper Networks em sua rede automaticamente, com intervenção manual mínima. Para usar o ZTP, você configura um servidor para fornecer as informações necessárias, que podem incluir uma imagem do Junos OS e um arquivo de configuração para carregar ou um script para executar. Quando você conecta fisicamente um dispositivo à rede e o inicializa com uma configuração padrão de fábrica, o dispositivo recupera as informações do servidor designado, atualiza a imagem do Junos OS conforme apropriado e executa o script ou carrega o arquivo de configuração.
Quando você conecta e inicializa um novo dispositivo de rede, se o Junos OS detectar um arquivo no servidor, a primeira linha do arquivo é examinada. Se o Junos OS encontrar os personagens #! seguido por um caminho de intérprete, ele trata o arquivo como um script e executa-o com o intérprete especificado. Você pode usar a biblioteca de solicitações em scripts executados para agilizar o processo ZTP.
Por exemplo, considere a seguinte amostra do script Python, que o novo dispositivo baixa e executa durante o processo ZTP. Quando o script é executado, ele primeiro baixa o ca_cert_remote
certificado CA do local no servidor especificado e o armazena localmente no ca_cert_local
local. O script então se conecta ao servidor de configuração na porta 8000 e emite uma solicitação GET para recuperar a nova configuração do dispositivo. A solicitação inclui o caminho até o certificado ca, que é usado para verificar o certificado do servidor durante a troca. Em seguida, o script usa a biblioteca Junos PyEZ para carregar a configuração no dispositivo e empenhá-la.
#!/usr/bin/python import os import paramiko import requests from jnpr.junos import Device from jnpr.junos.utils.config import Config # Define the servers storing the certificate and configuration host_cert = '198.51.100.1' host_config = '192.0.2.1' username = 'admin' password = 'secret123' # Define CA certificate file locations ca_cert_remote = '/u01/app/myCA/certs/rootCA.crt' ca_cert_local = '/var/tmp/rootCA.crt' # Retrieve the CA certificate from the server ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hostname=host_cert, username=username, password=password) sftp = ssh.open_sftp() sftp.get(ca_cert_remote, ca_cert_local) sftp.close() ssh.close() # Retrieve the configuration from the server uri = 'https://' + host_config + ':8000/' config = requests.get(uri, auth=(username, password), verify=ca_cert_local) # Load and commit the configuration on the device with Device() as dev: cu = Config(dev) cu.load(config.text, format='text', overwrite=True) cu.commit()