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, DELETEe 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 hostlinha de comando erpc_args, rpcpara 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_junosde 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: 1Para executar o script de operação com a determinada instância de roteamento, use o
setfibcomando 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: 1O 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()