01-gRPC配置
本章节下载: 01-gRPC配置 (534.46 KB)
目 录
gRPC(Google Remote Procedure Call,Google远程过程调用)是Google发布的基于HTTP 2.0协议承载的高性能开源软件框架,提供了支持多种编程语言的、对网络设备进行配置和管理的方法。通信双方可以基于该软件框架进行二次开发。
gRPC协议栈分层如表1-1所示。
表1-1 gRPC协议栈分层模型
分层 |
说明 |
内容层 |
业务模块的数据 通信双方需要了解彼此的数据模型,才能正确交互信息 |
Protocol Buffers编码层 |
gRPC通过Protocol Buffers编码格式承载数据 |
gRPC层 |
远程过程调用,定义了远程过程调用的协议交互格式 |
HTTP 2.0层 |
gRPC承载在HTTP 2.0协议上 |
TCP层 |
TCP连接提供面向连接的、可靠的数据链路 |
如图1-1所示,gRPC网络采用客户端/服务器模型,使用HTTP 2.0协议传输报文。
图1-1 gRPC网络架构
gRPC网络的工作机制如下:
(1) 服务器通过监听指定服务端口来等待客户端的连接请求。
(2) 用户通过执行客户端程序登录到服务器。
(3) 客户端调用.proto文件提供的gRPC方法发送请求消息。
(4) 服务器回复应答消息。
H3C设备支持作为gRPC服务器或者gRPC客户端。
.proto文件使用protocol buffers语言编写。protocol buffers是Google开发的数据描述语言,用于自定义数据结构并生成基于各种语言的代码,在序列化和结构化数据方面比XML语言更简单、解析更快。
Telemetry是一项监控设备性能和故障的远程数据采集技术。H3C的Telemetry技术采用gRPC协议将数据从设备推送给网管的采集器。如图1-2所示,网络设备和网管系统建立gRPC连接后,网管可以订阅设备上指定业务模块的数据信息。
图1-2 基于gRPC的Telemetry技术
图1-2中,设备支持以下两种gRPC对接模式:
· Dial-in模式:设备作为gRPC服务器,采集器作为gRPC客户端。由采集器主动向设备发起gRPC连接并订阅需要采集的数据信息。
Dial-in模式适用于小规模网络和采集器需要向设备下发配置的场景。
Dial-in模式支持以下操作:
¡ Get操作:获取设备运行状态和运行配置。
¡ gNMI(gRPC Network Management Interface,gRPC网络管理接口)类操作,具体包括:
- gNMI Capabilities操作:获取设备的能力集。
- gNMI Get操作:获取设备运行状态和运行配置。
- gNMI Set操作:向设备下发配置。
- gNMI Subscribe操作:向设备订阅数据推送服务,包括事件触发类数据和周期采样类数据。该操作实现的功能与gRPC Dial-out模式类似。
¡ CLI操作:向设备下发命令行。
· Dial-out模式:设备作为gRPC客户端,采集器作为gRPC服务器。设备主动和采集器建立gRPC连接,将设备上配置的订阅数据推送给采集器。
Dial-out模式适用于网络设备较多的情况下向采集器提供设备数据信息。
与gRPC相关的协议规范有:
· RFC 7540 - Hypertext Transfer Protocol version 2 (HTTP/2)
仅非FIPS模式支持配置gRPC特性。有关FIPS的介绍,请参见“安全配置指导”中的“FIPS”。
如果执行undo grpc enable命令关闭gRPC功能,所有gRPC相关配置都会被删除。
gRPC Dial-in模式配置任务如下:
(1) 配置gRPC服务
(2) (可选)配置设备与采集器之间的安全通信
(3) 配置gRPC用户
(4) (可选)开启gRPC Dial-in模式的日志功能
(1) 进入系统视图。
system-view
(2) 开启gRPC功能。
grpc enable
缺省情况下,gRPC功能处于关闭状态。
(3) (可选)配置gRPC服务的端口号。
grpc port port-number
缺省情况下,gRPC服务的端口号为50051。
grpc idle-timeout minutes
缺省情况下,gRPC会话超时时间为5分钟。
缺省情况下,设备和采集器建立的gRPC连接是非加密的。配置本功能引用PKI域后,设备和采集器会基于TLS(Transport Layer Security,传输层安全)协议进行通道加密和双向证书认证,从而提高gRPC通信的安全性。
指定的PKI域必须存在,并且PKI域中包含完整的证书和密钥。关于PKI的配置,请参见“安全配置指导”中的“PKI”。
指定PKI域后,gRPC功能将重启,与采集器的连接将短暂断开。采集器需要重新发送连接请求才能继续访问设备。
(1) 进入系统视图。
system-view
(2) 配置设备和采集器建立gRPC连接时引用的PKI域。
grpc pki domain domain-name
缺省情况下,设备和采集器建立gRPC连接时不会引用PKI域。
设备上需要为gRPC客户端创建本地用户,gRPC客户端才能与设备建立gRPC会话。
(1) 进入系统视图。
system-view
(2) 添加设备管理类本地用户。
local-user user-name [ class manage ]
(3) 设置本地用户的密码。
password [ { hash | simple } password ]
缺省情况下,不存在本地用户密码,即本地用户认证时无需输入密码,只要用户名有效且其他属性验证通过即可认证成功。
(4) 配置本地用户的授权用户角色为network-admin。
authorization-attribute user-role user-role
缺省情况下,本地用户的授权用户角色为network-operator。
(5) 配置本地用户可以使用的服务类型为HTTPS服务。
service-type https
缺省情况下,未配置用户的服务类型。
有关local-user、password、authorization-attribute和service-type命令的详细介绍,请参见“安全命令参考”中的“AAA”。
为了管理员定位gRPC问题的需要,可以开启gRPC日志功能,以便记录设备对gRPC报文的处理信息。
设备生成的gRPC日志信息会交给信息中心模块处理,信息中心模块的配置将决定日志信息的发送规则和发送方向。关于信息中心的详细描述请参见“网络管理和监控配置指导”中的“信息中心”。
当设备输出过多的gRPC日志信息而影响设备性能时,可关闭gRPC日志功能。
(1) 进入系统视图。
system-view
(2) 开启gRPC Dial-in模式的日志功能。请至少选择其中一项进行配置。
¡ 开启gRPC Dial-in模式的RPC类操作日志功能。
grpc log dial-in rpc { all | { cli | get }* }
缺省情况下,gRPC Dial-in模式的RPC类操作日志功能处于关闭状态。
¡ 开启gRPC Dial-in模式的gNMI类操作日志功能。
grpc log dial-in gnmi { all | { capabilities | get | set | subscribe }* }
缺省情况下,gRPC Dial-in模式的gNMI Set操作日志功能处于开启状态,其他gNMI类操作日志功能处于关闭状态。
gRPC Dial-out模式配置任务如下:
(1) 开启gRPC功能
(2) (可选)配置设备与采集器之间的安全通信
(3) 配置传感器
(4) 配置采集器
(5) 配置订阅
(6) (可选)开启gRPC Dial-out模式的日志功能
(1) 进入系统视图。
system-view
(2) 开启gRPC功能。
grpc enable
缺省情况下,gRPC功能处于关闭状态。
缺省情况下,设备和采集器建立的gRPC连接是非加密的。配置本功能引用PKI域后,设备和采集器会基于TLS协议进行通道加密和双向证书认证,从而提高gRPC通信的安全性。
指定的PKI域必须存在,并且PKI域中包含完整的证书和密钥。关于PKI的配置,请参见“安全配置指导”中的“PKI”。
指定PKI域后,gRPC功能将重启,与采集器的连接将短暂断开。设备会自动连接采集器。
(1) 进入系统视图。
system-view
(2) 配置设备和采集器建立gRPC连接时引用的PKI域。
grpc pki domain domain-name
缺省情况下,设备和采集器建立gRPC连接时不会引用PKI域。
设备通过传感器完成数据的采集。
采样路径用于指定需要采样的数据源,具体包括以下2种类型:
· 事件触发:传感器组的数据采样没有固定周期,仅由事件触发。关于事件触发类型的采样路径,请参见对应模块的《NETCONF XML API Event Reference》手册。
· 周期采样:传感器组以固定的时间间隔来进行数据采样。关于周期采样类型的采样路径,请参见对应模块的除《NETCONF XML API Event Reference》之外的其他NETCONF XML API手册。
(1) 进入系统视图。
system-view
(2) 进入Telemetry视图。
telemetry
(3) 创建传感器组,并进入传感器组视图。
sensor-group group-name
(4) 配置采样路径。
sensor path path
多次执行本命令可配置多个采样路径。
采集器用于接收网络设备推送的采样数据。设备上需要建立目标组并在目标组中配置正确的采集器地址信息,才能和采集器通信。
建议系统中创建的目标组数量不超过5个,否则会影响系统性能。
如果采集器位于VPN中,请先配置VPN实例,再配置采集器。
(1) 进入系统视图。
system-view
(2) 进入Telemetry视图。
telemetry
(3) 创建目标组,并进入目标组视图。
destination-group group-name
(4) 配置采集器的地址和相关参数。
(IPv4网络)
ipv4-address ipv4-address [ port port-number ] [ vpn-instance vpn-instance-name ]
(IPv6网络)
ipv6-address ipv6-address [ port port-number ] [ vpn-instance vpn-instance-name ]
采集器的IPv6地址不能指定为IPv6链路本地地址。有关IPv6链路本地地址的介绍,请参见“三层技术-IP业务配置指导”中的“IPv6基础”。
多次执行本命令可配置多个采集器。配置本命令时,只要任意一个参数不同,就算不同的采集器。
完成传感器组和目标组的配置后,需要创建订阅并将二者关联,设备才能和目标组中的采集器建立gRPC连接,从而将订阅报文发送给采集器。
(1) 进入系统视图。
system-view
(2) 进入Telemetry视图。
telemetry
(3) 创建订阅,并进入订阅视图。
subscription subscription-name
(4) (可选)配置设备发送的订阅报文的DSCP优先级。
dscp dscp-value
缺省情况下,设备发送的订阅报文的DSCP优先级为0。
DSCP优先级的取值越大,报文的优先级越高。
(5) (可选)配置设备发送订阅报文的源地址。
source-address { ipv4-address | interface interface-type interface-number | ipv6 ipv6-address }
缺省情况下,设备使用路由出接口的主IP地址作为发送订阅报文的源IP地址。
当设备发送订阅报文的源地址发生变化时,设备将会重新连接gRPC服务器。
(6) 配置关联传感器组。
sensor-group group-name [ sample-interval [ msec ] interval ]
当传感器组中的采样路径为事件触发类型时,请不要配置sample-interval参数,否则该采样路径不生效;当采样路径为周期采样类型时,必须配置sample-interval参数才会采样和推送数据。
(7) 配置关联目标组。
destination-group group-name
为了管理员定位gRPC问题的需要,可以开启gRPC日志功能,以便记录设备对gRPC报文的处理信息。
设备生成的gRPC日志信息会交给信息中心模块处理,信息中心模块的配置将决定日志信息的发送规则和发送方向。关于信息中心的详细描述请参见“网络管理和监控配置指导”中的“信息中心”。
当设备输出过多的gRPC日志信息而影响设备性能时,可关闭gRPC日志功能。
(1) 进入系统视图。
system-view
(2) 开启gRPC Dial-out模式的日志功能。
grpc log dial-out { all | { event | sample }* }
缺省情况下,gRPC Dial-out模式的日志功能处于关闭状态。
在完成上述配置后,在任意视图下执行display命令可以显示配置后gRPC Dial-in模式的运行情况,通过查看显示信息验证配置的效果。
表1-2 gRPC显示和维护
操作 |
命令 |
显示gRPC Dial-in模式的相关信息 |
display grpc |
本章节主要描述设备上的命令行配置。采集器上的gRPC对接软件需要另外开发,请参见“2.3 gRPC对接软件二次开发举例”。
如图1-3所示,设备作为gRPC服务器与采集器相连。采集器为gRPC客户端。
通过在设备上配置gRPC Dial-in模式,使gRPC客户端可以订阅设备上的LLDP事件。
图1-3 gRPC Dial-in模式配置组网图
在开始下面的配置之前,假设gRPC服务器与gRPC客户端的IP地址都已配置完毕,并且它们之间路由可达。
(1) 配置Device(gRPC服务器)
# 开启gRPC功能。
<Device> system-view
[Device] grpc enable
# 创建本地用户test,配置该用户的密码为test,授权用户角色为network-admin,可以使用的服务类型为HTTPS服务。
[Device] local-user test
[Device-luser-manage-test] password simple test
[Device-luser-manage-test] authorization-attribute user-role network-admin
[Device-luser-manage-test] service-type https
[Device-luser-manage-test] quit
(2) 配置gRPC客户端
a. 在gRPC客户端安装gRPC环境,具体安装方式请参考相关文档。
b. 获取H3C提供的.proto文件(该文件中已写入订阅LLDP事件的配置),并通过protocol buffers编译器生成特定语言(例如Java、Python、C/C++、Go)的执行代码。
c. 编写客户端程序,调用上一步生成的代码。
d. 执行客户端程序,登录到gRPC服务器。
当设备发生LLDP事件时,gRPC客户端成功收到设备上的订阅信息。
如图1-4所示,设备作为gRPC客户端与采集器相连。采集器为gRPC服务器,接收数据的端口号为50050。
通过在设备上配置gRPC Dial-out模式,使设备以10秒的周期向采集器推送接口模块的设备能力信息。
图1-4 gRPC Dial-out模式配置组网图
在开始下面的配置之前,假设设备与采集器的IP地址都已配置完毕,并且它们之间路由可达。
# 开启gRPC功能。
<Device> system-view
[Device] grpc enable
# 创建传感器组test,并添加采样路径为ifmgr/devicecapabilities。
[Device] telemetry
[Device-telemetry] sensor-group test
[Device-telemetry-sensor-group-test] sensor path ifmgr/devicecapabilities
[Device-telemetry-sensor-group-test] quit
# 创建目标组collector1,并配置IP地址为192.168.2.1、端口号为50050的采集器。
[Device-telemetry] destination-group collector1
[Device-telemetry-destination-group-collector1] ipv4-address 192.168.2.1 port 50050
[Device-telemetry-destination-group-collector1] quit
# 创建订阅A,配置关联传感器组为test,数据采样和推送周期为10秒,关联目标组为collector1。
[Device-telemetry] subscription A
[Device-telemetry-subscription-A] sensor-group test sample-interval 10
[Device-telemetry-subscription-A] destination-group collector1
[Device-telemetry-subscription-A] quit
采集器每10秒收到一次设备推送的数据信息。
Protocol Buffers编码提供了一种灵活、高效、自动序列化结构数据的机制。Protocol Buffers与XML、JSON编码类似,不同之处在于Protocol Buffers是一种二进制编码,性能更高。
表2-1对比了Protocol Buffers和对应的JSON编码格式。
表2-1 Protocol Buffers和对应的JSON编码格式示例
Protocol Buffers编码 |
对应的JSON编码 |
{ 1:“H3C” 2:“H3C” 3:“H3C Simware” 4:“Syslog/LogBuffer” 5:"notification": { "Syslog": { "LogBuffer": { "BufferSize": 512, "BufferSizeLimit": 1024, "DroppedLogsCount": 0, "LogsCount": 100, "LogsCountPerSeverity": { "Alert": 0, "Critical": 1, "Debug": 0, "Emergency": 0, "Error": 3, "Informational": 80, "Notice": 15, "Warning": 1 }, "OverwrittenLogsCount": 0, "State": "enable" } }, "Timestamp": "1527206160022" } } |
{ "producerName": "H3C", "deviceName": "H3C", "deviceModel": "H3C Simware", "sensorPath": "Syslog/LogBuffer", "jsonData": { "notification": { "Syslog": { "LogBuffer": { "BufferSize": 512, "BufferSizeLimit": 1024, "DroppedLogsCount": 0, "LogsCount": 100, "LogsCountPerSeverity": { "Alert": 0, "Critical": 1, "Debug": 0, "Emergency": 0, "Error": 3, "Informational": 80, "Notice": 15, "Warning": 1 }, "OverwrittenLogsCount": 0, "State": "enable" } }, "Timestamp": "1527206160022" } } } |
Protocol Buffers编码通过proto文件描述数据结构,用户可以利用Protoc等工具软件根据proto文件自动生成其他编程语言(例如Java、C++)代码,然后基于这些生成的代码进行二次开发,以实现gRPC设备对接。
H3C为Dial-in模式和Dial-out模式分别提供了proto文件。
Dial-in模式支持以下公共proto文件:
· grpc_service.proto文件:定义了Dial-in模式下的公共RPC方法。
· gnmi.proto文件:定义了gNMI类操作的公共RPC方法。
· gnmi_ext.proto文件:定义了gnmi.proto文件所需的扩展消息结构。
其中gnmi.proto和gnmi_ext.proto文件由Google提供,下载地址请参见“2.2.3 获取proto文件的方法”
grpc_service.proto文件由H3C提供,其内容和含义如下:
syntax = "proto2";
package grpc_service;
message GetJsonReply { //Get方法应答结果
required string result = 1;
}
message SubscribeReply { //订阅结果
required string result = 1;
}
message ConfigReply { //配置结果
required string result = 1;
}
message ReportEvent { //订阅事件结果定义
required string token_id = 1; //登录token_id
required string stream_name = 2; //订阅的事件流名称
required string event_name = 3; //订阅的事件名
required string json_text = 4; //订阅结果json字符串
}
message GetReportRequest{ //获取事件订阅结果请求
required string token_id = 1; //登录成功后的token_id
}
message LoginRequest { //登录请求参数定义
required string user_name = 1; //登录请求用户名
required string password = 2; //登录请求密码
}
message LoginReply { //登录请求应答定义
required string token_id = 1; //登录成功后返回的token_id
}
message LogoutRequest { //退出登录请求参数定义
required string token_id = 1; //token_id
}
message LogoutReply { //退出登录返回结果定义
required string result = 1; //退出登录结果
}
message SubscribeRequest { //定义事件流名称
required string stream_name = 1;
}
message CliConfigArgs { //向设备下发配置命令,并指定命令行参数
required int64 ReqId = 1; //配置命令请求ID
required string cli = 2; //配置命令
}
message CliConfigReply { //设备返回配置命令行执行的结果
required int64 ResReqId = 1; //返回配置命令请求ID,与CliConfigArgs相对应
required string output = 2; //返回配置命令执行输出
required string errors = 3; //标记配置命令执行结果
}
message DisplayCmdArgs { //向设备下发display命令,并指定命令行参数
required int64 ReqId = 1; //display命令请求ID
required string cli = 2; //display命令
}
message DisplayCmdReply { //设备返回display命令行执行的结果
required int64 ResReqId =1; //display命令请求ID,与DisplayCmdArgs相对应
required string output = 2; //返回display命令执行输出
required string errors = 3; //标记display命令执行结果
}
service GrpcService { //定义gRPC方法
rpc Login (LoginRequest) returns (LoginReply) {} //登录方法
rpc Logout (LogoutRequest) returns (LogoutReply) {} //退出登录方法
rpc SubscribeByStreamName (SubscribeRequest) returns (SubscribeReply) {} //订阅事件流
rpc GetEventReport (GetReportRequest) returns (stream ReportEvent) {} //获取事件结果
rpc CliConfig (CliConfigArgs) returns (stream CliConfigReply) {} //gRPC支持通过命令行下发配置命令,并返回执行结果
rpc DisplayCmdTextOutput(DisplayCmdArgs) returns(stream DisplayCmdReply) {} //gRPC支持通过命令行下发display命令,并返回查询结果
}
Dial-in模式支持Device、Ifmgr、IPFW、LLDP、Syslog等业务模块proto文件。
以Device.proto文件为例,该文件定义了Device模块数据的RPC方法,其内容和含义如下:
syntax = "proto2";
import "grpc_service.proto";
package device;
message DeviceBase { //获取设备基本信息结构定义
optional string HostName = 1; //设备的名称
optional string HostOid = 2; //sysoid
optional uint32 MaxChassisNum = 3; //最大框数
optional uint32 MaxSlotNum = 4; //最大slot数
optional string HostDescription = 5; //设备描述信息
}
message DevicePhysicalEntities { //设备物理实体信息
message Entity {
optional uint32 PhysicalIndex = 1; //实体索引
optional string VendorType = 2; //vendor类型
optional uint32 EntityClass = 3;//实体类型
optional string SoftwareRev = 4; //软件版本
optional string SerialNumber = 5; //序列号
optional string Model = 6; //模式
}
repeated Entity entity = 1;
}
service DeviceService { //定义的RPC方法
rpc GetJsonDeviceBase(DeviceBase) returns (grpc_service.GetJsonReply) {} //获取设备基本信息
rpc GetJsonDevicePhysicalEntities(DevicePhysicalEntities) returns (grpc_service.GetJsonReply) {} //获取设备实体信息
}
grpc_dialout.proto文件定义了Dial-out模式下的公共RPC方法,其内容和含义如下:
syntax = "proto2";
package grpc_dialout;
message DeviceInfo{ //推送的设备信息
required string producerName = 1; //厂商名
required string deviceName = 2; //设备的名称
required string deviceModel = 3; //设备型号
}
message DialoutMsg{ //推送的消息格式描述
required DeviceInfo deviceMsg = 1; //DeviceInfo所描述的设备信息
required string sensorPath = 2; //采样路径,对应netconf表的xpath路径
required string jsonData = 3; //采样结果数据(JSON格式字符串)
}
message DialoutResponse{ //采集器(gRPC服务器)返回信息,预留(暂不处理返回值,可填充任意值)
required string response = 1;
}
service GRPCDialout { //推送方法
rpc Dialout(stream DialoutMsg) returns (DialoutResponse);
}
gnmi.proto和gnmi_ext.proto文件的下载地址:
· https://github.com/openconfig/gnmi/tree/master/proto/gnmi/gnmi.proto
· https://github.com/openconfig/gnmi/tree/master/proto/gnmi_ext/gnmi_ext.proto
其他proto文件,请联系H3C技术支持提供。
本举例开发的软件用于实现:
· 采集器获取设备数据(Dial-in模式下的Get操作、gNMI Capabilities操作、gNMI Get操作、gNMI Subscribe操作或Dial-out模式)
· 采集器向设备下发配置(Dial-in模式下的gNMI Set操作或CLI操作)
开发环境为Linux,编程语言以C++为例。
(1) 获取proto文件:
¡ Dial-in模式下的Get操作:需要grpc_service.proto文件和具体业务模块对应的proto文件。
¡ Dial-in模式下的gNMI类操作:需要grpc_service.proto、gnmi.proto和gnmi_ext.proto文件。
¡ Dial-in模式下的CLI操作:需要grpc_service.proto文件。
¡ Dial-out模式:需要grpc_dialout.proto文件。
(2) 获取处理proto文件的工具软件protoc。
下载地址:https://github.com/google/protobuf/releases
(3) 获取对应开发语言的protobuf插件,例如C++插件protobuf-cpp。
下载地址:https://github.com/google/protobuf/releases
生成proto文件对应的C++代码。
将需要的proto文件收集到当前目录下,例如grpc_service.proto和BufferMonitor.proto。
$protoc --plugin=./grpc_cpp_plugin --grpc_out=. --cpp_out=. *.proto
将grpc_dialout.proto文件收集到当前目录下。
$ protoc --plugin=./grpc_cpp_plugin --grpc_out=. --cpp_out=. *.proto
对于Dial-in模式,主要是实现gRPC客户端代码(采集器上使用)。
由于生成的proto代码已经封装好了对应的服务类,只要在编写的客户端代码中调用其RPC方法,客户端就能够向设备(gRPC服务器)发起对应的RPC请求。
客户端代码主要包括以下3部分:
· 进行登录操作,获取token_id。
· 为要发起的RPC方法准备参数,用proto生成的服务类发起RPC调用并解析返回结果。
· 退出登录。
以调用GrpcService和BufferMonitorService服务类为例,编码步骤如下:
(1) 编写一个GrpcServiceTest类。
在这个类中使用由grpc_service.proto生成的GrpcService::Stub类,通过grpc_service.proto自动生成的Login和Logout方法分别完成登录和退出。
class GrpcServiceTest
{
public:
/* 构造函数 */
GrpcServiceTest(std::shared_ptr<Channel> channel): GrpcServiceStub(GrpcService::NewStub(channel)) {}
/* 成员函数 */
int Login(const std::string& username, const std::string& password);
void Logout();
void listen();
Status listen(const std::string& command);
/* 成员变量 */
std::string token;
private:
std::unique_ptr<GrpcService::Stub> GrpcServiceStub; //使用grpc_service.proto生成的GrpcService::Stub类
};
(2) 实现自定义的Login方法。
通过用户输入的用户名,密码调用GrpcService::Stub类的Login方法完成登录。
int GrpcServiceTest::Login(const std::string& username, const std::string& password)
{
LoginRequest request; //设置用户名密码
request.set_user_name(username);
request.set_password(password);
LoginReply reply;
ClientContext context;
//调用登录方法
Status status = GrpcServiceStub->Login(&context, request, &reply);
if (status.ok())
{
std::cout << "login ok!" << std::endl;
std::cout <<"token id is :" << reply.token_id() << std::endl;
token = reply.token_id(); //登录成功,获取到token.
return 0;
}
else{
std::cout << status.error_code() << ": " << status.error_message()
<< ". Login failed!" << std::endl;
return -1;
}
}
(3) 发起对设备的RPC方法请求。
这里以订阅接口丢包事件举例:
rpc SubscribePortQueDropEvent(PortQueDropEvent) returns (grpc_service.SubscribeReply) {}
(4) 编写一个BufMon_GrpcClient类来封装发起的RPC方法。
使用BufferMonitor.proto自动生成的BufferMonitorService::Stub类完成RPC方法的调用。
class BufMon_GrpcClient
{
public:
BufMon_GrpcClient(std::shared_ptr<Channel> channel): mStub(BufferMonitorService::NewStub(channel))
{}
std::string BufMon_Sub_AllEvent(std::string token);
std::string BufMon_Sub_BoardEvent(std::string token);
std::string BufMon_Sub_PortOverrunEvent(std::string token);
std::string BufMon_Sub_PortDropEvent(std::string token);
/* get 表项 */
std::string BufMon_Sub_GetStatistics(std::string token);
std::string BufMon_Sub_GetGlobalCfg(std::string token);
std::string BufMon_Sub_GetBoardCfg(std::string token);
std::string BufMon_Sub_GetNodeQueCfg(std::string token);
std::string BufMon_Sub_GetPortQueCfg(std::string token);
private:
std::unique_ptr<BufferMonitorService::Stub> mStub; //使用BufferMonitor.proto自动生成的类
};
(5) 实现自定义的std::string BufMon_Sub_PortDropEvent(std::string token)方法完成接口丢包事件订阅。
std::string BufMon_GrpcClient::BufMon_Sub_PortDropEvent(std::string token)
{
std::cout << "-------BufMon_Sub_PortDropEvent-------- " << std::endl;
PortQueDropEvent stNodeEvent;
PortQueDropEvent_PortQueDrop* pstParam = stNodeEvent.add_portquedrop();
UINT uiIfIndex = 0;
UINT uiQueIdx = 0;
UINT uiAlarmType = 0;
std::cout<<"Please input interface queue info : ifIndex queIdx alarmtype " << std::endl;
cout<<"alarmtype : 1 for ingress; 2 for egress; 3 for port headroom"<<endl;
std::cin>>uiIfIndex>>uiQueIdx>>uiAlarmType; //设置订阅参数,接口索引等。
pstParam->set_ifindex(uiIfIndex);
pstParam->set_queindex(uiQueIdx);
pstParam->set_alarmtype(uiAlarmType);
ClientContext context;
/* token need add to context */ //设置登录成功后返回的token_id
std::string key = "token_id";
std::string value = token;
context.AddMetadata(key, value);
SubscribeReply reply;
Status status = mStub->SubscribePortQueDropEvent(&context,stNodeEvent,&reply); //调用RPC方法
return reply.result();
}
(6) 循环等待事件上报。
在之前的GrpcServiceTest类中实现此方法,代码如下:
void GrpcServiceTest::listen()
{
GetReportRequest reportRequest;
ClientContext context;
ReportEvent reportedEvent;
/* add token to request */
reportRequest.set_token_id(token);
std::unique_ptr< ClientReader< ReportEvent>> reader(GrpcServiceStub->GetEventReport(&context, reportRequest)); //通过grpc_service.proto自动生成的类的GetEventReport来获取事件信息
std::string streamName;
std::string eventName;
std::string jsonText;
std::string token;
JsonFormatTool jsonTool;
std::cout << "Listen to server for Event" << std::endl;
while(reader->Read(&reportedEvent) ) //读取收到的上报事件
{
streamName = reportedEvent.stream_name();
eventName = reportedEvent.event_name();
jsonText = reportedEvent.json_text();
token = reportedEvent.token_id();
std::cout << "/***********EVENT COME**************/" << std::endl;
std::cout << "TOKEN: " << token << std::endl;
std::cout << "StreamName: "<< streamName << std::endl;
std::cout << "EventName: " << eventName << std::endl;
std::cout << "JsonText without format: " << std::endl << jsonText << std::endl;
std::cout << std::endl;
std::cout << "JsonText Formated: " << jsonTool.formatJson(jsonText) << std::endl;
std::cout << std::endl;
}
Status status = reader->Finish();
std::cout << "Status Message:" << status.error_message() << "ERROR code :" << status.error_code();
}
(7) 这样就完成了Dial-in模式的登录和RPC请求调用,最后调用Logout退出即可。
void GrpcServiceTest:: Logout ()
{
LogoutRequest request;
request.set_token_id(token);
LogoutReply reply;
ClientContext context;
Status status = mStub->Logout(&context, request, &reply);
std::cout << "Logout! :" << reply.result() << std::endl;
}
编码步骤如下:
(1) 编写一个GrpcServiceTest类。
与Get操作相同。
(2) 实现自定义的Login方法。
与Get操作相同。
(3) 发起对设备的RPC方法请求。
rpc Capabilities(CapabilityRequest) returns (CapabilityResponse);
(4) 编写gnmi_client类来封装发起的RPC方法。
class gnmi_client
{
public:
explicit gnmi_client(const std::string &address,const std::string &tokenId);
bool TestCapabilities();
bool TestGet();
bool TestSet();
bool TestSubscribePool();
bool TestSubscribeOnce();
bool TestSubscribeStream();
bool TestSubscribeStreamWithAlias();
private:
void PrintCapabilityResponse(const gnmi::CapabilityResponse &response);
void PrintGetResponse(const gnmi::GetResponse &response);
void PrintSubscribeResponse(const gnmi::SubscribeResponse &response);
void PrintSubscribeRequest(const gnmi::SubscribeRequest &request);
void PrintGetRequest(const gnmi::GetRequest &request);
void PrintSetRequest(const gnmi::SetRequest &request);
void FillGetRequest(gnmi::GetRequest &request);
void FillSetRequest(gnmi::SetRequest &request);
void FillSubscribeRequestByOnce(gnmi::SubscribeRequest &request);
void FillSubscribeRequestByPool(gnmi::SubscribeRequest &request);
void FillSubscribeRequestByStream(gnmi::SubscribeRequest &request);
void FillSubscribePool(gnmi::SubscribeRequest &request);
void FillSubscribeAlias(gnmi::SubscribeRequest &request);
private:
std::unique_ptr<gnmi::gNMI::Stub> mStubGnmiService;
std::string mTokenID;
};
(5) 实现自定义的TestCapabilities ()方法,以获取Capabilities内容。
bool gnmi_client::TestCapabilities()
{
CapabilityRequest request;
CapabilityResponse response;
ClientContext context;
context.AddMetadata("token_id", mTokenID);
std::cout << std::endl << "CapabilitiesRequest => " << std::endl;
Status ret = mStubGnmiService->Capabilities(&context,request,&response);
if( StatusCode::OK != ret.error_code() )
{
cout << "TestCapabilities ErrorMessage:" << ret.error_message() << endl;
return false;
}
else
{
PrintCapabilityResponse(response);
return true;
}
}
(6) 调用Logout退出登录。
与Get操作相同。
编码步骤如下:
(1) 编写一个GrpcServiceTest类。
与Get操作相同。
(2) 实现自定义的Login方法。
与Get操作相同。
(3) 发起对设备的RPC方法请求。
rpc Get(GetRequest) returns (GetResponse);
(4) 编写gnmi_client类来封装发起的RPC方法。
与gNMI Capabilities操作相同。
(5) 以获取Device/Base/HostName内容为例,实现自定义的TestGet方法。
bool gnmi_client::TestGet()
{
GetRequest request;
FillGetRequest(request);
PrintGetRequest(request);
GetResponse response;
ClientContext context;
context.AddMetadata("token_id", mTokenID);
Status ret = mStubGnmiService->Get(&context,request,&response);
if( StatusCode::OK != ret.error_code() )
{
cout << "TestGet ErrorMessage:" << ret.error_message() << endl;
return false;
}
else
{
PrintGetResponse(response);
return true;
}
}
void gnmi_client::FillGetRequest(gnmi::GetRequest &request)
{
auto prefix = request.mutable_prefix();
auto pathelem01 = prefix->add_elem();
pathelem01->set_name("Device");
auto path1 = request.add_path();
auto pathelem02 = path1->add_elem();
pathelem02->set_name("Base");
auto pathelem03 = path1->add_elem();
pathelem03->set_name("HostName");
}
(6) 调用Logout退出登录。
与Get操作相同。
(1) 编写一个GrpcServiceTest类。
与Get操作相同。
(2) 实现自定义的Login方法。
与Get操作相同。
(3) 发起对设备的RPC方法请求。
这里以Device模块为例,实现Set方法:
rpc Set(SetRequest) returns (SetResponse)
(4) 编写gNMITest类来封装发起的RPC方法。
使用gnmi.proto自动生成的gNMI::Stub类完成对RPC方法的调用。
class gNMITest
{
public:
gNMITest(std::shared_ptr<Channel> channel, const std::string tokenId ):
mStubGrpcService(GrpcService::NewStub(channel)),
mStubgNMIService(gNMI::NewStub(channel)),
mTokenID(tokenId ){}
SetResponse TestSetResponseInformation(SetRequest &request, const std::string tokenId);
/*---delete: Device/Base/HostName,将HostName恢复默认值-----------*/
SetResponse DeleteDeviceBaseHostName(); /* delete: Device Base/HostName*/
/* update: Device/Base/HostName, string_val("string_hostname") */
SetResponse UpdateDeviceBaseHostNameStringVal();
/* replace: Device/Base/HostName, string_val("string_hostname") */
REPL_001 SetResponse ReplaceDeviceBaseHostNameStringVal();
private:
std::unique_ptr<GrpcService::Stub> mStubGrpcService;
std::unique_ptr<gNMI::Stub> mStubgNMIService;
std::string mTokenID;
};
(5) 实现自定义的方法以完成对业务模块数据(以Device为例)的Set操作。
// 调用Set方法,实现client与server通信,并返回response。
SetResponse gNMITest::TestSetResponseInformation(SetRequest &request, const std::string tokenId)
{
SetResponse reply;
ClientContext context;
context.AddMetadata("token_id", tokenId);
/* 调用Set方法 */
Status ret = mStubgNMIService->Set(&context,request,&reply);
if( StatusCode::OK != ret.error_code())
{
std::cout<<"error: "<<ret.error_message()<<std::endl;
}
return reply;
}
// Delete操作
SetResponse gNMITest:: DeleteDeviceBaseHostName () /* prefix == Device/Base */
{
SetRequest request;
/* SetRequest->prefix */
Path *path01 = request.mutable_prefix();
PathElem *pathelem01 = path01->add_elem();
pathelem01->set_name("Device");
PathElem *pathelem02 = path01->add_elem();
pathelem02->set_name("Base");
/* SetRequest->delete */
Path *path02 = request.add_delete_();
PathElem *pathelem03 = path02->add_elem();
pathelem03->set_name("HostName");
/* gather response info. */
return TestSetResponseInformation(request, mTokenID);
}
// Update操作
SetResponse gNMITest::UpdateDeviceBaseHostNameStringVal()
{
SetRequest request;
/* SetRequest->prefix */
Path *path01 = request.mutable_prefix();
PathElem *pathelem01 = path01->add_elem();
pathelem01->set_name("Device");
PathElem *pathelem02 = path01->add_elem();
pathelem02->set_name("Base");
/* SetRequest->update */
Update *update01 = request.add_update();
Path *path02 = update01->mutable_path();
PathElem *pathelem03 = path02->add_elem();
pathelem03->set_name("HostName");
TypedValue *typevalue01 = update01->mutable_val();
typevalue01->set_string_val("string_hostname");
/* gather response info. */
return TestSetResponseInformation(request, mTokenID);
}
// Replace操作
SetResponse gNMITest::ReplaceDeviceBaseHostNameStringVal()
{
SetRequest request;
/* SetRequest->prefix */
Path *path01 = request.mutable_prefix();
PathElem *pathelem01 = path01->add_elem();
pathelem01->set_name("Device");
PathElem *pathelem02 = path01->add_elem();
pathelem02->set_name("Base");
/* SetRequest->replace */
Update *replace01 = request.add_replace();
Path *path02 = replace01->mutable_path();
PathElem *pathelem03 = path02->add_elem();
pathelem03->set_name("HostName");
TypedValue *typevalue01 = replace01->mutable_val();
typevalue01->set_string_val("string_hostname");
/* gather response info. */
return TestSetResponseInformation(request, mTokenID);
}
(6) 调用Logout退出登录。
与Get操作相同。
编码步骤如下:
(1) 编写一个GrpcServiceTest类。
与Get操作相同。
(2) 实现自定义的Login方法。
与Get操作相同。
(3) 发起对设备的RPC方法请求。
rpc Subscribe(stream SubscribeRequest) returns (stream SubscribeResponse);
(4) 编写gnmi_client类来封装发起的RPC方法。
与gNMI Capabilities操作相同。
(5) 实现自定义的订阅方法。
void gnmi_client::FillSubscribeRequestByOnce(gnmi::SubscribeRequest &request)
{
auto subscribeList = request.mutable_subscribe();
auto prefix = subscribeList->mutable_prefix();
auto pathelem01 = prefix->add_elem();
pathelem01->set_name("LLDP");
auto subscribe = subscribeList->add_subscription();
auto path = subscribe->mutable_path();
auto pathelem02 = path->add_elem();
pathelem02->set_name("NeighborEvent");
auto pathelem03 = path->add_elem();
pathelem03->set_name("Neighbor");
(*pathelem03->mutable_key())["IfName"] = "xxx";
subscribeList->set_mode(::gnmi::SubscriptionList_Mode_ONCE);
subscribeList->set_encoding(::gnmi::JSON);
}
void gnmi_client::FillSubscribeRequestByPool(gnmi::SubscribeRequest &request)
{
auto subscribeList = request.mutable_subscribe();
auto prefix = subscribeList->mutable_prefix();
auto pathelem01 = prefix->add_elem();
pathelem01->set_name("Device");
auto subscribe = subscribeList->add_subscription();
auto path = subscribe->mutable_path();
auto pathelem02 = path->add_elem();
pathelem02->set_name("CPUs");
auto pathelem03 = path->add_elem();
pathelem03->set_name("CPU");
auto pathelem04 = path->add_elem();
pathelem04->set_name("CPUUsage");
subscribeList->set_mode(::gnmi::SubscriptionList_Mode_POLL);
subscribeList->set_encoding(::gnmi::JSON);
}
void gnmi_client::FillSubscribeRequestByStream(gnmi::SubscribeRequest &request)
{
auto subscribeList = request.mutable_subscribe();
auto prefix = subscribeList->mutable_prefix();
auto pathelem01 = prefix->add_elem();
pathelem01->set_name("Diagnostic");
auto subscribe = subscribeList->add_subscription();
auto path = subscribe->mutable_path();
auto pathelem02 = path->add_elem();
pathelem02->set_name("CPUEvent");
auto pathelem03 = path->add_elem();
pathelem03->set_name("CPU");
(*pathelem03->mutable_key())["Chassis#condition"] = "equal:1";
subscribe->set_mode(::gnmi::ON_CHANGE);
subscribe->set_sample_interval(1000);
subscribe->set_suppress_redundant(false);
subscribe->set_heartbeat_interval(1000);
subscribeList->set_mode(::gnmi::SubscriptionList_Mode_STREAM);
subscribeList->set_encoding(::gnmi::JSON);
}
void gnmi_client::FillSubscribeAlias(gnmi::SubscribeRequest &request)
{
auto aliases = request.mutable_aliases();
auto alias = aliases->add_alias();
auto path = alias->mutable_path();
auto pathelem01 = path->add_elem();
pathelem01->set_name("Device");
auto pathelem02 = path->add_elem();
pathelem02->set_name("CPUs");
auto pathelem03 = path->add_elem();
pathelem03->set_name("CPU");
auto pathelem04 = path->add_elem();
pathelem04->set_name("CPUUsage");
alias->set_alias("#cpu_usage");
}
(6) 调用Logout退出登录。
与Get操作相同。
(1) 编写一个GrpcServiceTest类。
与Get操作相同。
(2) 实现自定义的Login方法。
与Get操作相同。
(3) 发起对设备的RPC方法请求。
这里使用grpc_service.proto文件中的CliConfig方法:
rpc CliConfig (CliConfigArgs) returns (stream CliConfigReply) {}
(4) 使用GrpcServiceTest类封装发起的RPC请求。
见Get操作中的GrpcServiceTest类。
(5) 实现自定义的方法以支持CliConfig操作。
// make a thread to listen the sever and get message
Status GrpcServiceTest::listen(const std::string& command)
{
CliConfigArgs reportRequest;
ClientContext context;
CliConfigReply reportedEvent;
std::string key = "token_id";
std::string value = token;
context.AddMetadata(key, value);
/* add token to request */
reportRequest.set_reqid(12345678);
reportRequest.set_cli(command);
std::unique_ptr< ClientReader< CliConfigReply>> reader(mStub->CliConfig(&context, reportRequest));
std::string streamName;
std::string output;
int64 resreqid;
std::cout << "Command result" << std::endl;
while( reader->Read(&reportedEvent) )
{
streamName = reportedEvent.errors();
output = reportedEvent.output();
resreqid = reportedEvent.resreqid();
std::cout << "resreqid: "<< resreqid << std::endl;
std::cout << "errors: "<< streamName << std::endl;
std::cout << "output: \n"<< output << "\n"<< std::endl;
}
Status status = reader->Finish();
return status;
}
(6) 循环等待下发命令。
在main函数中实现,并在下发命令之后返回命令执行结果(在GrpcServiceTest类中实现),main函数代码如下:
int main(int argc, char *argv[])
{
const char *cmd;
unsigned int i = 0;
unsigned int cycle = 0;
if (4 == argc)
{
g_server_address = argv[1];
g_username = argv[2];
g_password = argv[3];
std::cout << "server_address: " << g_server_address <<std::endl;
std::cout << "username: " << g_username << " " << "password: " << g_password << std::endl;
auto channel = grpc::CreateChannel(g_server_address,grpc::InsecureChannelCredentials());
// 1. login
GrpcServiceTest reporter(channel);
if(0 != reporter.Login(g_username, g_password))
{
return 0;
}
while(1)
{// 2.循环读取命令并执行
std::cout<<"\n\nPlease Input Command:\n";
getline(std::cin,g_command); // 获取下发的命令
Status status = reporter.listen(g_command);
if (!status.ok())
{
std::cout << status.error_code() << ": " << status.error_message() << std::endl;
break;
}
}
}
std::cout<<"Complete exec Command."<<std::endl;
return 0;
}
(7) 调用Logout退出登录。
与Get操作相同。
对于Dial-out模式,主要是实现服务端代码,使采集器(gRPC服务器)接收从设备上获取到的数据并进行解析。
服务端代码主要包括以下2部分:
· 继承自动生成的GRPCDialout::Service类,重载自动生成的RPC服务Dialout,并完成解析,获得相应字段内容。
· 将RPC服务注册到指定监听端口上。
编码步骤如下:
(1) 继承并重载RPC服务Dialout。
新建一个类DialoutTest并继承GRPCDialout::Service。
class DialoutTest final : public GRPCDialout::Service { //重载自动生成的抽象类
Status Dialout(ServerContext* context, ServerReader< DialoutMsg>* reader, DialoutResponse* response) override; //实现Dialout RPC方法
};
(2) 将DialoutTest服务注册为gRPC服务,并指定监听端口。
using grpc::Server;
using grpc::ServerBuilder;
std::string server_address("0.0.0.0:60057"); //指定要监听的地址和端口
DialoutTest dialout_test; //定义(1)中声明的对象
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());//添加监听
builder.RegisterService(&dialout_test); //注册服务
std::unique_ptr<Server> server(builder.BuildAndStart()); //启动服务
server->Wait();
(3) 实现Dialout方法,实现数据解析。
Status DialoutTest::Dialout(ServerContext* context, ServerReader< DialoutMsg>* reader, DialoutResponse* response)
{
DialoutMsg msg;
while( reader->Read(&msg))
{
const DeviceInfo &device_msg = msg.devicemsg();
std::cout<< "Producer-Name: " << device_msg.producername() << std::endl;
std::cout<< "Device-Name: " << device_msg.devicename() << std::endl;
std::cout<< "Device-Model: " << device_msg.devicemodel() << std::endl;
std::cout<<"Sensor-Path: " << msg.sensorpath()<<std::endl;
std::cout<<"Json-Data: " << msg.jsondata()<<std::endl;
std::cout<<std::endl;
}
response->set_response("test");
return Status::OK;
}
(4) 通过Read方法获取到proto文件生成的DialoutMsg对象后,可以调用对应的方法获取相应的字段值。
gNMI类操作中添加列的方法如表2-2所示。
表2-2 gNMI类操作中添加列的方法
请求描述 |
请求示例 |
客户端代码示例 |
对key中的列赋值 |
path: |
auto pathelem01 = path->add_elem(); |
对key中的列属性赋值 |
path: |
auto pathelem01 = path->add_elem(); |
对key中的分组的列赋值 |
path: |
auto pathelem01 = path->add_elem(); |
对key中的分组的列的属性赋值 |
path: |
auto pathelem01 = path->add_elem(); |
通过key对列属性赋值 |
path: |
auto pathelem01 = path->add_elem(); |
对于JSON中的列和属性赋值 |
{ |
{ |
不同YANG文件中存在同名的顶层节点时,可以在顶层的同名节点名之前加上YANG模型名来精确指定该节点;如果不加YANG模型名,则自动匹配第一个找到的同名节点 例如: 在节点interfaces前加上YANG模型名oc-if,表示指定OpenConfig组织发布的YANG文件中的interfaces节点 |
path: |
auto pathelem01 = path->add_elem(); |
gNMI Get操作支持的属性如下:
· regExp(正则表达式)
当过滤条件比较复杂时,可以在指定列上设置regExp属性为一个正则表达式,以完成过滤的目的。如下示例用于获取接口的描述信息,并要求这些描述信息全部为大写字母,不能有其他字符。
auto prefix = request.mutable_prefix();
auto pathelem01 = prefix->add_elem();
pathelem01->set_name("Ifmgr");
auto path1 = request.add_path();
auto pathelem02 = path1->add_elem();
pathelem02->set_name("Interfaces");
auto pathelem03 = path1->add_elem();
pathelem03->set_name("Interface");
auto pathelem04 = path1->add_elem();
pathelem04->set_name("Description");
(*pathelem04->mutable_key())["#regExp"] = "^[A-Z]*$";
· match(条件匹配)
条件匹配命令如表2-3所示。
操作 |
命令 |
说明 |
大于 |
match="more:value" |
值大于value,支持的数据类型为:日期、数字、字符串 |
小于 |
match="less:value" |
值小于value,支持的数据类型为:日期、数字、字符串 |
不小于 |
match="notLess:value" |
值不小于value,支持的数据类型为:日期、数字、字符串 |
不大于 |
match="notMore:value" |
值不大于value,支持的数据类型为:日期、数字、字符串 |
等于 |
match="equal:value" |
值等于value,支持的数据类型为:日期、数字、字符串、OID、BOOL |
不等于 |
match="notEqual:value" |
值不等于value,支持的数据类型为:日期、数字、字符串、OID、BOOL |
包含 |
match="include:string" |
包含字符串string,支持的数据类型为:字符串 |
不包含 |
match="exclude:string" |
不能包含字符串string,支持的数据类型为:字符串 |
开始于 |
match="startWith:string" |
以字符串string开头,支持的数据类型为:字符串、OID |
结束于 |
match="endWith:string" |
以字符串string结束,支持的数据类型为:字符串 |
如下示例用于获取实体扩展信息中CPU利用率大于50%的实体。
auto prefix = request.mutable_prefix();
auto pathelem01 = prefix->add_elem();
pathelem01->set_name("Device");
auto path1 = request.add_path();
auto pathelem02 = path1->add_elem();
pathelem02->set_name("ExtPhysicalEntities");
auto pathelem03 = path1->add_elem();
pathelem03->set_name("Entity");
auto pathelem04 = path1->add_elem();
pathelem04->set_name("CpuUsage");
(*pathelem04->mutable_key())["#match"] = "more:50";
· valuetype(属性值类型)
获取接口数据时,如果IfIndex、vrfindex列的值为数字,设备无法识别该值为名称类型还是索引类型。此时,用户可以使用valuetype指定该值的类型。valuetype取值为:
属性 |
说明 |
name |
值为名称类型 |
index |
值为索引类型 |
auto |
设备先按名称类型进行匹配,如果没有匹配到任何信息,再按照索引类型进行匹配 如果不指定valuetype的属性,缺省使用auto |
如下示例中,IfIndex属性值为1,属性值类型为index:
auto prefix = request.mutable_prefix();
auto pathelem01 = prefix->add_elem();
pathelem01->set_name("VLAN");
auto path1 = request.add_path();
auto pathelem02 = path1->add_elem();
pathelem02->set_name("TrunkInterfaces");
auto pathelem03 = path1->add_elem();
pathelem03->set_name("Interface");
(*pathelem03->mutable_key())["IfIndex"] = "1";
(*pathelem03->mutable_key())["IfIndex#valuetype"] = "index";
· count
该属性用于获取指定数量的数据项,例如运行状态、运行配置信息。
如下为一个携带了count属性的示例:
auto prefix = request.mutable_prefix();
auto pathelem01 = prefix->add_elem();
pathelem01->set_name("Syslog");
auto path1 = request.add_path();
auto pathelem02 = path1->add_elem();
pathelem02->set_name("Logs");
(*pathelem03->mutable_key())["count"] = "5";
auto pathelem03 = path1->add_elem();
pathelem03->set_name("Log");
(*pathelem03->mutable_key())["Index"] = "10";
· 值过滤
在Get操作中,对表中的列直接指定值,设备将对这些值进行严格匹配。如果指定了多个列的值,则返回同时符合这几个条件的数据。
如下为匹配IfIndex=1的示例:
auto prefix = request.mutable_prefix();
auto pathelem01 = prefix->add_elem();
pathelem01->set_name("VLAN");
auto path1 = request.add_path();
auto pathelem02 = path1->add_elem();
pathelem02->set_name("TrunkInterfaces");
auto pathelem03 = path1->add_elem();
pathelem03->set_name("Interface");
(*pathelem03->mutable_key())["IfIndex"] = "1";
gNMI Set支持的属性如下:
· incremental
该属性作用于集合性质的列,例如vlan permitlist列表。XML请求中有增量下发选项时,最终执行结果不影响本列原有的数据。
例如,下发一个接口的VLAN配置,使用增量下发,262接口原有Untagged VLAN列表为12~15,下发后为1~10,12~15。请求如下:
Path *path01 = request.mutable_prefix();
PathElem *pathelem01 = path01->add_elem();
pathelem01->set_name("VLAN");
PathElem *pathelem02 = path01->add_elem();
pathelem02->set_name("HybridInterfaces");
Update *Update01 = request.add_update();
Path *path02 = Update01->mutable_path();
PathElem *pathelem03 = path02->add_elem();
pathelem03->set_name("Interface");
(*pathelem03->mutable_key())["IfName"] = "262";
(*pathelem03->mutable_key())["UntaggedVlanList#incremental"] = "true";
(*pathelem03->mutable_key())["UntaggedVlanList"] = "1-10";
· valuetype
配置接口数据时,如果IfIndex和vrfindex列的值为数字,设备无法识别该值为名称类型还是索引类型。此时,用户可以使用valuetype指定该值的类型。valuetype取值为:
属性 |
说明 |
name |
值为名称类型 |
index |
值为索引类型 |
auto |
设备先按名称类型进行匹配,如果没有匹配到任何信息,再按照索引类型进行匹配 如果不指定valuetype的属性,缺省使用auto |
下面以IfIndex列值为index类型,值为1为例:
Path *path01 = request.mutable_prefix();
PathElem *pathelem01 = path01->add_elem();
pathelem01->set_name("VLAN");
PathElem *pathelem02 = path01->add_elem();
pathelem02->set_name("TrunkInterfaces");
/* SetRequest->delete */
Path *path02 = request.add_delete_();
PathElem *pathelem04 = path02->add_elem();
pathelem04->set_name("Interface ");
(*pathelem04->mutable_key())["IfIndex"] = "1";
(*pathelem04->mutable_key())["IfIndex#valuetype"] = "index";
gNMI Subscribe支持的属性如下:
· gNMI Get操作支持的所有属性,请参见“gNMI Get操作支持的属性”
这些属性可用于周期式上报的数据表的订阅。
· condition(数据推送条件)
该属性用于事件表的订阅,可选择的属性值包括:
¡ more:大于
¡ less:小于
¡ notLess:不小于
¡ notMore:不大于
¡ equal:等于
¡ notEqual:不等于
¡ include:包含
¡ exclude:不包含
¡ startWith:开始于
¡ endWith:结束于
如下示例用于监控LLDP事件:
auto prefix = subscribeList->mutable_prefix();
auto pathelem01 = prefix->add_elem();
pathelem01->set_name("LLDP");
auto subscribe = subscribeList->add_subscription();
auto path = subscribe->mutable_path();
auto pathelem02 = path->add_elem();
pathelem02->set_name("NeighborEvent");
auto pathelem03 = path->add_elem();
pathelem03->set_name("Neighbor");
(*pathelem03->mutable_key())["IfName"] = "Ten-GigabitEthernet1/0/1";
(*pathelem03->mutable_key())["IfName#condition"] = "equal";
· match(条件匹配)
该属性用于数据表的订阅,有以下两种使用方法:
¡ 与固定值进行条件匹配:支持的匹配命令如表2-3所示。
¡ 与其他列数据进行条件匹配(仅触发式上报的数据表支持)。可选择的属性值包括:
- refMore:大于
- refLess:小于
- refNotLess:不小于
- refNotMore:不大于
- refEqual:等于
- refNotEqual:不等于
下面以current-usage列值大于alarm-threshold列值触发条件为例:
auto subscribe = subscribeList->add_subscription();
auto path = subscribe->mutable_path();
auto pathelem02 = path->add_elem();
pathelem02->set_name("queue");
auto pathelem03 = path->add_elem();
pathelem03->set_name("state");
(*pathelem03->mutable_key())["current-usage#match"] = "refMore:alarm-threshold";
· select
该属性用于在触发式上报的数据表的订阅中选择列,表示仅订阅指定的列数据。
下面以订阅current-usage和available-usage为例:
auto subscribe = subscribeList->add_subscription();
auto path = subscribe->mutable_path();
auto pathelem02 = path->add_elem();
pathelem02->set_name("queue");
auto pathelem03 = path->add_elem();
pathelem03->set_name("state");
(*pathelem03->mutable_key())["current-usage#select"] = "";
(*pathelem03->mutable_key())["available-usage#select"] = "";
gNMI类操作对象包括数据表和事件表:
· 数据表有两种类型:周期式上报和触发式上报。
· 事件表均为触发式上报。
不同款型规格的资料略有差异, 详细信息请向具体销售和400咨询。H3C保留在没有任何通知或提示的情况下对资料内容进行修改的权利!