NVUE API
当您升级到 Cumulus Linux 5.6 或更高版本时,交换机将覆盖您在 Cumulus Linux 5.5 或更早版本中通过编辑文件执行的任何手动配置,例如配置侦听地址、端口、TLS 或证书。
除了 CLI 之外,NVUE 还支持 REST API。除了使用 SSH 访问 Cumulus Linux 之外,您还可以使用 HTTP 客户端(例如 cURL 或 Web 浏览器)与交换机进行交互。
nvued
服务提供对 NVUE REST API 的访问。Cumulus Linux 在内部公开 HTTP 端点,这使得 NVUE REST API 可以在 Cumulus Linux 交换机本地访问。NVUE CLI 也使用内部 API 与 nvued
服务通信。为了提供对 NVUE REST API 的外部访问,Cumulus Linux 使用 HTTP 反向代理服务器,并支持来自外部 REST API 客户端的 HTTPS 和 TLS 连接。
下图显示了 NVUE REST API 架构,并说明了 Cumulus Linux 如何在内部转发请求。

支持的 HTTP 方法
NVUE REST API 支持以下方法
- GET 方法显示配置和操作数据,并且等效于
nv show
命令。 - POST 方法创建和提交操作。您通常将此方法用于
nv action
命令以及用于创建修订版本的nv config
命令。 - PATCH 方法替换或取消设置配置。您将此方法用于
nv set
和nv config apply
命令。您可以执行以下任一操作- 目标配置补丁以进行配置更改,您可以在其中运行针对特定 OpenAPI 端点 URI 的特定 NVUE REST API。根据 NVUE 架构定义,您需要将 PATCH REST API 请求定向到特定端点(例如,
/nvue_v1/vrf/<vrf-id>/router/bgp
),并提供符合架构的有效负载。使用目标配置补丁,您可以控制各个资源。 - 根补丁,您可以在其中在架构的根节点上运行 NVUE PATCH API,以便单个 PATCH 操作可以在单个有效负载中更改一个、某些或整个配置。PATCH 方法的有效负载必须了解整个 NVUE 对象模型架构,因为您相对于根节点
/nvue_v1
进行配置更改。您通常执行根补丁以批量将所有配置推送到交换机;例如,如果您使用 SDN 控制器或网络管理系统,则每次需要进行更改时都推送整个交换机配置,无论更改大小。根补丁还可以通过更少的往返交换机次数进行配置更改。 - PATCH 请求中的输入有效负载可以具有同一资源的
set
或unset
json 对象,但不能同时具有两者。API 执行set
和unset
对象的顺序是不确定的且不受支持。
- 目标配置补丁以进行配置更改,您可以在其中运行针对特定 OpenAPI 端点 URI 的特定 NVUE REST API。根据 NVUE 架构定义,您需要将 PATCH REST API 请求定向到特定端点(例如,
- DELETE 方法删除配置,并且等效于
nv unset
命令。
在 Cumulus Linux 5.9 及更早版本中,REST API PATCH 响应返回 NVUE 系统的完整状态(您的配置更改以及交换机上的所有其他 NVUE 配置),这对于大规模配置来说可能效率低下,因为系统状态会随着配置的增长而增长。在 Cumulus Linux 5.10 及更高版本中,REST API PATCH 响应仅返回生成的配置更改。响应通常等于请求有效负载;但是,在某些情况下,响应会返回 NVUE 服务器自动修补的其他更改。例如,当使用像 swp1 这样的命名良好的接口名称时,NVUE 会自动配置 type
PATCH request: {'interface': {'swp1': {}}
PATCH Response: {'interface': {'swp1': {'type': 'swp'}},
...
在 Cumulus Linux 5.10 及更高版本中,DELETE 响应返回 204(No Content)
状态代码。在 Cumulus Linux 5.9 及更早版本中,DELETE 响应返回 200
和一个空的 json 正文 ({}
)。
保护 API 安全
NVUE REST API 支持 HTTP 基本身份验证,以及 NVUE CLI 支持的相同的用户名和密码的底层身份验证方法。用户帐户在 API 和 CLI 上的工作方式相同。
证书
Cumulus Linux 包含自签名证书和私钥,以便在服务器上使用,从而使其开箱即可用。交换机在首次启动时生成自签名证书和私钥。带有公钥的 X.509 证书位于 /etc/ssl/certs/cumulus.pem
中,相应的私钥位于 /etc/ssl/private/cumulus.key
中。
NVIDIA 建议您使用自己的证书和密钥。有关生成自签名证书和密钥的步骤,请参阅 Ubuntu 证书和安全文档。
Cumulus Linux 允许您管理 CA 证书(例如 DigiCert 或 Verisign)和实体(端点)证书。CA 证书和实体证书都可以包含证书链。
您可以将证书导入到交换机上(从外部源获取证书)、设置要用于 NVUE REST API 的证书,并显示有关证书的信息,例如序列号以及证书有效的日期和时间。
导入证书
- 您最多可以导入 25 个实体证书和最多 25 个 CA 捆绑包。每个 CA 捆绑包文件最多支持 100 个 CA 证书。
- 您导入的证书包含敏感的私钥信息。NVIDIA 建议您使用安全传输,例如 SFTP、SCP 或 HTTPS。
- 要导入实体证书,请运行
nv action import system security certificate <cert-id>
命令。 - 要导入 CA 证书捆绑包文件,请运行
nv action import system security ca-certificate <cert-id>
命令。
如果证书受密码保护,则需要包含密码。
您必须提供证书 ID (<cert-id>
) 以唯一标识您导入的证书。
以下示例导入带有公钥的 CA 证书捆绑包,并将证书称为 tls-cert-1
。证书受密码 mypassphrase
保护。公钥是 Base64 ASCII 编码的 PEM 字符串。
- 您必须使用三个双引号 (
"""<public-key>"""
) 将公钥括在 NVUE 命令中。 - 使用 REST API 时,您必须使用一个双引号 (
"<public-key>"
) 将公钥括起来。
cumulus@switch:~$ nv action import system security ca-certificate tls-cert-1 passphrase mypassphrase data """<public-key>"""
以下示例导入实体证书,并将证书称为 tls-cert-1
。证书受密码 mypassphrase
保护。
证书捆绑包必须为 .PFX 或 .P12 格式。
cumulus@switch:~$ nv action import system security certificate tls-cert-1 passphrase mypassphrase uri-bundle scp://user@pass:1.2.3.4:/opt/certs/cert.p12
以下示例导入带有公钥 URI scp://user@pass:1.2.3.4
和私钥 URI scp://user@pass:1.2.3.4
的实体证书,并将证书称为 tls-cert-1
。证书不受密码保护。
CA 证书必须为 .pem、.p7a 或 .p7c 格式。
cumulus@switch:~$ nv action import system security certificate tls-cert-1 uri-public-key scp://user@pass:1.2.3.4 uri-private-key scp://user@pass:1.2.3.4
设置要使用的证书
您可以配置 NVUE REST API 以使用特定证书。
以下示例配置 API 以使用名为 tls-cert-1
的证书或 CA 捆绑包
cumulus@switch:~$ nv set system api certificate tls-cert-1
cumulus@switch:~$ nv config apply
以下示例配置 API 以使用自签名证书
cumulus@switch:~$ nv set system api certificate self-signed
cumulus@switch:~$ nv config apply
要取消设置要与 NVUE REST API 一起使用的证书
cumulus@switch:~$ nv unset system api certificate tls-cert-1
要配置用于 mTLS 的相互身份验证的证书
cumulus@switch:~$ nv set system api mtls ca-certificate tls-cert-1
删除证书
- 要删除实体证书和交换机上存储的密钥数据,请运行
nv action delete system security certificate <cert-id>
命令。 - 要删除 CA 证书和交换机上存储的密钥数据,请运行
nv action delete system security ca-certificate <cert-id>
命令。
以下命令删除证书 tls-cert-1
cumulus@switch:~$ nv action delete system security certificate tls-cert-1
显示证书信息
- 要显示交换机上的所有实体证书,请运行
nv show system security certificate
命令。 - 要显示交换机上的所有 CA 证书,请运行
nv show system security ca-certificate
命令。
以下示例显示交换机上的所有实体证书
cumulus@switch:~$ nv show system security certificate
- 要显示正在使用特定实体证书的应用程序,请运行
nv show system security certificate <cert-id> installed
命令。 - 要显示正在使用特定 CA 证书的应用程序,请运行
nv show system security ca-certificate <cert-id> installed
命令。
以下示例显示正在使用特定实体证书的应用程序。
cumulus@switch:~$ nv show system security certificate tls-cert-1 installed
- 要显示有关特定实体证书的详细信息,请运行
nv show system security certificate <cert-id> dump
命令。 - 要显示有关特定 CA 证书的详细信息,请运行
nv show system security ca-certificate <cert-id> dump
命令。
以下示例显示有关 CA 证书 tls-cert-1
的详细信息
cumulus@switch:~$ nv show system security ca-certificate tls-cert-1 dump
仅 API 用户
要创建没有 SSH 权限的仅 API 用户,请使用 Linux 组权限。您可以在 ZTP 脚本中创建仅 API 用户。
# Create the dedicated automation user
adduser --disabled-password --gecos "Automation User,,,," --shell /usr/bin/nologin automation
# Set the password
echo 'automation:password!' | chpasswd
# Add the user to nvapply group to make NVUE config changes
adduser automation nvapply
控制平面 ACL
您可以通过配置以下内容来保护 API 安全
- 侦听地址;请参阅下面的API 端口和侦听地址。
- 控制平面 ACL;请参阅以下示例。
此示例显示了如何创建 ACL 以允许来自管理子网和本地交换机的用户使用 REST API 与交换机通信,并限制所有其他访问。
cumulus@switch:~$ nv set acl API-PROTECT type ipv4
cumulus@switch:~$ nv set acl API-PROTECT rule 10 action permit
cumulus@switch:~$ nv set acl API-PROTECT rule 10 match ip .protocol tcp .dest-port 8765 .source-ip 192.168.200.0/24
cumulus@switch:~$ nv set acl API-PROTECT rule 10 remark "Allow the Management Subnet to talk to API"
cumulus@switch:~$ nv set acl API-PROTECT rule 20 action permit
cumulus@switch:~$ nv set acl API-PROTECT rule 20 match ip .protocol tcp .dest-port 8765 .source-ip 127.0.0.1
cumulus@switch:~$ nv set acl API-PROTECT rule 20 remark "Allow the local switch to talk to the API"
cumulus@switch:~$ nv set acl API-PROTECT rule 30 action deny
cumulus@switch:~$ nv set acl API-PROTECT rule 30 match ip .protocol tcp .dest-port 8765
cumulus@switch:~$ nv set acl API-PROTECT rule 30 remark "Block everyone else from talking to the API"
cumulus@switch:~$ nv set system control-plane acl API-PROTECT inbound
支持的对象
NVUE 对象模型支持 Cumulus Linux 交换机上的大多数功能。以下列表显示了支持的对象。NVUE API 在这些对象中的每个对象内支持更多对象。要查看受支持的 API 端点的完整列表,请参阅 Cumulus Linux 的 NVUE OpenAPI 规范。
高级对象 | 描述 |
---|---|
acl | 访问控制列表。 |
bridge | 桥域配置。 |
evpn | EVPN 配置。 |
interface | 接口配置。 |
mlag | MLAG 配置。 |
nve | 网络虚拟化配置,例如 VXLAN 特定 MLAG 配置和 VXLAN 洪泛。 |
platform | 平台配置,例如硬件和软件组件。 |
qos | QoS RoCE 配置。 |
router | 路由器配置,例如路由器策略、全局 BGP 和 OSPF 配置、PBR、PIM、IGMP、VRR 和 VRRP 配置。 |
service | DHCP 中继和服务器、NTP、PTP、LLDP 和 syslog 配置。 |
system | 全局系统设置,例如 PBR 的保留路由表范围和第 3 层 VNI 的保留 VLAN 范围、系统登录消息和交换机重启历史记录。 |
vrf | VRF 配置。 |
使用 API
NVUE CLI 和 REST API 在功能上是等效的;您可以从 REST API 或 CLI 运行所有管理操作。NVUE 对象模型驱动 REST API 和 CLI 管理操作。所有操作都是一致的;例如,CLI nv show commands
反映您通过 REST API 运行的任何 PATCH 操作(创建和更新)。
NVUE 遵循声明式模型,删除上下文特定的命令和设置。NVUE 的结构就像一棵大树,代表 Cumulus Linux 实例的整个状态。树的底部是代表对象的高级分支,例如路由器和接口。在每个分支下都有更多分支。当您浏览树时,您会获得更具体的上下文。在树的叶子处是实际属性,表示为键值对。通过树的路径类似于文件系统路径。
默认情况下,Cumulus Linux 启用 NVUE REST API。要禁用 NVUE REST API,请运行 nv set system api state disabled
命令。
要在 Cumulus Linux 5.6 及更高版本中使用 NVUE REST API,您必须更改 cumulus 用户的密码;否则,当您运行命令时,您会看到 403 响应。
API 端口和侦听地址
本节介绍如何
- 设置 NVUE REST API 端口。如果您未设置端口,则 Cumulus Linux 使用默认端口 8765。
- 指定 NVUE REST API 侦听地址;您可以指定 IPv4 地址、IPv6 地址或
localhost
。如果您未指定侦听地址,则 NGINX 将侦听目标端口的所有地址。
以下示例将端口设置为 8888
cumulus@switch:~$ nv set system api port 8888
cumulus@switch:~$ nv config apply
您可以通过指定不同的侦听地址来侦听多个接口
cumulus@switch:~$ nv set system api listening-address 10.10.10.1
cumulus@switch:~$ nv set system api listening-address 10.10.20.1
cumulus@switch:~$ nv config apply
以下示例配置 eth0 上的侦听地址,eth0 具有 IP 地址 172.0.24.0,并且默认使用管理 VRF
cumulus@switch:~$ nv set system api listening-address 172.0.24.0
cumulus@switch:~$ nv config apply
以下示例在 swp1 上配置 VRF BLUE,swp1 具有 IP 地址 10.10.20.1,然后将 API 侦听地址设置为 swp1 的 IP 地址(为 VRF BLUE 配置)。
cumulus@switch:~$ nv set interface swp1 ip address 10.10.10.1/24
cumulus@switch:~$ nv set interface swp1 ip vrf BLUE
cumulus@switch:~$ nv config apply
cumulus@switch:~$ nv set system api listening-address 10.10.10.1
cumulus@switch:~$ nv config apply
以下示例将端口设置为 8888
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k --request PATCH https://127.0.0.1:8765/nvue_v1/system/api?rev=2 -H 'Content-Type:application/json' -d '{"port": 8888 }'
您可以通过指定不同的侦听地址来侦听多个接口。以下示例将 localhost、接口地址 10.10.10.1 和 10.10.20.1 设置为 listen-addresses。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k --request PATCH https://127.0.0.1:8765/nvue_v1/system/api/listening-address?rev=2 -H 'Content-Type:application/json' -d '{ "localhost": {}, "10.10.10.1": {}, "10.10.20.1": {}}'
以下示例配置 eth0 上的侦听地址,eth0 具有 IP 地址 172.0.24.0,并且默认使用管理 VRF
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k --request PATCH https://127.0.0.1:8765/nvue_v1/system/api/listening-address?rev=2 -H 'Content-Type:application/json' -d '{"172.0.24.0": {}}'
显示 NVUE REST API 信息
要显示 REST API 端口配置、状态(已启用或已禁用)、证书、侦听地址和连接信息
运行 nv show system api
命令
cumulus@switch:~$ nv show system api
operational applied
-------------- ----------- -------
port 8888 8888
state enabled enabled
certificate self-signed self-signed
[listening-address] localhost localhost
connections
accepted 31
active 1
handled 33
reading 0
requests 28
waiting 0
writing 1
要仅显示连接信息,请运行 nv show system api connections
命令
cumulus@switch:~$ nv show system api connections
operational applied
-------- ----------- -------
accepted 31
active 1
handled 33
reading 0
requests 28
waiting 0
writing 1
要显示配置的侦听地址,请运行 nv show system api listening-address
命令
cumulus@switch:~$ nv show system api listening-address
---------
localhost
要显示交换机上安装的所有证书,请运行 nv show system security certificate
命令。要显示有关特定证书的信息(例如序列号以及证书的有效期),请运行 nv show system security certificate <certificate>
命令
cumulus@switch:~$ nv show system security certificate tls-cert-1
operational applied pending
------------- ------------------------- ------- -------
installed
app TLS
serial-number 67:03:3B:B4:6E:35:D3
valid-from 2023-02-14T00:35:18+00:00
valid-to 2033-02-11T00:35:18+00:00
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k --request GET https://127.0.0.1:8765/nvue_v1/system/api?rev=2 -H "accept: application/json"
{
"certificate": "self-signed",
"listening-address": {
"10.10.10.1": {},
"10.10.20.1": {},
"172.0.24.0": {},
"localhost": {}
},
"port": 8888,
"state": "enabled"
}
要显示配置的侦听地址
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k --request GET https://127.0.0.1:8765/nvue_v1/system/api/listening-address?rev=2 -H "accept: application/json"
{
"10.10.10.1": {},
"10.10.20.1": {}
}
要显示交换机上的证书
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k --request GET https://127.0.0.1:8765/nvue_v1/system/api/certificate?rev=2 -H "accept: application/json"
{
"tls-cert-1": {},
"tls-cert-2": {}
}
要显示有关特定证书的信息(例如序列号以及证书的有效期)
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k --request GET https://127.0.0.1:8765/nvue_v1/system/api/certificate/tls-cert-1?rev=2 -H "accept: application/json"
{
"serial-number": "67:03:3B:B4:6E:35:D3",
"valid-from": "2023-02-14T00:35:18+00:00",
"valid-to": "2033-02-11T00:35:18+00:00"
}
运行 cURL 命令
您可以从命令行运行 cURL 命令。使用交换机的用户名和密码。例如
cumulus@switch:~$ curl -u 'cumulus:cumulus' --insecure https://127.0.0.1:8765/nvue_v1/interface
{
"eth0": {
"ip": {
"address": {
"192.168.200.12/24": {}
}
},
"link": {
"mtu": 1500,
"state": {
"up": {}
},
"stats": {
"carrier-transitions": 2,
"in-bytes": 184151,
"in-drops": 0,
"in-errors": 0,
"in-pkts": 2371,
"out-bytes": 117506,
"out-drops": 0,
"out-errors": 0,
"out-pkts": 762
}
...
API 用例
以下示例显示了主要的 API 用例。
查看配置
使用以下示例获取交换机上当前应用的配置。更改 rev
参数以查看任何修订版本。rev
参数的可能选项包括 startup
、pending
、operational
和 applied
。
cumulus@switch:~$ curl -k -u cumulus:cumulus -X GET "https://127.0.0.1:8765/nvue_v1/?rev=applied&filled=false"
"acl": {},
"bridge": {
"domain": {
"br_default": {
"encap": "802.1Q",
"mac-address": "auto",
"multicast": {
"snooping": {
"enable": "off"
}
},
"stp": {
"priority": 32768,
"state": {
"up": {}
}
},
"type": "vlan-aware",
"untagged": 1,
"vlan": {
"10": {
"multicast": {
...
#!/usr/bin/env python3
import requests
from requests.auth import HTTPBasicAuth
import json
import time
auth = HTTPBasicAuth(username="cumulus", password="password")
nvue_end_point = "https://127.0.0.1:8765/nvue_v1"
mime_header = {"Content-Type": "application/json"}
if __name__ == "__main__":
r = requests.get(url=nvue_end_point + "/?rev=applied&filled=false",
auth=auth,
verify=False)
print("=======Current Applied Revision=======")
print(json.dumps(r.json(), indent=2))
cumulus@switch:~$ nv config show
- set:
bridge:
domain:
br_default:
type: vlan-aware
vlan:
'10':
vni:
'10': {}
'20':
vni:
'20': {}
'30':
vni:
'30': {}
evpn:
enable: on
mlag:
backup:
10.10.10.2: {}
enable: on
init-delay: 10
mac-address: 44:38:39:BE:EF:AA
...
替换整个配置
要替换整个配置
使用 POST 创建新的修订版本 ID
cumulus@switch:~$ curl -u 'cumulus:cumulus' --insecure -X POST https://127.0.0.1:8765/nvue_v1/revision { "1": { "state": "pending", "transition": { "issue": {}, "progress": "" } } }
记录修订版本 ID。在上面的示例中,修订版本 ID 为
"1"
。执行根补丁以删除整个配置。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{}' -H 'Content-Type: application/json' -k -X DELETE https://127.0.0.1:8765/nvue_v1/?rev=1 {}
执行根补丁以使用新配置更新交换机。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{ "system": { "hostname": "switch01" }, "bridge": { "domain": { "br_default": { "type": "vlan-aware", "vlan": { "10": { "vni": { "10": {} } }, "20": { "vni": { "20": {} } }, "30": { "vni": { "30": {} } } } } } }, "interface": { "eth0": { "ip": { "address": { "192.168.200.6/24": {} }, "vrf": "mgmt" }, "type": "eth" }, "lo": { "ip": { "address": { "10.10.10.1/32": {} } }, "type": "loopback" }, "swp51": { "link": { "state": { "up": {} } }, "type": "swp" }, "swp52": { "link": { "state": { "up": {} } }, "type": "swp" }, "swp53": { "link": { "state": { "up": {} } }, "type": "swp" }, "swp54": { "link": { "state": { "up": {} } }, "type": "swp" } }, "mlag": { "backup": { "10.10.10.2": {} }, "enable": "on", "init-delay": 10, "mac-address": "44:38:39:BE:EF:AA", "peer-ip": "linklocal", "priority": 1000 } "router": { "bgp": { "enable": "on" }, "vrr": { "enable": "on" } }, "service": {}, "vrf": { "mgmt": { "router": { "static": { "0.0.0.0/0": { "address-family": "ipv4-unicast", "via": { "192.168.200.1": { "type": "ipv4-address" } } } } } } } }' -H 'Content-Type: application/json' -k -X PATCH https://127.0.0.1:8765/nvue_v1/?rev=1 {}
使用 PATCH 将更改应用于修订版本变更集。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -H 'Content-Type:application/json' -d '{"state": "apply", "auto-prompt": {"ays": "ays_yes"}}' -k -X PATCH https://127.0.0.1:8765/nvue_v1/revision/1 { "state": "apply", "transition": { "issue": {}, "progress": "" } }
cumulus@switch:~$ nv config apply
查看应用状态和配置
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k -X GET https://127.0.0.1:8765/nvue_v1/revision/1 { "state": "applied", "transition": { "issue": {}, "progress": "" } }
cumulus@switch:~$ curl -u 'cumulus:cumulus' --insecure https://127.0.0.1:8765/nvue_v1/system { "build": "Cumulus Linux 5.4.0", "hostname": "switch01", "timezone": "Etc/UTC", "uptime": 763 } cumulus@switch:~$ curl -u 'cumulus:cumulus' --insecure https://127.0.0.1:8765/nvue_v1/bridge/domain/br_default/vlan/10 { "multicast": { "snooping": { "querier": { "source-ip": "0.0.0.0" } } }, "ptp": { "enable": "off" }, "vni": { "10": { "flooding": { "enable": "auto" }, "mac-learning": "off" } }
#!/usr/bin/env python3 import requests from requests.auth import HTTPBasicAuth import json import time auth = HTTPBasicAuth(username="cumulus", password="password") nvue_end_point = "https://127.0.0.1:8765/nvue_v1" mime_header = {"Content-Type": "application/json"} DUMMY_SLEEP = 5 # In seconds POLL_APPLIED = 1 # in seconds RETRIES = 10 def print_request(r: requests.Request): print("=======Request=======") print("URL:", r.url) print("Headers:", r.headers) print("Body:", r.body) def print_response(r: requests.Response): print("=======Response=======") print("Headers:", r.headers) print("Body:", json.dumps(r.json(), indent=2)) def create_nvue_changest(): r = requests.post(url=nvue_end_point + "/revision", auth=auth, verify=False) print_request(r.request) print_response(r) response = r.json() changeset = response.popitem()[0] return changeset def apply_nvue_changeset(changeset): apply_payload = {"state": "apply", "auto-prompt": {"ays": "ays_yes"}} url = nvue_end_point + "/revision/" + requests.utils.quote(changeset, safe="") r = requests.patch(url=url, auth=auth, verify=False, data=json.dumps(apply_payload), headers=mime_header) print_request(r.request) print_response(r) def is_config_applied(changeset) -> bool: # Check if the configuration was indeed applied global RETRIES global POLL_APPLIED retries = RETRIES while retries > 0: r = requests.get(url=nvue_end_point + "/revision/" + requests.utils.quote(changeset, safe=""), auth=auth, verify=False) response = r.json() print(response) if response["state"] == "applied": return True retries -= 1 time.sleep(POLL_APPLIED) return False def apply_new_config(path,payload): # Create a new revision ID changeset = create_nvue_changest() print("Using NVUE Changeset: '{}'".format(changeset)) # Delete existing configuration query_string = {"rev": changeset} r = requests.delete(url=nvue_end_point + path, auth=auth, verify=False, params=query_string, headers=mime_header) print_request(r.request) print_response(r) # Patch the new configuration query_string = {"rev": changeset} r = requests.patch(url=nvue_end_point + path, auth=auth, verify=False, data=json.dumps(payload), params=query_string, headers=mime_header) print_request(r.request) print_response(r) # Apply the changes to the new revision changeset apply_nvue_changeset(changeset) # Check if the changeset was applied is_config_applied(changeset) def nvue_get(path): r = requests.get(url=nvue_end_point + path, auth=auth, verify=False) print_request(r.request) print_response(r) if __name__ == "__main__": payload = { "system": { "hostname": "switch01" }, "bridge": { "domain": { "br_default": { "type": "vlan-aware", "vlan": { "10": { "vni": { "10": {} } }, "20": { "vni": { "20": {} } }, "30": { "vni": { "30": {} } } } } } }, "interface": { "eth0": { "ip": { "address": { "192.168.200.6/24": {} }, "vrf": "mgmt" }, "type": "eth" }, "lo": { "ip": { "address": { "10.10.10.1/32": {} } }, "type": "loopback" }, "swp51": { "link": { "state": { "up": {} } }, "type": "swp" }, "swp52": { "link": { "state": { "up": {} } }, "type": "swp" }, "swp53": { "link": { "state": { "up": {} } }, "type": "swp" }, "swp54": { "link": { "state": { "up": {} } }, "type": "swp" } }, "mlag": { "backup": { "10.10.10.2": {} }, "enable": "on", "init-delay": 10, "mac-address": "44:38:39:BE:EF:AA", "peer-ip": "linklocal", "priority": 1000 } "router": { "bgp": { "enable": "on" }, "vrr": { "enable": "on" } }, "service": {}, "vrf": { "mgmt": { "router": { "static": { "0.0.0.0/0": { "address-family": "ipv4-unicast", "via": { "192.168.200.1": { "type": "ipv4-address" } } } } } } } } apply_new_config("/",payload) time.sleep(DUMMY_SLEEP) print("=====Verifying some of the configurations=====") nvue_get("/system") nvue_get("/bridge/domain/br_default/vlan/10")
cumulus@switch:~$ nv show system operational applied -------- ------------------- ------- hostname switch01 cumulus build Cumulus Linux 5.4.0 uptime 0:12:59 timezone Etc/UTC
cumulus@switch:~$ nv show bridge domain br_default vlan 10 operational applied pending description --------------- ----------- ------- ------- ------------------------------------------------------ [vni] 10 10 10 L2 VNI multicast snooping querier source-ip 0.0.0.0 0.0.0.0 0.0.0.0 Source IP to use when sending IGMP/MLD queries. ptp enable off off off Turn the feature 'on' or 'off'. The default is 'off'.
进行配置更改
要进行配置更改
使用 POST 创建新的修订版本 ID
cumulus@switch:~$ curl -u 'cumulus:cumulus' --insecure -X POST https://127.0.0.1:8765/nvue_v1/revision { "2": { "state": "pending", "transition": { "issue": {}, "progress": "" } } }
记录修订版本 ID。在上面的示例中,修订版本 ID 为
"2"
。使用 PATCH 进行更改,并将其链接到修订版本 ID
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{"99.99.99.99/32": {}}' -H 'Content-Type: application/json' -k -X PATCH https://127.0.0.1:8765/nvue_v1/interface/lo/ip/address?rev=2 { "99.99.99.99/32": {} }
cumulus@switch:~$ nv set interface lo ip address 99.99.99.99/32
使用 PATCH 将更改应用于修订版本变更集
cumulus@switch:~$ curl -u 'cumulus:cumulus' -H 'Content-Type:application/json' -k -X PATCH https://127.0.0.1:8765/nvue_v1/revision/2 { "state": "apply", "transition": { "issue": {}, "progress": "" } }
cumulus@switch:~$ nv config apply
查看应用状态和配置
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k -X GET https://127.0.0.1:8765/nvue_v1/revision/2 { "state": "applied", "transition": { "issue": {}, "progress": "" } }
cumulus@switch:~$ curl -u 'cumulus:cumulus' --insecure https://127.0.0.1:8765/nvue_v1/interface/lo/ip/address { "127.0.0.1/8": {}, "99.99.99.99/32": {}, "::1/128": {} }
#!/usr/bin/env python3 import requests from requests.auth import HTTPBasicAuth import json import time auth = HTTPBasicAuth(username="cumulus", password="password") nvue_end_point = "https://127.0.0.1:8765/nvue_v1" mime_header = {"Content-Type": "application/json"} DUMMY_SLEEP = 5 # In seconds POLL_APPLIED = 1 # in seconds RETRIES = 10 def print_request(r: requests.Request): print("=======Request=======") print("URL:", r.url) print("Headers:", r.headers) print("Body:", r.body) def print_response(r: requests.Response): print("=======Response=======") print("Headers:", r.headers) print("Body:", json.dumps(r.json(), indent=2)) def create_nvue_changest(): r = requests.post(url=nvue_end_point + "/revision", auth=auth, verify=False) print_request(r.request) print_response(r) response = r.json() changeset = response.popitem()[0] return changeset def apply_nvue_changeset(changeset): apply_payload = {"state": "apply", "auto-prompt": {"ays": "ays_yes"}} url = nvue_end_point + "/revision/" + requests.utils.quote(changeset, safe="") r = requests.patch(url=url, auth=auth, verify=False, data=json.dumps(apply_payload), headers=mime_header) print_request(r.request) print_response(r) def is_config_applied(changeset) -> bool: # Check if the configuration was indeed applied global RETRIES global POLL_APPLIED retries = RETRIES while retries > 0: r = requests.get(url=nvue_end_point + "/revision/" + requests.utils.quote(changeset, safe=""), auth=auth, verify=False) response = r.json() print(response) if response["state"] == "applied": return True retries -= 1 time.sleep(POLL_APPLIED) return False def apply_new_config(path,payload): # Create a new revision ID changeset = create_nvue_changest() print("Using NVUE Changeset: '{}'".format(changeset)) # Delete existing configuration query_string = {"rev": changeset} r = requests.delete(url=nvue_end_point + path, auth=auth, verify=False, params=query_string, headers=mime_header) print_request(r.request) print_response(r) # Patch the new configuration query_string = {"rev": changeset} r = requests.patch(url=nvue_end_point + path, auth=auth, verify=False, data=json.dumps(payload), params=query_string, headers=mime_header) print_request(r.request) print_response(r) # Apply the changes to the new revision changeset apply_nvue_changeset(changeset) # Check if the changeset was applied is_config_applied(changeset) def nvue_get(path): r = requests.get(url=nvue_end_point + path, auth=auth, verify=False) print_request(r.request) print_response(r) if __name__ == "__main__": payload = { "99.99.99.99/32": {} } apply_new_config("/interface/lo/ip/address",payload) time.sleep(DUMMY_SLEEP) nvue_get("/interface/lo/ip/address")
cumulus@switch:~$ nv show interface lo ip address ------------- 99.99.99.99/32 127.0.0.1/8 ::1/128
查看配置之间的差异
要查看配置之间的差异,请使用 API GET /nvue_v1/<resource>?rev=<rev-A>&diff=<rev-B>
方法以及您要 diff
的配置。此方法等效于 NVUE nv config diff <rev-A> <rev-B>
命令。
要查看启动修订版本和应用修订版本之间的差异
cumulus@switch:~$ curl -u 'cumulus:cumulus' --insecure -X GET /nvue_v1/interface?rev=startup&diff=applied
要查看修订版本 1 和修订版本 2 之间的差异
cumulus@switch:~$ curl -u 'cumulus:cumulus' --insecure -X GET /nvue_v1/<resource>?rev=1&diff=2
您可以更改修订版本的顺序;例如,GET /nvue_v1/<resource>?rev=2&diff=1
。
配置更改故障排除
当配置更改失败时,您会在更改请求中看到错误。
由于依赖关系而导致配置失败
如果您暂存配置,但由于依赖关系而失败,则失败会显示原因。在以下示例中,更改失败,因为未设置 BGP 路由器 ID。
cumulus@switch:~$ curl -u 'cumulus:cumulus' --insecure https://127.0.0.1:8765/nvue_v1/revision/6
{
"state": "invalid",
"transition": {
"issue": {
"0": {
"code": "config_invalid",
"data": {
"location": "router.bgp.enable",
"reason": "BGP requires router-id to be set globally or in the VRF.\n"
},
"message": "Config invalid at router.bgp.enable: BGP requires router-id to be set globally or in the VRF.\n",
"severity": "error"
}
},
"progress": "Invalid config"
}
}
暂存的配置缺少 router-id
。
cumulus@switch:~$ curl -u 'cumulus:cumulus' --insecure https://127.0.0.1:8765/nvue_v1/vrf/default/router/bgp?rev=6
{
"autonomous-system": 65999,
"enable": "on"
}
配置应用失败并显示警告
在某些情况下,例如首次使用 NVUE 推送或手动更改文件而不是使用 NVUE 时,您会看到警告提示,并且应用失败。
cumulus@switch:~$ curl -u 'cumulus:cumulus' --insecure -X GET https://127.0.0.1:8765/nvue_v1/revision/6
{
"6": {
"state": "ays_fail",
"transition": {
"issue": {
"0": {
"code": "client_timeout",
"data": {},
"message": "Timeout while waiting for client response",
"severity": "error"
}
},
"progress": "Aborted apply after warnings"
}
}
要解决此问题,请观察失败或错误,然后检查您尝试应用的配置。解决错误后,重试 API。如果您希望忽略错误并强制应用,请将 "auto-prompt":{"ays": "ays_yes"}
添加到配置应用。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{"state":"apply","auto-prompt":{"ays": "ays_yes"}}' -H 'Content-Type:application/json' --insecure -X PATCH https://127.0.0.1:8765/nvue_v1/revision/6
保存配置
要将应用的配置更改保存到启动配置文件 (/etc/nvue.d/startup.yaml
),以便更改在重新启动后仍然存在,请对应用的修订版本使用 PATCH 并使用 save
状态。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k -X PATCH -d '{"state": "save", "auto-prompt": {"ays": "ays_yes"}}' -H 'Content-Type: application/json' https://127.0.0.1:8765/nvue_v1/revision/applied
{
"state": "save",
"transition": {
"issue": {},
"progress": ""
}
}
#!/usr/bin/env python3
import requests
from requests.auth import HTTPBasicAuth
import json
import time
auth = HTTPBasicAuth(username="cumulus", password="password")
nvue_end_point = "https://127.0.0.1:8765/nvue_v1"
mime_header = {"Content-Type": "application/json"}
DUMMY_SLEEP = 5 # In seconds
POLL_APPLIED = 1 # in seconds
RETRIES = 10
def print_request(r: requests.Request):
print("=======Request=======")
print("URL:", r.url)
print("Headers:", r.headers)
print("Body:", r.body)
def print_response(r: requests.Response):
print("=======Response=======")
print("Headers:", r.headers)
print("Body:", json.dumps(r.json(), indent=2))
def save_nvue_changeset():
apply_payload = {"state": "save", "auto-prompt": {"ays": "ays_yes"}}
url = nvue_end_point + "/revision/applied"
r = requests.patch(url=url,
auth=auth,
verify=False,
data=json.dumps(apply_payload),
headers=mime_header)
print_request(r.request)
print_response(r)
if __name__ == "__main__":
save_nvue_changeset()
cumulus@switch:~$ nv config save
saved
取消设置配置更改
要取消设置配置更改,请对键使用 null
值。例如,要从交换机中删除 vlan100
,请使用以下语法
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{"vlan100":null}' -H 'Content-Type: application/json' --insecure -X PATCH https://127.0.0.1:8765/nvue_v1/interface/rev=4
当您取消设置更改时,您仍然必须使用 PATCH
操作。该值指示删除条目。数据为 {"vlan100":null}
和 PATCH 操作。
使用 API 进行主动监控
以下示例获取 interface swp1
的计数器。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k -X GET https://127.0.0.1:8765/nvue_v1/interface/swp1/link/stats
{
"carrier-transitions": 6,
"in-bytes": 293771538,
"in-drops": 0,
"in-errors": 0,
"in-pkts": 2321737,
"out-bytes": 366068936,
"out-drops": 0,
"out-errors": 0,
"out-pkts": 3536629
}
#!/usr/bin/env python3
import requests
from requests.auth import HTTPBasicAuth
import json
import time
auth = HTTPBasicAuth(username="cumulus", password="password")
nvue_end_point = "https://127.0.0.1:8765/nvue_v1"
mime_header = {"Content-Type": "application/json"}
if __name__ == "__main__":
r = requests.get(url=nvue_end_point + "/interface/swp1/link/stats",
auth=auth,
verify=False)
print("=======Interface swp1 Statistics=======")
print(json.dumps(r.json(), indent=2))
cumulus@switch:~$ nv show interface swp1 link stats
operational applied pending description
------------------- ----------- ------- ------- ----------------------------------------------------------------------
carrier-transitions 6 Number of times the interface state has transitioned between up and...
in-bytes 280.15 MB total number of bytes received on the interface
in-drops 0 number of received packets dropped
in-errors 0 number of received packets with errors
in-pkts 2321659 total number of packets received on the interface
out-bytes 349.10 MB total number of bytes transmitted out of the interface
out-drops 0 The number of outbound packets that were chosen to be discarded eve...
out-errors 0 The number of outbound packets that could not be transmitted becaus...
out-pkts 3536508 total number of packets transmitted out of the interface
检索视图类型
NVUE 为某些 show
命令提供视图。视图是信息的子集。
要查看 show 命令可用的视图,请使用 --view
运行命令并按 TAB 键
cumulus@switch:~$ nv show interface --view <<TAB>>
acl-statistics description lldp physical status
bond-members detail lldp-detail pluggables svi
bonds dot1x-counters mac port-security synce-counters
brief dot1x-summary mlag-cc qos-profile up
counters down neighbor small vrf
cumulus@switch:~$ nv show vrf default router rib ipv4 route --view <<TAB>>
brief detail
要通过 REST API 检索视图类型,您可以使用 curl -u 'cumulus:CumulusLinux!' -k -X GET http://path?view=<brief>
语法。例如,NVUE nv show vrf <vrf-id> router rib ipv4 route --view=brief
命令的等效 REST API 方法是
cumulus@switch:~$ curl -u 'cumulus:CumulusLinux!' -k -X GET https://127.0.0.1:8765/nvue_v1/vrf/BLUE/router/rib/ipv4/route?view=brief
NVUE nv show interface --view=acl-statistics
命令的等效 REST API 方法是
cumulus@switch:~$ curl -u 'cumulus:CumulusLinux!' -k -X GET https://127.0.0.1:8765/nvue_v1/interface?view=acl-statistics
对于查询不存在的视图。API 返回 400 Bad Request
错误,并显示该端点的所有已定义视图。
您还可以在查询中提供 include
和 omit
参数,以在响应中包含或省略某些属性。
您不能在同一 API 方法中同时使用 view=
和 include
或 omit
参数。例如,以下 REST API 方法返回错误
cumulus@switch:~$ curl -u 'cumulus:CumulusLinux!' --insecure -X GET "https://127.0.0.1:8765/nvue_v1/vrf/default/router/rib/ipv4/route?include=/*/route-entry/*/protocol,/*/route-entry/*/nexthop-group-id,/*/route-entry/*/uptime&view=brief"
以下示例返回路由表中的所有路由,并包含所有属性(API 请求不包含 include 或 omit 选项)
cumulus@switch:~$ curl -u 'cumulus:CumulusLinux!' --insecure -X GET https://127.0.0.1:8765/nvue_v1/vrf/default/router/rib/ipv4/route
{
"10.0.1.12/32": {
"route-entry": {
"1": {
"distance": 0,
"flags": {
"fib-selected": {},
"installed": {},
"offloaded": {},
"selected": {}
},
"flags-string": "*Sio",
"metric": 0,
"nexthop-group-id": 12,
"protocol": "connected",
"uptime": "2024-10-18T22:27:35Z"
}
}
},
"10.0.1.34/32": {
"route-entry": {
"1": {
"distance": 20,
"flags": {
"fib-selected": {},
"installed": {},
"selected": {}
},
"flags-string": "*Si",
"metric": 0,
"nexthop-group-id": 45,
"protocol": "bgp",
"uptime": "2024-10-18T22:27:39Z"
}
}
},
"10.0.1.255/32": {
"route-entry": {
"1": {
"distance": 20,
"flags": {
"fib-selected": {},
"installed": {},
"selected": {}
},
"flags-string": "*Si",
"metric": 0,
"nexthop-group-id": 38,
"protocol": "bgp",
"uptime": "2024-10-18T22:27:39Z"
}
}
},
"10.10.10.1/32": {
"route-entry": {
"1": {
"distance": 20,
"flags": {
"fib-selected": {},
"installed": {},
"selected": {}
},
"flags-string": "*Si",
"metric": 0,
"nexthop-group-id": 45,
"protocol": "bgp",
"uptime": "2024-10-18T22:27:39Z"
}
}
},
"10.10.10.2/32": {
"route-entry": {
"1": {
"distance": 20,
"flags": {
"fib-selected": {},
"installed": {},
"selected": {}
},
"flags-string": "*Si",
"metric": 0,
"nexthop-group-id": 38,
"protocol": "bgp",
"uptime": "2024-10-18T22:27:39Z"
}
}
},
"10.10.10.3/32": {
"route-entry": {
"1": {
"distance": 20,
"flags": {
"fib-selected": {},
"installed": {},
"selected": {}
},
"flags-string": "*Si",
"metric": 0,
"nexthop-group-id": 34,
"protocol": "bgp",
"uptime": "2024-10-18T22:27:38Z"
}
}
}
以下示例返回路由表中的所有路由,但仅包含协议、正常运行时间和 nexthop-group-id 属性
cumulus@switch:~$ curl -u 'cumulus:CumulusLinux!' --insecure -X GET "https://127.0.0.1:8765/nvue_v1/vrf/default/router/rib/ipv4/route?include=/*/route-entry/*/protocol,/*/route-entry/*/nexthop-group-id,/*/route-entry/*/uptime"
{
"10.0.1.12/32": {
"route-entry": {
"1": {
"nexthop-group-id": 12,
"protocol": "connected",
"uptime": "2024-10-18T22:27:35Z"
}
}
},
"10.0.1.34/32": {
"route-entry": {
"1": {
"nexthop-group-id": 45,
"protocol": "bgp",
"uptime": "2024-10-18T22:27:39Z"
}
}
},
"10.0.1.255/32": {
"route-entry": {
"1": {
"nexthop-group-id": 38,
"protocol": "bgp",
"uptime": "2024-10-18T22:27:39Z"
}
}
},
"10.10.10.1/32": {
"route-entry": {
"1": {
"nexthop-group-id": 45,
"protocol": "bgp",
"uptime": "2024-10-18T22:27:39Z"
}
}
},
"10.10.10.2/32": {
"route-entry": {
"1": {
"nexthop-group-id": 38,
"protocol": "bgp",
"uptime": "2024-10-18T22:27:39Z"
}
}
},
"10.10.10.3/32": {
"route-entry": {
"1": {
"nexthop-group-id": 34,
"protocol": "bgp",
"uptime": "2024-10-18T22:27:38Z"
}
}
}
}
以下示例返回路由表中的所有路由,但省略所有其他属性(nexthop-group-id、协议、正常运行时间、距离、指标和标志)
cumulus@switch:~$ curl -u 'cumulus:CumulusLinux!' --insecure -X GET https://127.0.0.1:8765/nvue_v1/vrf/default/router/rib/ipv4/route?omit=/*/*
{
"10.0.1.12/32": {},
"10.0.1.34/32": {},
"10.0.1.255/32": {},
"10.10.10.1/32": {},
"10.10.10.2/32": {},
"10.10.10.3/32": {}
}
转换 CLI 更改以使用 API
您可以从 CLI 获取配置更改,并使用 API 配置同一组更改。
使用 NVUE CLI 在系统上进行配置更改。
cumulus@switch:~$ nv set system hostname switch01 cumulus@switch:~$ nv set interface lo ip address 99.99.99.99/32 cumulus@switch:~$ nv set interface eth0 ip address 192.168.200.6/24 cumulus@switch:~$ nv set interface bond0 bond member swp1-4
将更改视为 JSON blob。
cumulus@switch:~$ nv config diff -o json [ { "set": { "interface": { "bond0": { "bond": { "member": { "swp1": {}, "swp2": {}, "swp3": {}, "swp4": {} } }, "type": "bond" }, "lo": { "ip": { "address": { "99.99.99.99/32": {} } } } }, "system": { "hostname": "switch01" } } } ]
将 JSON blob 钉在根补丁请求上作为有效负载。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{ "interface": { "bond0": { "bond": { "member": { "swp1": {}, "swp2": {}, "swp3": {}, "swp4": {} } }, "type": "bond" }, "lo": { "ip": { "address": { "99.99.99.99/32": {} } } } }, "system": { "hostname": "switch01" } }' -k -X PATCH https://127.0.0.1:8765/nvue_v1/?rev=3 { "bridge": { "domain": { "br_default": { "type": "vlan-aware", "vlan": { "10": { "vni": { "10": {} } }, "20": { "vni": { "20": {} } }, "30": { "vni": { "30": {} } } } } } }, "evpn": { "enable": "on" }, "interface": { "bond1": { "bond": { "lacp-bypass": "on", "member": { "swp1": {} }, ...
使用 PATCH 将更改应用于修订版本变更集。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -H 'Content-Type:application/json' -k -d '{"state": "apply", "auto-prompt": {"ays": "ays_yes"}}' -X PATCH https://127.0.0.1:8765/nvue_v1/revision/3 { "state": "apply", "transition": { "issue": {}, "progress": "" } }
查看应用状态和配置
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k -X GET https://127.0.0.1:8765/nvue_v1/revision/3 { "state": "applied", "transition": { "issue": {}, "progress": "" } }
#!/usr/bin/env python3 import requests from requests.auth import HTTPBasicAuth import json import time auth = HTTPBasicAuth(username="cumulus", password="password") nvue_end_point = "https://127.0.0.1:8765/nvue_v1" mime_header = {"Content-Type": "application/json"} DUMMY_SLEEP = 5 # In seconds POLL_APPLIED = 1 # in seconds RETRIES = 10 def print_request(r: requests.Request): print("=======Request=======") print("URL:", r.url) print("Headers:", r.headers) print("Body:", r.body) def print_response(r: requests.Response): print("=======Response=======") print("Headers:", r.headers) print("Body:", json.dumps(r.json(), indent=2)) def create_nvue_changest(): r = requests.post(url=nvue_end_point + "/revision", auth=auth, verify=False) print_request(r.request) print_response(r) response = r.json() changeset = response.popitem()[0] return changeset def apply_nvue_changeset(changeset): # apply_payload = {"state": "apply"} apply_payload = {"state": "apply", "auto-prompt": {"ays": "ays_yes"}} url = nvue_end_point + "/revision/" + requests.utils.quote(changeset, safe="") r = requests.patch(url=url, auth=auth, verify=False, data=json.dumps(apply_payload), headers=mime_header) print_request(r.request) print_response(r) def is_config_applied(changeset) -> bool: # Check if the configuration was indeed applied global RETRIES global POLL_APPLIED retries = RETRIES while retries > 0: r = requests.get(url=nvue_end_point + "/revision/" + requests.utils.quote(changeset, safe=""), auth=auth, verify=False) response = r.json() print(response) if response["state"] == "applied": return True retries -= 1 time.sleep(POLL_APPLIED) return False def apply_new_config(path,payload): # Create a new revision ID changeset = create_nvue_changest() print("Using NVUE Changeset: '{}'".format(changeset)) # Delete existing configuration query_string = {"rev": changeset} r = requests.delete(url=nvue_end_point + path, auth=auth, verify=False, params=query_string, headers=mime_header) print_request(r.request) print_response(r) # Patch the new configuration query_string = {"rev": changeset} r = requests.patch(url=nvue_end_point + path, auth=auth, verify=False, data=json.dumps(payload), params=query_string, headers=mime_header) print_request(r.request) print_response(r) # Apply the changes to the new revision changeset apply_nvue_changeset(changeset) # Check if the changeset was applied is_config_applied(changeset) def nvue_get(path): r = requests.get(url=nvue_end_point + path, auth=auth, verify=False) print_request(r.request) print_response(r) if __name__ == "__main__": payload = { "interface": { "bond0": { "bond": { "member": { "swp1": {}, "swp2": {}, "swp3": {}, "swp4": {} } }, "type": "bond" }, "lo": { "ip": { "address": { "99.99.99.99/32": {} } } } }, "system": { "hostname": "switch01" } } apply_new_config("/",payload) time.sleep(DUMMY_SLEEP) nvue_get("/interface/bond0") nvue_get("/interface/lo") nvue_get("/system")
API 示例
以下部分提供实用的 API 示例。
配置系统
要在交换机上设置系统主机名、登录前或登录后消息以及时区,请将目标 API 请求发送到 /nvue_v1/system
。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{"system": {"hostname":"switch01","timezone":"America/Los_Angeles","message":{"pre-login":"Welcome to NVIDIA Cumulus Linux","post-login":"You have successfully logged in to switch01"}}}' -k -X PATCH https://127.0.0.1:8765/nvue_v1/?rev=4
#!/usr/bin/env python3
import requests
from requests.auth import HTTPBasicAuth
import json
import time
auth = HTTPBasicAuth(username="cumulus", password="password")
nvue_end_point = "https://127.0.0.1:8765/nvue_v1"
mime_header = {"Content-Type": "application/json"}
DUMMY_SLEEP = 5 # In seconds
POLL_APPLIED = 1 # in seconds
RETRIES = 10
def print_request(r: requests.Request):
print("=======Request=======")
print("URL:", r.url)
print("Headers:", r.headers)
print("Body:", r.body)
def print_response(r: requests.Response):
print("=======Response=======")
print("Headers:", r.headers)
print("Body:", json.dumps(r.json(), indent=2))
def create_nvue_changest():
r = requests.post(url=nvue_end_point + "/revision",
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
response = r.json()
changeset = response.popitem()[0]
return changeset
def apply_nvue_changeset(changeset):
# apply_payload = {"state": "apply"}
apply_payload = {"state": "apply", "auto-prompt": {"ays": "ays_yes"}}
url = nvue_end_point + "/revision/" + requests.utils.quote(changeset,
safe="")
r = requests.patch(url=url,
auth=auth,
verify=False,
data=json.dumps(apply_payload),
headers=mime_header)
print_request(r.request)
print_response(r)
def is_config_applied(changeset) -> bool:
# Check if the configuration was indeed applied
global RETRIES
global POLL_APPLIED
retries = RETRIES
while retries > 0:
r = requests.get(url=nvue_end_point + "/revision/" + requests.utils.quote(changeset, safe=""),
auth=auth,
verify=False)
response = r.json()
print(response)
if response["state"] == "applied":
return True
retries -= 1
time.sleep(POLL_APPLIED)
return False
def apply_new_config(path,payload):
# Create a new revision ID
changeset = create_nvue_changest()
print("Using NVUE Changeset: '{}'".format(changeset))
# Delete existing configuration
query_string = {"rev": changeset}
r = requests.delete(url=nvue_end_point + path,
auth=auth,
verify=False,
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Patch the new configuration
query_string = {"rev": changeset}
r = requests.patch(url=nvue_end_point + path,
auth=auth,
verify=False,
data=json.dumps(payload),
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Apply the changes to the new revision changeset
apply_nvue_changeset(changeset)
# Check if the changeset was applied
is_config_applied(changeset)
def nvue_get(path):
r = requests.get(url=nvue_end_point + path,
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
if __name__ == "__main__":
payload = {
"system":
{
"hostname":"switch01",
"timezone":"America/Los_Angeles",
"message":
{
"pre-login":"Welcome to NVIDIA Cumulus Linux",
"post-login:"You have successfully logged in to switch01"
}
}
}
apply_new_config("/",payload) # Root patch
time.sleep(DUMMY_SLEEP)
nvue_get("/system")
cumulus@switch:~$ nv set system hostname switch01
cumulus@switch:~$ nv set system timezone America/Los_Angeles
cumulus@switch:~$ nv set system message pre-login "Welcome to NVIDIA Cumulus Linux"
cumulus@switch:~$ nv set system message post-login "You have successfully logged into switch01"
配置服务
要在交换机上设置 NTP、DNS 和 SNMP,请将目标 API 请求发送到 /nvue_v1/service
。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{"service": { "ntp": {"default":{"server":{"4.cumulusnetworks.pool.ntp.org":{"iburst":"on"}}}}, "dns": {"mgmt":{"server":{"192.168.1.100":{}}}}, "syslog": {"mgmt":{"server":{"192.168.1.120":{"port":8000}}}}}}' -k -X PATCH https://127.0.0.1:8765/nvue_v1/?rev=5
#!/usr/bin/env python3
import requests
from requests.auth import HTTPBasicAuth
import json
import time
auth = HTTPBasicAuth(username="cumulus", password="password")
nvue_end_point = "https://127.0.0.1:8765/nvue_v1"
mime_header = {"Content-Type": "application/json"}
DUMMY_SLEEP = 5 # In seconds
POLL_APPLIED = 1 # in seconds
RETRIES = 10
def print_request(r: requests.Request):
print("=======Request=======")
print("URL:", r.url)
print("Headers:", r.headers)
print("Body:", r.body)
def print_response(r: requests.Response):
print("=======Response=======")
print("Headers:", r.headers)
print("Body:", json.dumps(r.json(), indent=2))
def create_nvue_changest():
r = requests.post(url=nvue_end_point + "/revision",
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
response = r.json()
changeset = response.popitem()[0]
return changeset
def apply_nvue_changeset(changeset):
# apply_payload = {"state": "apply"}
apply_payload = {"state": "apply", "auto-prompt": {"ays": "ays_yes"}}
url = nvue_end_point + "/revision/" + requests.utils.quote(changeset,
safe="")
r = requests.patch(url=url,
auth=auth,
verify=False,
data=json.dumps(apply_payload),
headers=mime_header)
print_request(r.request)
print_response(r)
def is_config_applied(changeset) -> bool:
# Check if the configuration was indeed applied
global RETRIES
global POLL_APPLIED
retries = RETRIES
while retries > 0:
r = requests.get(url=nvue_end_point + "/revision/" + requests.utils.quote(changeset, safe=""),
auth=auth,
verify=False)
response = r.json()
print(response)
if response["state"] == "applied":
return True
retries -= 1
time.sleep(POLL_APPLIED)
return False
def apply_new_config(path,payload):
# Create a new revision ID
changeset = create_nvue_changest()
print("Using NVUE Changeset: '{}'".format(changeset))
# Delete existing configuration
query_string = {"rev": changeset}
r = requests.delete(url=nvue_end_point + path,
auth=auth,
verify=False,
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Patch the new configuration
query_string = {"rev": changeset}
r = requests.patch(url=nvue_end_point + path,
auth=auth,
verify=False,
data=json.dumps(payload),
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Apply the changes to the new revision changeset
apply_nvue_changeset(changeset)
# Check if the changeset was applied
is_config_applied(changeset)
def nvue_get(path):
r = requests.get(url=nvue_end_point + path,
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
if __name__ == "__main__":
payload = {
"service":
{
"ntp":
{
"default":
{
"server:
{
"4.cumulusnetworks.pool.ntp.org":
{
"iburst":"on"
}
}
}
},
"dns":
{
"mgmt":
{
"server:
{
"192.168.1.100":{}
}
}
},
"syslog":
{
"mgmt":
{
"server:
{
"192.168.1.120":
{
"port":8000
}
}
}
}
}
}
apply_new_config("/",payload) # Root patch
time.sleep(DUMMY_SLEEP)
nvue_get("/service/ntp")
nvue_get("/service/dns")
nvue_get("/service/syslog")
cumulus@switch:~$ nv set service ntp default server 4.cumulusnetworks.pool.ntp.org iburst on
cumulus@switch:~$ nv set service dns mgmt server 192.168.1.100
cumulus@switch:~$ nv set service syslog mgmt server 192.168.1.120 port 8000
配置用户
以下示例创建一个新用户,然后删除该用户。
此示例创建一个名为 test1
的新用户。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{"system": {"aaa": {"user": {"test1": {"hashed-password":"72b28582708d749c6c82f3b3f226041f1bd37090281641eaeba8d44bd915d0042d609a92759d9f6fb96475cb0601cf428cd22613df8a53a09461e0b426cf0a35","role": "nvue-monitor","enable": "on","full-name": "Test User"}}}}}' -k -X PATCH https://127.0.0.1:8765/nvue_v1/?rev=5
此示例删除 test1
用户。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k -X DELETE https://127.0.0.1:8765/nvue_v1/system/aaa/user/test1?rev=6
#!/usr/bin/env python3
import requests
from requests.auth import HTTPBasicAuth
import json
import time
auth = HTTPBasicAuth(username="cumulus", password="password")
nvue_end_point = "https://127.0.0.1:8765/nvue_v1"
mime_header = {"Content-Type": "application/json"}
DUMMY_SLEEP = 5 # In seconds
POLL_APPLIED = 1 # in seconds
RETRIES = 10
def print_request(r: requests.Request):
print("=======Request=======")
print("URL:", r.url)
print("Headers:", r.headers)
print("Body:", r.body)
def print_response(r: requests.Response):
print("=======Response=======")
print("Headers:", r.headers)
print("Body:", json.dumps(r.json(), indent=2))
def create_nvue_changest():
r = requests.post(url=nvue_end_point + "/revision",
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
response = r.json()
changeset = response.popitem()[0]
return changeset
def apply_nvue_changeset(changeset):
# apply_payload = {"state": "apply"}
apply_payload = {"state": "apply", "auto-prompt": {"ays": "ays_yes"}}
url = nvue_end_point + "/revision/" + requests.utils.quote(changeset,
safe="")
r = requests.patch(url=url,
auth=auth,
verify=False,
data=json.dumps(apply_payload),
headers=mime_header)
print_request(r.request)
print_response(r)
def is_config_applied(changeset) -> bool:
# Check if the configuration was indeed applied
global RETRIES
global POLL_APPLIED
retries = RETRIES
while retries > 0:
r = requests.get(url=nvue_end_point + "/revision/" + requests.utils.quote(changeset, safe=""),
auth=auth,
verify=False)
response = r.json()
print(response)
if response["state"] == "applied":
return True
retries -= 1
time.sleep(POLL_APPLIED)
return False
def apply_new_config(path,payload):
# Create a new revision ID
changeset = create_nvue_changest()
print("Using NVUE Changeset: '{}'".format(changeset))
# Delete existing configuration
query_string = {"rev": changeset}
r = requests.delete(url=nvue_end_point + path,
auth=auth,
verify=False,
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Patch the new configuration
query_string = {"rev": changeset}
r = requests.patch(url=nvue_end_point + path,
auth=auth,
verify=False,
data=json.dumps(payload),
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Apply the changes to the new revision changeset
apply_nvue_changeset(changeset)
# Check if the changeset was applied
is_config_applied(changeset)
def delete_config(path):
# Create an NVUE changeset
changeset = create_nvue_changest()
print("Using NVUE Changeset: '{}'".format(changeset))
# Equivalent to JSON `null`
payload = None
# Stage the change
query_string = {"rev": changeset}
r = requests.delete(url=nvue_end_point + path,
auth=auth,
verify=False,
data=json.dumps(payload),
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Apply the staged changeset
apply_nvue_changeset(changeset)
# Check if the changeset was applied
is_config_applied(changeset)
def nvue_get(path):
r = requests.get(url=nvue_end_point + path,
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
if __name__ == "__main__":
# Need to create a hashed password - The supported password
# hashes are documented here:
# https://docs.nvda.net.cn/networking-ethernet-software/cumulus-linux-55/System-Configuration/Authentication-Authorization-and-Accounting/User-Accounts/#hashed-passwords # noqa
# Here in this example, we use SHA-512
import crypt
hashed_password = crypt.crypt("hello$world#2023", salt=crypt.METHOD_SHA512)
payload = {
"system": {
"aaa": {
"user": {
"test1": {
"hashed-password": hashed_password,
"role": "nvue-monitor",
"enable": "on",
"full-name": "Test User",
}
}
}
}
}
apply_new_config("/",payload) # Root patch
time.sleep(DUMMY_SLEEP)
nvue_get("/system/user/aaa")
"""Delete an existing user account using the AAA API."""
delete_config("/system/aaa/user/test1")
time.sleep(DUMMY_SLEEP)
nvue_get("/system/user/aaa")
此示例创建一个新用户 test1
。
cumulus@switch:~$ nv set system aaa user test1
cumulus@switch:~$ nv set system aaa user test1 full-name "Test User"
cumulus@switch:~$ nv set system aaa user test1 password "abcd@test"
cumulus@switch:~$ nv set system aaa user test1 role nvue-monitor
cumulus@switch:~$ nv set system aaa user test1 enable on
此示例删除用户 test1
。
cumulus@switch:~$ nv unset system aaa user test1
配置接口
以下示例配置一个接口。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -k -d '{"swp1": {"link":{"state":{"up": {}}}}}' -H 'Content-Type: application/json' -k -X PATCH https://127.0.0.1:8765/nvue_v1/interface?rev=21
#!/usr/bin/env python3
import requests
from requests.auth import HTTPBasicAuth
import json
import time
auth = HTTPBasicAuth(username="cumulus", password="password")
nvue_end_point = "https://127.0.0.1:8765/nvue_v1"
mime_header = {"Content-Type": "application/json"}
DUMMY_SLEEP = 5 # In seconds
POLL_APPLIED = 1 # in seconds
RETRIES = 10
def print_request(r: requests.Request):
print("=======Request=======")
print("URL:", r.url)
print("Headers:", r.headers)
print("Body:", r.body)
def print_response(r: requests.Response):
print("=======Response=======")
print("Headers:", r.headers)
print("Body:", json.dumps(r.json(), indent=2))
def create_nvue_changest():
r = requests.post(url=nvue_end_point + "/revision",
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
response = r.json()
changeset = response.popitem()[0]
return changeset
def apply_nvue_changeset(changeset):
# apply_payload = {"state": "apply"}
apply_payload = {"state": "apply", "auto-prompt": {"ays": "ays_yes"}}
url = nvue_end_point + "/revision/" + requests.utils.quote(changeset,
safe="")
r = requests.patch(url=url,
auth=auth,
verify=False,
data=json.dumps(apply_payload),
headers=mime_header)
print_request(r.request)
print_response(r)
def is_config_applied(changeset) -> bool:
# Check if the configuration was indeed applied
global RETRIES
global POLL_APPLIED
retries = RETRIES
while retries > 0:
r = requests.get(url=nvue_end_point + "/revision/" + requests.utils.quote(changeset, safe=""),
auth=auth,
verify=False)
response = r.json()
print(response)
if response["state"] == "applied":
return True
retries -= 1
time.sleep(POLL_APPLIED)
return False
def apply_new_config(path,payload):
# Create a new revision ID
changeset = create_nvue_changest()
print("Using NVUE Changeset: '{}'".format(changeset))
# Delete existing configuration
query_string = {"rev": changeset}
r = requests.delete(url=nvue_end_point + path,
auth=auth,
verify=False,
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Patch the new configuration
query_string = {"rev": changeset}
r = requests.patch(url=nvue_end_point + path,
auth=auth,
verify=False,
data=json.dumps(payload),
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Apply the changes to the new revision changeset
apply_nvue_changeset(changeset)
# Check if the changeset was applied
is_config_applied(changeset)
def nvue_get(path):
r = requests.get(url=nvue_end_point + path,
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
if __name__ == "__main__":
payload = {
"swp1":
{
"type":"swp",
"link":
{
"state":"up"
}
}
}
apply_new_config("/interface",payload)
time.sleep(DUMMY_SLEEP)
nvue_get("/interface/swp1")
cumulus@switch:~$ nv set interface swp1
配置 Bond
以下示例配置一个 bond。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{"bond0": {"type":"bond","bond":{"member":{"swp1":{},"swp2":{},"swp3":{},"swp4":{}}}}}' -H 'Content-Type: application/json' -k -X PATCH https://127.0.0.1:8765/nvue_v1/interface?rev=7
{
"bond0": {
"bond": {
"member": {
"swp1": {},
"swp2": {},
"swp3": {},
"swp4": {}
}
},
"type": "bond"
}
}
#!/usr/bin/env python3
import requests
from requests.auth import HTTPBasicAuth
import json
import time
auth = HTTPBasicAuth(username="cumulus", password="password")
nvue_end_point = "https://127.0.0.1:8765/nvue_v1"
mime_header = {"Content-Type": "application/json"}
DUMMY_SLEEP = 5 # In seconds
POLL_APPLIED = 1 # in seconds
RETRIES = 10
def print_request(r: requests.Request):
print("=======Request=======")
print("URL:", r.url)
print("Headers:", r.headers)
print("Body:", r.body)
def print_response(r: requests.Response):
print("=======Response=======")
print("Headers:", r.headers)
print("Body:", json.dumps(r.json(), indent=2))
def create_nvue_changest():
r = requests.post(url=nvue_end_point + "/revision",
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
response = r.json()
changeset = response.popitem()[0]
return changeset
def apply_nvue_changeset(changeset):
# apply_payload = {"state": "apply"}
apply_payload = {"state": "apply", "auto-prompt": {"ays": "ays_yes"}}
url = nvue_end_point + "/revision/" + requests.utils.quote(changeset,
safe="")
r = requests.patch(url=url,
auth=auth,
verify=False,
data=json.dumps(apply_payload),
headers=mime_header)
print_request(r.request)
print_response(r)
def is_config_applied(changeset) -> bool:
# Check if the configuration was indeed applied
global RETRIES
global POLL_APPLIED
retries = RETRIES
while retries > 0:
r = requests.get(url=nvue_end_point + "/revision/" + requests.utils.quote(changeset, safe=""),
auth=auth,
verify=False)
response = r.json()
print(response)
if response["state"] == "applied":
return True
retries -= 1
time.sleep(POLL_APPLIED)
return False
def apply_new_config(path,payload):
# Create a new revision ID
changeset = create_nvue_changest()
print("Using NVUE Changeset: '{}'".format(changeset))
# Delete existing configuration
query_string = {"rev": changeset}
r = requests.delete(url=nvue_end_point + path,
auth=auth,
verify=False,
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Patch the new configuration
query_string = {"rev": changeset}
r = requests.patch(url=nvue_end_point + path,
auth=auth,
verify=False,
data=json.dumps(payload),
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Apply the changes to the new revision changeset
apply_nvue_changeset(changeset)
# Check if the changeset was applied
is_config_applied(changeset)
def nvue_get(path):
r = requests.get(url=nvue_end_point + path,
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
if __name__ == "__main__":
payload = {
"bond0":
{
"type":"bond",
"bond":
{
"member":
{
"swp1":{},
"swp2":{},
"swp3":{},
"swp4":{}
}
}
}
}
apply_new_config("/interface",payload)
time.sleep(DUMMY_SLEEP)
nvue_get("/interface/bond0")
cumulus@switch:~$ nv set interface bond0 bond member swp1-4
配置 Bridge
以下示例配置一个 bridge。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{"swp1": {"bridge":{"domain":{"br_default":{}}}},"swp2": {"bridge":{"domain":{"br_default":{}}}}}' -H 'Content-Type: application/json' -k -X PATCH https://127.0.0.1:8765/nvue_v1/interface?rev=21
{
"swp1": {
"bridge": {
"domain": {
"br_default": {}
}
},
"type": "swp"
},
"swp2": {
"bridge": {
"domain": {
"br_default": {}
}
},
"type": "swp"
}
}
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{"untagged":1,"vlan":{"10":{},"20":{}}}' -H 'Content-Type: application/json' -k -X PATCH https://127.0.0.1:8765/nvue_v1/bridge/domain/br_default?rev=8
{ “untagged”: 1, “vlan”: { “10”: {}, “20”: {} } }
#!/usr/bin/env python3
import requests
from requests.auth import HTTPBasicAuth
import json
import time
auth = HTTPBasicAuth(username="cumulus", password="password")
nvue_end_point = "https://127.0.0.1:8765/nvue_v1"
mime_header = {"Content-Type": "application/json"}
DUMMY_SLEEP = 5 # In seconds
POLL_APPLIED = 1 # in seconds
RETRIES = 10
def print_request(r: requests.Request):
print("=======Request=======")
print("URL:", r.url)
print("Headers:", r.headers)
print("Body:", r.body)
def print_response(r: requests.Response):
print("=======Response=======")
print("Headers:", r.headers)
print("Body:", json.dumps(r.json(), indent=2))
def create_nvue_changest():
r = requests.post(url=nvue_end_point + "/revision",
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
response = r.json()
changeset = response.popitem()[0]
return changeset
def apply_nvue_changeset(changeset):
# apply_payload = {"state": "apply"}
apply_payload = {"state": "apply", "auto-prompt": {"ays": "ays_yes"}}
url = nvue_end_point + "/revision/" + requests.utils.quote(changeset,
safe="")
r = requests.patch(url=url,
auth=auth,
verify=False,
data=json.dumps(apply_payload),
headers=mime_header)
print_request(r.request)
print_response(r)
def is_config_applied(changeset) -> bool:
# Check if the configuration was indeed applied
global RETRIES
global POLL_APPLIED
retries = RETRIES
while retries > 0:
r = requests.get(url=nvue_end_point + "/revision/" + requests.utils.quote(changeset, safe=""),
auth=auth,
verify=False)
response = r.json()
print(response)
if response["state"] == "applied":
return True
retries -= 1
time.sleep(POLL_APPLIED)
return False
def apply_new_config(path,payload):
# Create a new revision ID
changeset = create_nvue_changest()
print("Using NVUE Changeset: '{}'".format(changeset))
# Delete existing configuration
query_string = {"rev": changeset}
r = requests.delete(url=nvue_end_point + path,
auth=auth,
verify=False,
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Patch the new configuration
query_string = {"rev": changeset}
r = requests.patch(url=nvue_end_point + path,
auth=auth,
verify=False,
data=json.dumps(payload),
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Apply the changes to the new revision changeset
apply_nvue_changeset(changeset)
# Check if the changeset was applied
is_config_applied(changeset)
def nvue_get(path):
r = requests.get(url=nvue_end_point + path,
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
if __name__ == "__main__":
int_payload = {
"swp1":
{
"bridge":
{
"domain":
{
"br_default":{}
}
},
"swp2":
{
"bridge":
{
"domain":
{
"br_default":{}
}
}
}
}
}
apply_new_config("/interface",int_payload)
br_payload = {
"untagged":1,
"vlan":
{
"10":{},
"20":{}
}
}
apply_new_config("/bridge/domain/br_default",br_payload)
time.sleep(DUMMY_SLEEP)
nvue_get("/interface/swp1")
nvue_get("/bridge/domain/br_default")
cumulus@switch:~$ nv set interface swp1-2 bridge domain br_default
cumulus@switch:~$ nv set bridge domain br_default vlan 10,20
cumulus@switch:~$ nv set bridge domain br_default untagged 1
配置 BGP
以下示例配置 BGP。
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{"bgp": {"autonomous-system": 65101,"router-id":"10.10.10.1"}}' -H 'Content-Type: application/json' -k -X PATCH https://127.0.0.1:8765/nvue_v1/router?rev=9
cumulus@switch:~$ curl -u 'cumulus:cumulus' -d '{"bgp":{"neighbor":{"swp51":{"remote-as":"external"}},"address-family":{"ipv4-unicast":{"network":{"10.10.10.1/32":{}}}}}}' -H 'Content-Type: application/json' -k -X PATCH https://127.0.0.1:8765/nvue_v1/vrf/default/router?rev=9
#!/usr/bin/env python3
import requests
from requests.auth import HTTPBasicAuth
import json
import time
auth = HTTPBasicAuth(username="cumulus", password="password")
nvue_end_point = "https://127.0.0.1:8765/nvue_v1"
mime_header = {"Content-Type": "application/json"}
DUMMY_SLEEP = 5 # In seconds
POLL_APPLIED = 1 # in seconds
RETRIES = 10
def print_request(r: requests.Request):
print("=======Request=======")
print("URL:", r.url)
print("Headers:", r.headers)
print("Body:", r.body)
def print_response(r: requests.Response):
print("=======Response=======")
print("Headers:", r.headers)
print("Body:", json.dumps(r.json(), indent=2))
def create_nvue_changest():
r = requests.post(url=nvue_end_point + "/revision",
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
response = r.json()
changeset = response.popitem()[0]
return changeset
def apply_nvue_changeset(changeset):
# apply_payload = {"state": "apply"}
apply_payload = {"state": "apply", "auto-prompt": {"ays": "ays_yes"}}
url = nvue_end_point + "/revision/" + requests.utils.quote(changeset,
safe="")
r = requests.patch(url=url,
auth=auth,
verify=False,
data=json.dumps(apply_payload),
headers=mime_header)
print_request(r.request)
print_response(r)
def is_config_applied(changeset) -> bool:
# Check if the configuration was indeed applied
global RETRIES
global POLL_APPLIED
retries = RETRIES
while retries > 0:
r = requests.get(url=nvue_end_point + "/revision/" + requests.utils.quote(changeset, safe=""),
auth=auth,
verify=False)
response = r.json()
print(response)
if response["state"] == "applied":
return True
retries -= 1
time.sleep(POLL_APPLIED)
return False
def apply_new_config(path,payload):
# Create a new revision ID
changeset = create_nvue_changest()
print("Using NVUE Changeset: '{}'".format(changeset))
# Delete existing configuration
query_string = {"rev": changeset}
r = requests.delete(url=nvue_end_point + path,
auth=auth,
verify=False,
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Patch the new configuration
query_string = {"rev": changeset}
r = requests.patch(url=nvue_end_point + path,
auth=auth,
verify=False,
data=json.dumps(payload),
params=query_string,
headers=mime_header)
print_request(r.request)
print_response(r)
# Apply the changes to the new revision changeset
apply_nvue_changeset(changeset)
# Check if the changeset was applied
is_config_applied(changeset)
def nvue_get(path):
r = requests.get(url=nvue_end_point + path,
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
if __name__ == "__main__":
rt_payload = {
"bgp":
{
"autonomous-system": 65101,
"router-id":"10.10.10.1"
}
}
apply_new_config("/router",rt_payload)
vrf_payload = {
"bgp":
{
"neighbor":
{
"swp51":
{
"remote-as":"external"
}
},
"address-family":
{
"ipv4-unicast":
{
"network":
{
"10.10.10.1/32":{}
}
}
}
}
}
apply_new_config("/vrf/default/router",vrf_payload)
time.sleep(DUMMY_SLEEP)
nvue_get("/router")
nvue_get("/vrf/default/router")
cumulus@switch:~$ nv set router bgp autonomous-system 65101
cumulus@switch:~$ nv set router bgp router-id 10.10.10.1
cumulus@switch:~$ nv set vrf default router bgp neighbor swp51 remote-as external
cumulus@switch:~$ nv set vrf default router bgp address-family ipv4-unicast network 10.10.10.1/32
操作操作
NVUE 操作操作是临时操作,不会修改配置的状态;它们重置接口、BGP、QoS 缓冲区和池的计数器,并从 protodown MLAG bond 中删除冲突。
要清除 swp1 上的计数器
cumulus@switch:~$ curl -u 'cumulus:cumulus' -H 'Content-Type:application/json' -d '{"@clear": {"state": "start", "parameters": {}}}' -k -X POST https://127.0.0.1:8765/nvue_v1/interface/swp1/counters
1
cumulus@switch:~$ curl -u 'cumulus:cumulus' -X GET https://127.0.0.1:8765/nvue_v1/action/1 -k
{"detail":"swp1 counters cleared.","http_status":200,"issue":[],"state":"action_success","status":"swp1 counters cleared.","timeout":60,"type":""}
要清除 swp1 上的 QoS 缓冲区
cumulus@switch:~$ curl -u 'cumulus:cumulus' -H 'Content-Type:application/json' -d '{"@clear": {"state": "start", "parameters": {}}}' -k -X POST https://127.0.0.1:8765/nvue_v1/interface/swp1/qos/buffer
2
cumulus@switch:~$ curl -u 'cumulus:cumulus' -X GET https://127.0.0.1:8765/nvue_v1/action/2 -k
{"detail":"QoS buffers cleared on swp1.","http_status":200,"issue":[],"state":"action_success","status":"QoS buffers cleared on swp1.","timeout":60,"type":""}
#!/usr/bin/env python3
import requests
from requests.auth import HTTPBasicAuth
import json
import time
auth = HTTPBasicAuth(username="cumulus", password="password")
nvue_end_point = "https://127.0.0.1:8765/nvue_v1"
mime_header = {"Content-Type": "application/json"}
DUMMY_SLEEP = 5 # In seconds
POLL_APPLIED = 1 # in seconds
RETRIES = 10
def print_request(r: requests.Request):
print("=======Request=======")
print("URL:", r.url)
print("Headers:", r.headers)
print("Body:", r.body)
def print_response(r: requests.Response):
print("=======Response=======")
print("Headers:", r.headers)
print("Body:", json.dumps(r.json(), indent=2))
def nvue_action():
r = requests.post(url=nvue_end_point + path,
auth=auth,
verify=False,
data=json.dumps(apply_payload),
headers=mime_header)
print_request(r.request)
print_response(r)
return response
def nvue_get(path):
r = requests.get(url=nvue_end_point + path,
auth=auth,
verify=False)
print_request(r.request)
print_response(r)
if __name__ == "__main__":
payload = {
"@clear":
{
"state": "start",
"parameters": {}
}
}
action_id=nvue_action("/interface/swp1/qos/counter",payload)
time.sleep(DUMMY_SLEEP)
nvue_get(f"/action/{action_id}")
cumulus@switch:~$ nv action clear interface swp1 qos counter
Python 脚本示例
配置示例
在以下 python 示例中,full_config_example()
方法设置系统登录前消息,全局启用 BGP,并在单个批量操作中更改一些其他配置设置。API 端点转到根节点 /nvue_v1
。bridge_config_example()
方法执行针对 /nvue_v1/bridge/domain/<domain-id>
的目标 API 请求,以设置 vlan-vni-offset
属性。
链路操作示例
在以下示例中,get_link_status()
获取作为参数传递的交换机的当前运行状态。link_status_down()
使作为参数传递的 leafs
和 spines
之间的 totalLinks
链路断开。它使用 LLDP 发现邻居交换机,并筛选出非 400G 或 swp 的接口。link_status_up()
将先前断开的 downLinks
链路恢复。
重启示例
在以下示例中,switch_reboot()
重新启动作为参数传递的交换机。 issu_reboot()
在作为参数传递的交换机上触发 ISSU(系统内服务升级),并以定义的 reboot_mode
重新启动交换机。
尝试 API
要试用 NVUE REST API,请使用 NVIDIA Air 上提供的 NVUE API 实验室。 该实验室提供了一个基本示例,帮助您入门。 您也可以尝试本文档中的其他示例。
资源
有关使用 NVUE REST API 的信息,请参阅 NVUE API Swagger 文档。 完整的对象模型下载可在此处获取:此处。
注意事项
- 与 NVUE CLI 不同,NVUE API 不支持为用户帐户配置纯文本密码; 您必须使用 NVUE API 为用户帐户配置哈希密码。
- 如果您需要在交换机上进行多次更新,NVIDIA 建议您使用根补丁,这样可以用更少的往返次数对交换机进行配置更改。 运行许多特定的 NVUE PATCH API 来设置或取消设置对象需要多次往返交换机,以建立 HTTP 连接、传输有效负载和响应、管理网络利用率等等。