如何在运行 Junos OS 的设备上为 Python 使用请求库
运行 Junos OS、支持 Python 扩展包的某些设备上提供了 Python 请求库。您可以使用 requests
Python 脚本中的模块发送 HTTP/1.1 请求。在运行具有增强型自动化的 Junos OS 的设备上,您也可使用 requests
Python 交互模式中的模块。请求库提供其他方法,用于支持初始部署,以及在运行 Junos OS 的设备上执行例行监控和配置更改。有关该 requests
模块及其功能的信息,请参阅 http://docs.python-requests.org/ 上的“请求”文档。
发出请求
您可以使用 requests
设备上的模块 Python 脚本发送 HTTP/1.1 请求。要提出请求,请导入脚本中的模块,然后调用对应于所需请求的功能。该模块支持 HTTP GET
和 POST
请求以及 HEAD
、 DELETE
和 PUT
请求。请求返回包含服务器响应的 响应 对象。默认情况下,请求使用默认路由实例进行。
请求库可用于在运行支持 REST API 服务的 Junos OS 的设备上执行 RPC。目标设备必须在层级配置适当的语句 [edit system services rest]
,以便使用 REST 通过 HTTP 或 HTTPS 启用 Junos OS 命令。
例如,以下操作脚本执行 GET 请求,在运行 Junos OS 的 get-software-information
远程设备上执行 RPC,该设备在默认端口 (3000) 上配置了基于 HTTP 的 REST API 服务。脚本将打印响应状态代码,如果状态代码表示成功,则将打印响应内容。
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> ...
要检索标头,您可以发送一个简单的 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
如果 GET 请求需要其他参数,则可以包括 params
参数并提供字典或查询字符串中发送的元数或字节列表,或者您可以将密钥/值对作为 URL 的一部分传递。同样,您可以提供自定义标头, headers
方法是包括该论点和一本 HTTP 标头字典。
以下请求使用给定接口的 terse 选项执行 get-interface-information
RPC,并以文本格式返回响应:
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)
以下示例将参数用作 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)
要在同一请求中执行多个 RPC,请发起 HTTP POST 请求,然后设置 data
参数以引用 RPC 执行。有关执行多个 RPC 的示例,请参阅执行 操作 RPC 和管理 配置 的部分。
执行操作性 RPC
您可以使用该 requests
模块在运行已启用 REST API 服务的 Junos OS 的远程设备上从 Junos XML API 中执行 RPC。
以下操作脚本使用该 requests
模块在目标设备上执行相当于 show interfaces ge-2/0/1 terse
操作模式命令的 RPC:
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)
以下操作脚本将发送在目标设备上执行多个 RPC 的 POST 请求。该 data
参数引用执行的 RPC,在多行字符串中定义以实现可读性。
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)
您还可以创建一个通用操作脚本,供用户提供必要的变量以及脚本结构并执行请求。请考虑以下用于配置 host
rpc
rpc_args
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;
以下示例操作脚本连接到运行 Junos OS 的远程设备,该设备已在层次结构级别配置适当的语句 [edit system services rest]
,以便使用 REST 通过 HTTP 启用 Junos OS 命令。连接密码的脚本提示符,并连接到通过论证提供的 host
主机和端口。然后,脚本使用模块 requests
发送 GET 请求执行通过命令行参数提供的 RPC。
从 Junos OS 版本 21.2R1 和 Junos OS Evolved 版本 21.2R1 开始,当设备将命令行参数传递到 Python op 脚本时,它将单个连字符 (-) 前缀为单个字符的论证名称,并将两个连字符 (--) 前缀为多字符论证名称。在早期版本中,设备会将单个连字符 (-) 前缀至所有参数名称。
# 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)
执行脚本时,它会在远程设备上使用指定选项执行 RPC,然后打印对标准输出的响应。
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>
管理配置
您可以使用该 requests
模块检索或更改运行已启用 REST API 服务的 Junos OS 的设备上的配置。
以下操作脚本使用 POST 请求从候选配置中检索 [edit system]
层次结构:
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)
HTTP POST 请求还允许您在单个请求中执行多个 RPC,例如锁定、加载、提交和解锁配置。
以下示例操作脚本连接到远程设备,并在给定接口上配置地址。锁定、负载、提交和解锁操作可单独定义以实现可读性,但 RPC 会在请求中串联。
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)
执行操作脚本时,将返回 RPC 结果以执行锁定、加载、提交和解锁操作。在某些设备上,响应输出将单独的 RPC 回复与边界线分开,边界线后面包括 --
边界字符串和 Content-Type
标头。其他设备可能仅包含标 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--
在 HTTPS 请求中使用证书
HTTP 基本身份验证机制将用户凭据发送为 Base64 编码的明文字符串。为了保护认证凭据免遭窃听,我们建议通过 HTTPS 启用 RESTful API 服务,该服务使用传输层安全 (TLS) 或安全套接字层 (SSL) 对通信进行加密。有关配置此服务的信息,请参阅 Junos OS REST API 指南。
默认情况下,请求库会验证 HTTPS 请求的 SSL 证书。您可以在 verify
请求中包含控制 SSL 验证选项的和 cert
参数。有关这些选项的详细信息,请参阅 请求文档。
当您使用 Python 2.7 执行使用模块执行 HTTPS 请求的 requests
脚本时,脚本将生成 InsecurePlatformWarning
并 SubjectAltNameWarning
发出警告。
以下操作脚本将通过 HTTPS 发送 GET 请求,并将该参数设置 verify
到包含可信 CA 证书的 CA 捆绑包或目录的文件路径。指定的 CA 证书用于验证服务器的证书。
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)
要指定本地客户端证书,请将参数设置 cert
为等于包含客户端私钥和证书的单个文件的路径,或者设置为包含个别客户端证书和私钥文件路径的元件。
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')
指定路由实例
默认情况下,请求使用默认路由实例执行。您也可使用 mgmt_junos
管理实例或其他非默认路由实例执行请求。通过 Junos OS 基础架构执行脚本时,可通过调用 set_routing_instance()
脚本中的功能来指定路由实例。某些设备还支持指定路由实例并在 Unix 级外壳中执行脚本。
在运行 Junos OS Evolved 的设备上, set_routing_instance()
该功能仅支持使用管理路由实例。
在 Python 脚本中,使用非默认路由实例(包括实 mgmt_junos
例)执行请求:
以下操作脚本使用 mgmt_junos
管理实例连接到目标设备并执行请求。
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)
有关在 Python 脚本中使用 set_routing_instance()
该功能的信息,请 参阅 set_routing_instance()。
除了在脚本中指定路由实例之外,某些设备还支持指定路由实例并从 Unix 级外壳执行脚本。在使用增强型自动化(FreeBSD 版本 7.1 或更高版本)运行 Junos OS 的设备上,您可以使用 setfib
命令在给定路由实例(包括管理实例和其他非默认路由实例)中执行请求。
以下 Python 脚本只需在远程设备上执行 get-software-information
RPC 并打印响应:
#!/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)
要在 setfib
运行 Junos OS 和增强自动化的设备上使用非默认路由实例执行脚本:
查找与该实例的路由表相关联的软件索引。
在以下示例中,设备配置为使用非默认专用管理实例
mgmt_junos
。在命令输出中引用路由表索引。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
要使用给定的路由实例执行操作脚本,请使用
setfib
命令执行脚本并参考索引。例如:user@host> start shell % setfib -F36738 python /var/db/scripts/op/request-software-info.py
在以下示例中,设备配置了非默认路由实例 vr1,而 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
以下命令使用 vr1 路由实例执行操作脚本:
% setfib -F8 python /var/db/scripts/op/request-software-info.py
执行 ZTP 运维
全自动部署 (ZTP) 使您能够在网络中自动调配新的瞻博网络设备,并尽可能少地进行手动干预。要使用 ZTP,请配置一个服务器来提供所需的信息,包括要加载的 Junos OS 映像和一个配置文件或要执行的脚本。当您将设备物理连接到网络并使用出厂默认配置启动时,设备将从指定服务器检索信息,相应升级 Junos OS 映像,然后执行脚本或加载配置文件。
连接并启动新网络设备时,如果 Junos OS 检测到服务器上的文件,将检查文件的第一行。如果 Junos OS 找到字符 #!接着是翻译路径,它会将文件作为脚本处理,然后由指定的解释器执行。您可以在已执行脚本中使用“请求”库来简化 ZTP 流程。
例如,请考虑以下示例 Python 脚本,新设备会在 ZTP 过程中下载和执行此脚本。执行脚本时,将首先从 ca_cert_remote
指定服务器上的位置下载 CA 证书,并将其存储在 ca_cert_local
本地位置。然后,脚本连接到端口 8000 上的配置服务器,并发出 GET 请求检索新设备配置。请求包括到 CA 证书的路径,该路径用于在交换期间验证服务器的证书。然后,脚本使用 Junos PyEZ 库在设备上加载配置并提交。
#!/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()