Comment utiliser la bibliothèque de requêtes pour Python sur les équipements exécutant Junos OS
La bibliothèque de requêtes pour Python est disponible sur certains équipements exécutant Junos OS qui prennent en charge le package d’extensions Python. Vous pouvez utiliser le module dans les requests
scripts Python pour envoyer des requêtes HTTP/1.1. Sur les équipements exécutant Junos OS avec automatisation améliorée, vous pouvez également utiliser le requests
module en mode interactif Python. La bibliothèque de requêtes fournit des méthodes supplémentaires pour prendre en charge les déploiements initiaux ainsi que pour effectuer une surveillance de routine et des modifications de configuration sur les équipements exécutant Junos OS. Pour plus d’informations sur le requests
module et ses fonctions, consultez la documentation sur les demandes sur http://docs.python-requests.org/.
Émission de demandes
Vous pouvez utiliser le module dans la requests
boîte à scripts Python pour envoyer des requêtes HTTP/1.1. Pour effectuer une demande, importez le module dans votre script et appelez la fonction correspondant à la demande souhaitée. Le module prend en charge les requêtes ET GET
HTTP POST
, ainsi que HEAD
, DELETE
et les PUT
requêtes. La requête renvoie un objet Response contenant la réponse du serveur. Par défaut, les requêtes sont faites à l’aide de l’instance de routage par défaut.
La bibliothèque de requêtes peut être utilisée pour exécuter des RPC sur des équipements exécutant Junos OS qui prennent en charge le service d’API REST. L’équipement cible doit être configuré avec les instructions appropriées au niveau hiérarchique [edit system services rest]
pour activer les commandes Junos OS sur HTTP ou HTTPS à l’aide de REST.
Par exemple, le script op suivant exécute une demande GET qui exécute le get-software-information
RPC sur un équipement distant exécutant Junos OS sur lequel le service API REST sur HTTP est configuré sur le port par défaut (3000). Le script imprime le code d’état de la réponse, et si le code d’état indique le succès, il imprime le contenu de la réponse.
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> ...
Pour récupérer uniquement les en-têtes, vous pouvez envoyer une simple HEAD
demande.
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 une requête GET nécessite des paramètres supplémentaires, vous pouvez soit inclure l’argument params
et fournir un dictionnaire ou une liste de tuples ou d’octets à envoyer dans la chaîne de requête, ou vous pouvez passer des paires clé/valeur dans le cadre de l’URL. De même, vous pouvez fournir des en-têtes personnalisés en incluant l’argument headers
et un dictionnaire d’en-têtes HTTP.
La requête suivante exécute le get-interface-information
RPC avec l’option terse pour l’interface donnée et renvoie la réponse au format texte :
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)
L’exemple suivant fournit les arguments sous forme de paires clé/valeur dans l’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)
Pour exécuter plusieurs RPC dans la même demande, lancez une demande HTTP POST et définissez le data
paramètre pour référencer les RPC à exécuter. Consultez les sections Exécution de PPC opérationnels et Gestion de la configuration pour obtenir des exemples d’exécution de plusieurs RPC.
Exécution de RPCs opérationnels
Vous pouvez utiliser le requests
module pour exécuter des RPC à partir de l’API XML Junos sur un équipement distant exécutant Junos OS sur lequel le service API REST est activé.
Le script opérationnel suivant utilise le requests
module pour exécuter l’équivalent RPC de la show interfaces ge-2/0/1 terse
commande du mode opérationnel sur l’équipement cible :
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)
Le script op suivant envoie une requête POST qui exécute plusieurs RPC sur l’équipement cible. Le data
paramètre fait référence aux RPC à exécuter, qui sont définis dans une chaîne multi-ligne pour plus de lisibilité.
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)
Vous pouvez également créer un script op générique pour lequel l’utilisateur fournit les variables nécessaires et les constructions de script et exécute la requête. Prenons l’exemple de la configuration de script op suivante, qui configure le host
, rpc
et rpc_args
les arguments de ligne de commande pour le script op 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;
L’exemple de script op suivant se connecte à un équipement distant exécutant Junos OS, qui a été configuré avec les instructions appropriées au [edit system services rest]
niveau hiérarchique pour activer les commandes Junos OS sur HTTP à l’aide de REST. Le script invite le mot de passe de connexion et se connecte à l’hôte et au port fournis par l’argument host
. Le script utilise ensuite le requests
module pour envoyer une requête GET exécutant le RPC fourni via les arguments de la ligne de commande.
À partir de La version 21.2R1 de Junos OS et de la version 21.2R1 de Junos OS Evolved, lorsque l’équipement transmet des arguments de ligne de commande à un script Python op, il préfixe un seul trait d’union (-) vers des noms d’arguments à caractère unique et préfixe deux traits d’union (--) à des noms d’arguments multi-caractères. Dans les versions précédentes, les équipements préfixent un seul trait d’union (-) à tous les noms d’arguments.
# 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)
Lorsque vous exécutez le script, il exécute le RPC avec les options spécifiées sur l’équipement distant et imprime la réponse au résultat standard.
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>
Gestion de la configuration
Vous pouvez utiliser le requests
module pour récupérer ou modifier la configuration sur un équipement exécutant Junos OS sur lequel le service API REST est activé.
Le script op suivant récupère la hiérarchie de la [edit system]
configuration du candidat à l’aide d’une demande 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)
Les requêtes HTTP POST vous permettent également d’exécuter plusieurs RPC dans une seule demande, par exemple, pour verrouiller, charger, valider et déverrouiller une configuration.
L’exemple de script op suivant se connecte à l’équipement distant et configure une adresse sur l’interface donnée. Les opérations de verrouillage, de chargement, de validation et de déverrouillage sont définies séparément pour la lisibilité, mais les RPC sont concaténés dans la requête.
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)
Lorsque vous exécutez le script op, il renvoie les résultats RPC pour les opérations de verrouillage, de chargement, de validation et de déverrouillage. Sur certains équipements, la sortie de réponse sépare les réponses RPC individuelles par des lignes de limites qui comprennent --
une chaîne de limites et un Content-Type
en-tête. D’autres équipements peuvent inclure uniquement l’en-tête Content-Type
.
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--
Utilisation de certificats dans les requêtes HTTPS
Le mécanisme d’authentification de base HTTP envoie les informations d’identification de l’utilisateur sous la forme d’une chaîne de texte en clair codée en Base64. Pour protéger les informations d’authentification contre les écoutes, nous vous recommandons d’activer le service d’API RESTful sur HTTPS, qui chiffre la communication à l’aide de TLS (Transport Layer Security) ou de Secure Sockets Layer (SSL). Pour plus d’informations sur la configuration de ce service, consultez le Guide de l’API REST de Junos OS.
Par défaut, la bibliothèque de demandes vérifie les certificats SSL pour les demandes HTTPS. Vous pouvez inclure les verify
arguments et cert
dans la demande pour contrôler les options de vérification SSL. Pour obtenir des informations détaillées sur ces options, consultez la documentation sur les demandes.
Lorsque vous utilisez Python 2.7 pour exécuter un script qui utilise le requests
module pour exécuter des requêtes HTTPS, le script génère des SubjectAltNameWarning
avertissementsInsecurePlatformWarning
.
Le script op suivant envoie une requête GET sur HTTPS et définit l’argument sur le verify
chemin de fichier d’une offre ca ou d’un répertoire contenant des certificats d’autorité de certification de confiance. Les certificats d’autorité de certification spécifiés sont utilisés pour vérifier le certificat du serveur.
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)
Pour spécifier un certificat local côté client, définissez l’argument cert
égal au chemin d’un seul fichier contenant la clé et le certificat privés du client ou à un tuple contenant les chemins du certificat client individuel et des fichiers de clé privée.
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')
Spécification de l’instance de routage
Par défaut, les requêtes sont exécutées à l’aide de l’instance de routage par défaut. Vous pouvez également exécuter des requêtes à l’aide de l’instance mgmt_junos
de gestion ou d’une autre instance de routage non par défaut. Lorsque vous exécutez des scripts via l’infrastructure Junos OS, vous pouvez spécifier l’instance de routage en appelant la set_routing_instance()
fonction du script. Certains équipements prennent également en charge la spécification de l’instance de routage et l’exécution d’un script dans le shell de niveau Unix.
Sur les équipements exécutant Junos OS Evolved, la set_routing_instance()
fonction ne prend en charge que l’instance de routage de gestion.
Dans un script Python, pour exécuter une requête à l’aide d’une instance de routage non par défaut, y compris l’instance mgmt_junos
:
Le script op suivant utilise l’instance mgmt_junos
de gestion pour se connecter à l’équipement cible et exécuter des requêtes.
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)
Pour plus d’informations sur l’utilisation de la set_routing_instance()
fonction dans les scripts Python, voir set_routing_instance().
En plus de spécifier l’instance de routage dans le script, certains équipements prennent en charge la spécification de l’instance de routage et l’exécution d’un script à partir du shell de niveau Unix. Sur les équipements exécutant Junos OS avec automatisation améliorée (FreeBSD version 7.1 ou ultérieure), vous pouvez utiliser la setfib
commande pour exécuter des requêtes avec l’instance de routage donnée, y compris l’instance de gestion et d’autres instances de routage non par défaut.
Le script Python suivant exécute simplement le get-software-information
RPC sur un équipement distant et imprime la réponse :
#!/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)
Pour exécuter setfib
le script à l’aide d’une instance de routage non par défaut sur un équipement exécutant Junos OS avec automatisation améliorée :
Trouvez l’index logiciel associé à la table de routage de cette instance.
Dans l’exemple suivant, l’équipement est configuré pour utiliser l’instance
mgmt_junos
de gestion dédiée non par défaut . L’index de la table de routage est référencé dans la sortie de commande.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
Pour exécuter le script op avec l’instance de routage donnée, utilisez la
setfib
commande pour exécuter le script et référencer l’index. Par exemple :user@host> start shell % setfib -F36738 python /var/db/scripts/op/request-software-info.py
Dans l’exemple suivant, l’équipement est configuré avec une instance de routage non par défaut, vr1, et l’index de la table de routage vr1.inet est 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
La commande suivante exécute le script op à l’aide de l’instance de routage vr1 :
% setfib -F8 python /var/db/scripts/op/request-software-info.py
Exécution des opérations ZTP
Le provisionnement sans intervention (ZTP) vous permet de provisionner automatiquement de nouveaux équipements Juniper Networks sur votre réseau, avec un minimum d’intervention manuelle. Pour utiliser ZTP, vous configurez un serveur pour fournir les informations requises, qui peuvent inclure une image Junos OS et un fichier de configuration à charger ou un script à exécuter. Lorsque vous connectez physiquement un équipement au réseau et que vous le démarrez avec une configuration d’usine par défaut, l’équipement récupère les informations du serveur désigné, met à niveau l’image de Junos OS selon le cas et exécute le script ou charge le fichier de configuration.
Lorsque vous connectez et démarrez un nouvel équipement réseau, si Junos OS détecte un fichier sur le serveur, la première ligne du fichier est examinée. Si Junos OS trouve les caractères #! suivi d’un chemin d’interprétation, il traite le fichier comme un script et l’exécute avec l’interpréteur spécifié. Vous pouvez utiliser la bibliothèque de requêtes dans des scripts exécutés pour rationaliser le processus ZTP.
Prenons par exemple l’exemple de script Python suivant, que le nouvel équipement télécharge et exécute pendant le processus ZTP. Lorsque le script s’exécute, il télécharge d’abord le certificat d’autorité de certification à partir de l’emplacement ca_cert_remote
sur le serveur spécifié et le stocke localement dans l’emplacement ca_cert_local
. Le script se connecte ensuite au serveur de configuration sur le port 8000 et lance une demande GET pour récupérer la nouvelle configuration de l’équipement. La demande inclut le chemin vers le certificat d’autorité de certification, qui est utilisé pour vérifier le certificat du serveur pendant l’échange. Le script utilise ensuite la bibliothèque Junos PyEZ pour charger la configuration sur l’équipement et la valider.
#!/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()