Cómo usar la biblioteca de solicitudes para Python en dispositivos que ejecutan Junos OS
La biblioteca de solicitudes para Python está disponible en ciertos dispositivos que ejecutan Junos OS que admiten el paquete de extensiones de Python. Puede usar el requests
módulo en scripts de Python para enviar solicitudes HTTP/1.1. En dispositivos que ejecutan Junos OS con automatización mejorada, también puede usar el módulo en el requests
modo interactivo de Python. La biblioteca de solicitudes proporciona métodos adicionales para admitir implementaciones iniciales, así como para realizar monitoreo de rutina y cambios de configuración en dispositivos que ejecutan Junos OS. Para obtener más información sobre el requests
módulo y sus funciones, consulte la documentación de solicitudes en http://docs.python-requests.org/.
Emisión de solicitudes
Puede usar el módulo en las requests
secuencias de comandos de Python enbox para enviar solicitudes HTTP/1.1. Para realizar una solicitud, importe el módulo en el script y llame a la función correspondiente a la solicitud deseada. El módulo admite HTTP GET
y POST
solicitudes, así como HEAD
, DELETE
y PUT
solicitudes. La solicitud devuelve un objeto Response que contiene la respuesta del servidor. De forma predeterminada, las solicitudes se realizan mediante la instancia de enrutamiento predeterminada.
La biblioteca de solicitudes se puede utilizar para ejecutar RPCs en dispositivos que ejecutan Junos OS que admiten el servicio de API REST. El dispositivo de destino debe configurarse con las instrucciones adecuadas en el [edit system services rest]
nivel de jerarquía para habilitar los comandos de Junos OS a través de HTTP o HTTPS mediante REST.
Por ejemplo, la siguiente secuencia de comandos de operación realiza una solicitud GET que ejecuta la get-software-information
RPC en un dispositivo remoto que ejecuta Junos OS que tiene el servicio de API REST a través de HTTP configurado en el puerto predeterminado (3000). La secuencia de comandos imprime el código de estado de respuesta y, si el código de estado indica el éxito, imprime el contenido de la respuesta.
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 solo los encabezados, puede enviar una solicitud simple 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
Si una solicitud GET requiere parámetros adicionales, puede incluir el params
argumento y proporcionar un diccionario o una lista de tuplas o bytes para enviar en la cadena de consulta, o puede pasar pares de clave/valor como parte de la DIRECCIÓN URL. De forma similar, puede proporcionar encabezados personalizados incluyendo el headers
argumento y un diccionario de encabezados HTTP.
La siguiente solicitud ejecuta el get-interface-information
RPC con la opción terse para la interfaz dada y devuelve la respuesta en 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)
En el ejemplo siguiente, se suministran los argumentos como pares clave/valor en la DIRECCIÓN 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 ejecutar varias RPCs en la misma solicitud, inicie una solicitud HTTP POST y establezca el data
parámetro para hacer referencia a las RPCs que se ejecutarán. Consulte secciones Ejecución de RPCs operativas y Administración de la configuración para ver ejemplos que ejecutan varias RPCs.
Ejecución de RPCs operativas
Puede usar el requests
módulo para ejecutar RPCs desde la API XML de Junos en un dispositivo remoto que ejecute Junos OS que tenga habilitado el servicio de API REST.
El siguiente script operativo utiliza el requests
módulo para ejecutar el equivalente RPC del show interfaces ge-2/0/1 terse
comando del modo operativo en el dispositivo de destino:
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)
El siguiente script operativo envía una solicitud POST que ejecuta varias RPCs en el dispositivo de destino. El data
parámetro hace referencia a las RPCs que se van a ejecutar, las cuales se definen en una cadena multilínea para una legibilidad.
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)
También puede crear una secuencia de comandos op genérica para la cual el usuario proporciona las variables necesarias y la secuencia de comandos construye y ejecuta la solicitud. Considere la siguiente configuración de script operativo, que configura los host
argumentos de línea de comandos , rpc
y rpc_args
de comandos para la secuencia de comandos requests-rpc.py op:
[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;
La siguiente secuencia de comandos de operación de ejemplo se conecta a un dispositivo remoto que ejecuta Junos OS, que se configuró con las instrucciones adecuadas en el [edit system services rest]
nivel de jerarquía para habilitar los comandos de Junos OS a través de HTTP mediante REST. El script solicita la contraseña de conexión y se conecta al host y al puerto proporcionados mediante el host
argumento. A continuación, la secuencia de comandos usa el requests
módulo para enviar una solicitud GET que ejecuta la RPC proporcionada a través de los argumentos de línea de comandos.
A partir de Junos OS versión 21.2R1 y Junos OS Evolved versión 21.2R1, cuando el dispositivo pasa los argumentos de línea de comandos a una secuencia de comandos op de Python, antepone un solo guión (-) a nombres de argumentos de un solo carácter y prefijos de dos guiones (--) a nombres de argumentos de varios caracteres. En versiones anteriores, los dispositivos antepongen un solo guión (-) a todos los nombres de argumento.
# 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)
Cuando ejecuta la secuencia de comandos, ejecuta la RPC con las opciones especificadas en el dispositivo remoto e imprime la respuesta a la salida estándar.
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>
Administración de la configuración
Puede usar el requests
módulo para recuperar o cambiar la configuración en un dispositivo que ejecute Junos OS que tenga habilitado el servicio de API REST.
La siguiente secuencia de comandos op recupera la [edit system]
jerarquía de la configuración candidata mediante una solicitud 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)
Las solicitudes HTTP POST también le permiten ejecutar varias RPCs en una sola solicitud, por ejemplo, para bloquear, cargar, confirmar y desbloquear una configuración.
La siguiente secuencia de comandos de operación de ejemplo se conecta al dispositivo remoto y configura una dirección en la interfaz dada. Las operaciones de bloqueo, carga, confirmación y desbloqueo se definen por separado para la legibilidad, pero las RPCs se concatenan en la solicitud.
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)
Cuando se ejecuta la secuencia de comandos op, devuelve los resultados rpc para las operaciones de bloqueo, carga, confirmación y desbloqueo. En algunos dispositivos, la salida de respuesta separa las respuestas RPC individuales con líneas de límite que incluyen --
seguidas de una cadena de límite y un Content-Type
encabezado. Otros dispositivos pueden incluir solo el Content-Type
encabezado.
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--
Uso de certificados en solicitudes HTTPS
El mecanismo de autenticación básica HTTP envía las credenciales de usuario como una cadena de texto sin cifrar codificada en Base64. Para proteger las credenciales de autenticación de las escuchas, recomendamos habilitar el servicio de API RESTful a través de HTTPS, que cifra la comunicación mediante la seguridad de capa de transporte (TLS) o la capa de sockets seguros (SSL). Para obtener más información acerca de cómo configurar este servicio, consulte la Guía de la API de REST de Junos OS.
De forma predeterminada, la biblioteca de solicitudes verifica los certificados SSL para las solicitudes HTTPS. Puede incluir los verify
argumentos y cert
en la solicitud para controlar las opciones de verificación SSL. Para obtener información detallada acerca de estas opciones, consulte la documentación de solicitudes.
Cuando usa Python 2.7 para ejecutar una secuencia de comandos que usa el requests
módulo para ejecutar solicitudes HTTPS, el script genera InsecurePlatformWarning
y SubjectAltNameWarning
advierte.
La siguiente secuencia de comandos op envía una solicitud GET a través de HTTPS y establece el argumento en la verify
ruta de archivo de un paquete de CA o un directorio que contiene certificados de CA de confianza. Los certificados de CA especificados se utilizan para comprobar el certificado del 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 un certificado del lado del cliente local, establezca el cert
argumento igual a la ruta de un solo archivo que contenga la clave y el certificado privados del cliente o a una tupla que contenga las rutas del certificado de cliente individual y los archivos de clave privada.
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')
Especificación de la instancia de enrutamiento
De forma predeterminada, las solicitudes se ejecutan mediante la instancia de enrutamiento predeterminada. También puede ejecutar solicitudes mediante la mgmt_junos
instancia de administración u otra instancia de enrutamiento no predeterminada. Cuando ejecuta scripts a través de la infraestructura de Junos OS, puede especificar la instancia de enrutamiento llamando a la set_routing_instance()
función en el script. Algunos dispositivos también admiten la especificación de la instancia de enrutamiento y la ejecución de una secuencia de comandos en el shell de nivel unix.
En dispositivos que ejecutan Junos OS Evolved, la set_routing_instance()
función solo admite el uso de la instancia de enrutamiento de administración.
En una secuencia de comandos de Python, para ejecutar una solicitud mediante una instancia de enrutamiento no predeterminada, incluida la mgmt_junos
instancia:
La siguiente secuencia de comandos op usa la mgmt_junos
instancia de administración para conectarse al dispositivo de destino y ejecutar solicitudes.
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 obtener más información sobre el uso de la set_routing_instance()
función en scripts de Python, consulte set_routing_instance().
Además de especificar la instancia de enrutamiento en la secuencia de comandos, ciertos dispositivos admiten la especificación de la instancia de enrutamiento y la ejecución de una secuencia de comandos desde el shell de nivel unix. En dispositivos que ejecutan Junos OS con automatización mejorada (FreeBSD versión 7.1 o posterior), puede usar el setfib
comando para ejecutar solicitudes con la instancia de enrutamiento dada, incluida la instancia de administración y otras instancias de enrutamiento no predeterminadas.
La siguiente secuencia de comandos de Python simplemente ejecuta el get-software-information
RPC en un dispositivo remoto e imprime la respuesta:
#!/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 ejecutar la secuencia de comandos mediante una instancia de enrutamiento no predeterminada en un dispositivo que ejecuta Junos OS con automatización mejorada:
Encuentre el índice de software asociado con la tabla de enrutamiento para esa instancia.
En el ejemplo siguiente, el dispositivo está configurado para usar la instancia
mgmt_junos
de administración dedicada no predeterminada. Se hace referencia al índice de la tabla de enrutamiento en el resultado del 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 ejecutar la secuencia de comandos op con la instancia de enrutamiento dada, utilice el
setfib
comando para ejecutar la secuencia de comandos y hacer referencia al índice. Por ejemplo:user@host> start shell % setfib -F36738 python /var/db/scripts/op/request-software-info.py
En el ejemplo siguiente, el dispositivo está configurado con una instancia de enrutamiento no predeterminada, vr1, y el índice de tabla de enrutamiento vr1.inet es 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
El siguiente comando ejecuta la secuencia de comandos op mediante la instancia de enrutamiento vr1:
% setfib -F8 python /var/db/scripts/op/request-software-info.py
Realización de operaciones ZTP
El aprovisionamiento sin intervención (ZTP) le permite aprovisionar nuevos dispositivos de Juniper Networks de forma automática, con una intervención manual mínima. Para usar ZTP, configure un servidor para proporcionar la información necesaria, que puede incluir una imagen de Junos OS y un archivo de configuración que se va a cargar o una secuencia de comandos que se va a ejecutar. Cuando conecta físicamente un dispositivo a la red y lo inicia con una configuración predeterminada de fábrica, el dispositivo recupera la información del servidor designado, actualiza la imagen de Junos OS según corresponda y ejecuta la secuencia de comandos o carga el archivo de configuración.
Cuando se conecta y arranca un nuevo dispositivo de red, si Junos OS detecta un archivo en el servidor, se examina la primera línea del archivo. Si Junos OS encuentra los caracteres #! seguido de una ruta de intérprete, trata el archivo como una secuencia de comandos y lo ejecuta con el intérprete especificado. Puede usar la biblioteca solicitudes en scripts ejecutados para optimizar el proceso de ZTP.
Por ejemplo, considere la siguiente secuencia de comandos de Python de ejemplo, que el nuevo dispositivo descarga y ejecuta durante el proceso ZTP. Cuando se ejecuta la secuencia de comandos, primero descarga el certificado de CA de la ca_cert_remote
ubicación en el servidor especificado y lo almacena localmente en la ca_cert_local
ubicación. A continuación, la secuencia de comandos se conecta al servidor de configuración en el puerto 8000 y emite una solicitud GET para recuperar la nueva configuración del dispositivo. La solicitud incluye la ruta al certificado de CA, que se usa para comprobar el certificado del servidor durante el intercambio. A continuación, la secuencia de comandos usa la biblioteca Junos PyEZ para cargar la configuración en el dispositivo y confirmarla.
#!/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()