• 产品与解决方案
  • 行业解决方案
  • 服务
  • 支持
  • 合作伙伴
  • 新华三人才研学中心
  • 关于我们

12-Telemetry配置指导

目录

01-gRPC配置

本章节下载 01-gRPC配置  (367.39 KB)

01-gRPC配置


1 gRPC

1.1  gRPC简介

gRPC(Google Remote Procedure Call,Google远程过程调用)是Google发布的基于HTTP 2.0传输层协议承载的高性能开源软件框架,提供了支持多种编程语言的、对网络设备进行配置和管理的方法。通信双方可以基于该软件框架进行二次开发。

1.1.1  gRPC协议

gRPC协议栈分层如表1-1所示。

表1-1 gRPC协议栈分层模型

分层

说明

内容层

业务模块的数据

通信双方需要了解彼此的数据模型,才能正确交互信息

Protocol Buffers编码层

gRPC通过Protocol Buffers编码格式承载数据

gRPC层

远程过程调用,定义了远程过程调用的协议交互格式

HTTP 2.0层

gRPC承载在HTTP 2.0协议上

TCP层

TCP连接提供面向连接的、可靠的数据链路

 

1.1.2  gRPC网络架构

图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语言更简单、解析更快。

 

1.1.3  基于gRPC的Telemetry技术

Telemetry是一项监控设备性能和故障的远程数据采集技术。H3C的Telemetry技术采用gRPC协议将数据从设备推送给网管的采集器。如图1-2所示,网络设备和网管系统建立gRPC连接后,网管可以订阅设备上指定业务模块的数据信息。

图1-2 基于gRPC的Telemetry技术

 

1.1.4  Dial-in模式和Dial-out模式

图1-2中,设备支持以下两种gRPC对接模式:

·     Dial-in模式:设备作为gRPC服务器,采集器作为gRPC客户端。由采集器主动向设备发起gRPC连接并订阅需要采集的数据信息。

Dial-in模式适用于小规模网络和采集器需要向设备下发配置的场景。

Dial-in模式支持以下操作:

¡     Get操作:获取设备运行状态和运行配置。

¡     Set操作:向设备下发配置。

¡     CLI操作:向设备下发命令行。

·     Dial-out模式:设备作为gRPC客户端,采集器作为gRPC服务器。设备主动和采集器建立gRPC连接,将设备上配置的订阅数据推送给采集器。

Dial-out模式适用于网络设备较多的情况下向采集器提供设备数据信息。

1.1.5  协议规范

与gRPC相关的协议规范有:

·     RFC 7540 - Hypertext Transfer Protocol version 2 (HTTP/2)

1.2  gRPC配置限制和指导

仅非FIPS模式支持配置gRPC特性。有关FIPS的介绍,请参见“安全配置指导”中的“FIPS”。

1.3  配置gRPC Dial-in模式

1.3.1  gRPC Dial-in模式配置任务简介

gRPC Dial-in模式配置任务如下:

(1)     配置gRPC服务

(2)     配置gRPC用户

1.3.2  配置gRPC服务

1. 配置限制和指导

如果开启gRPC功能时提示失败,请通过display tcpdisplay ipv6 tcp命令查看gRPC服务端口号是否被占用。如果已被占用,请修改gRPC服务端口号,再重新执行grpc enable命令。

有关display tcp命令的介绍,请参见“三层技术-IP业务命令参考”中的“IP性能优化”。有关display ipv6 tcp命令的介绍,请参见“三层技术-IP业务命令参考”中的“IPv6基础”。

2. 配置步骤

(1)     进入系统视图。

system-view

(2)     (可选)配置gRPC服务的端口号。

grpc port port-number

缺省情况下,gRPC服务的端口号为50051。

(3)     开启gRPC功能。

grpc enable

缺省情况下,gRPC功能处于关闭状态。

(4)     (可选)配置gRPC会话超时时间。

grpc idle-timeout minutes

缺省情况下,gRPC会话超时时间为5分钟。

1.3.3  配置gRPC用户

1. 功能简介

设备上需要为gRPC客户端创建本地用户,gRPC客户端才能与设备建立gRPC会话。

2. 配置步骤

(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-userpasswordauthorization-attributeservice-type命令的详细介绍,请参见“安全命令参考”中的“AAA”。

1.4  配置gRPC Dial-out模式

1.4.1  gRPC Dial-out模式配置任务简介

gRPC Dial-out模式配置任务如下:

(1)     开启gRPC功能

(2)     配置传感器

(3)     配置采集器

(4)     配置订阅

1.4.2  开启gRPC功能

(1)     进入系统视图。

system-view

(2)     开启gRPC功能。

grpc enable

缺省情况下,gRPC功能处于关闭状态。

1.4.3  配置传感器

1. 功能简介

设备通过传感器完成数据的采集。

采样路径用于指定需要采样的数据源,具体包括以下2种类型:

·     事件触发:传感器组的数据采样没有固定周期,仅由事件触发。关于事件触发类型的采样路径,请参见对应模块的《NETCONF XML API Event Reference》手册。

·     周期采样:传感器组以固定的时间间隔来进行数据采样。关于周期采样类型的采样路径,请参见对应模块的除《NETCONF XML API Event Reference》之外的其他NETCONF XML API手册。

2. 配置步骤

(1)     进入系统视图。

system-view

(2)     进入Telemetry视图。

telemetry

(3)     创建传感器组,并进入传感器组视图。

sensor-group group-name

(4)     配置采样路径。

sensor path path

多次执行本命令可配置多个采样路径。

1.4.4  配置采集器

1. 功能简介

采集器用于接收网络设备推送的采样数据。设备上需要建立目标组并在目标组中配置正确的采集器地址信息,才能和采集器通信。

2. 配置限制和指导

建议系统中创建的目标组数量不超过5个,否则会影响系统性能。

3. 配置步骤

(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基础”。

多次执行本命令可配置多个采集器。配置本命令时,只要任意一个参数不同,就算不同的采集器。

1.4.5  配置订阅

1. 功能简介

完成传感器组和目标组的配置后,需要创建订阅并将二者关联,设备才能和目标组中的采集器建立gRPC连接,从而将订阅报文发送给采集器。

2. 配置步骤

(1)     进入系统视图。

system-view

(2)     进入Telemetry视图。

telemetry

(3)     创建订阅,并进入订阅视图。

subscription subscription-name

(4)     (可选)配置设备发送订阅报文的源地址。

source-address { ipv4-address | interface interface-type interface-number | ipv6 ipv6-address }

缺省情况下,设备使用路由出接口的主IP地址作为发送订阅报文的源IP地址。

当设备发送订阅报文的源地址发生变化时,设备将会重新连接gRPC服务器。

(5)     配置关联传感器组。

sensor-group group-name [ sample-interval interval ]

当传感器组中的采样路径为事件触发类型时,请不要配置sample-interval参数,否则该采样路径不生效;当采样路径为周期采样类型时,必须配置sample-interval参数才会采样和推送数据。

(6)     配置关联目标组。

destination-group group-name

1.5  gRPC显示和维护

在完成上述配置后,在任意视图下执行display命令可以显示配置后gRPC Dial-in模式的运行情况,通过查看显示信息验证配置的效果。

表1-2 gRPC显示和维护

操作

命令

显示gRPC Dial-in模式的相关信息

display grpc

 

1.6  gRPC典型配置举例

本章节主要描述设备上的命令行配置。采集器上的gRPC对接软件需要另外开发,请参见“2.3  gRPC对接软件二次开发举例”。

1.6.1  gRPC Dial-in模式配置举例

1. 组网需求

图1-3所示,设备作为gRPC服务器与采集器相连。采集器为gRPC客户端。

通过在设备上配置gRPC Dial-in模式,使gRPC客户端可以订阅设备上的LLDP事件。

2. 组网图

图1-3 gRPC Dial-in模式配置组网图

 

3. 配置步骤

在开始下面的配置之前,假设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服务器。

4. 验证配置

当设备发生LLDP事件时,gRPC客户端成功收到设备上的订阅信息。

1.6.2  gRPC Dial-out模式配置举例

1. 组网需求

图1-4所示,设备作为gRPC客户端与采集器相连。采集器为gRPC服务器,接收数据的端口号为50050。

通过在设备上配置gRPC Dial-out模式,使设备以10秒的周期向采集器推送接口模块的设备能力信息。

2. 组网图

图1-4 gRPC Dial-out模式配置组网图

 

3. 配置步骤

在开始下面的配置之前,假设设备与采集器的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

4. 验证配置

采集器每10秒收到一次设备推送的数据信息。

 

 


2 Protocol Buffers编码介绍

2.1  Protocol Buffers编码格式

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"

}

}

}

 

2.2  proto文件介绍

Protocol Buffers编码通过proto文件描述数据结构,用户可以利用Protoc等工具软件根据proto文件自动生成其他编程语言(例如Java、C++)代码,然后基于这些生成的代码进行二次开发,以实现gRPC设备对接。

H3C为Dial-in模式和Dial-out模式分别提供了proto文件。

2.2.1  Dial-in模式的proto文件

1. 公共proto文件

Dial-in模式支持以下公共proto文件:

·     grpc_service.proto文件:定义了Dial-in模式下的公共RPC方法。

·     gnmi.proto文件:定义了Set操作的公共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命令,并返回查询结果

}

2. 业务模块proto文件

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) {} //获取设备实体信息

}

2.2.2  Dial-out模式的proto文件

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);

}

2.2.3  获取proto文件的方法

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技术支持提供。

2.3  gRPC对接软件二次开发举例

本举例开发的软件用于实现:

·     采集器获取设备数据(Dial-in模式下的Get操作,或Dial-out模式)

·     采集器向设备下发配置(Dial-in模式下的Set操作或CLI操作)

开发环境为Linux,编程语言以C++为例。

2.3.1  准备工作

(1)     获取proto文件:

¡     Dial-in模式下的Get操作:需要grpc_service.proto文件和具体业务模块对应的proto文件。

¡     Dial-in模式下的Set操作:需要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

2.3.2  生成代码

生成proto文件对应的C++代码。

1. Dial-in模式

将需要的proto文件收集到当前目录下,例如grpc_service.proto和BufferMonitor.proto。

$protoc --plugin=./grpc_cpp_plugin  --grpc_out=. --cpp_out=. *.proto

2. Dial-out模式

将grpc_dialout.proto文件收集到当前目录下。

$ protoc --plugin=./grpc_cpp_plugin  --grpc_out=.  --cpp_out=. *.proto

2.3.3  开发代码

1. Dial-in模式下的Get操作

对于Dial-in模式,主要是实现gRPC客户端代码(采集器上使用)。

由于生成的proto代码已经封装好了对应的服务类(这里以GrpcService和BufferMonitorService为例),只要在编写的客户端代码中调用其RPC方法,客户端就能够向设备(gRPC服务器)发起对应的RPC请求。

客户端代码主要包括以下3部分:

·     进行登录操作,获取token_id。

·     为要发起的RPC方法准备参数,用proto生成的服务类发起RPC调用并解析返回结果。

·     退出登录。

编码步骤如下:

(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;

}

2. Dial-in模式下的Set操作

(1)     编写一个GrpcServiceTest类。

与Dial-in模式下的Get操作相同。

(2)     实现自定义的Login方法。

与Dial-in模式下的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退出登录。

与Dial-in模式下的Get操作相同。

3. Dial-in模式下的CLI操作

(1)     编写一个GrpcServiceTest类。

与Dial-in模式下的Get操作相同。

(2)     实现自定义的Login方法。

与Dial-in模式下的Get操作相同。

(3)     发起对设备的RPC方法请求。

这里使用grpc_service.proto文件中的CliConfig方法:

rpc CliConfig (CliConfigArgs)  returns (stream CliConfigReply) {}

(4)     使用GrpcServiceTest类封装发起的RPC请求。

见Dial-in模式下的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退出登录。

与Dial-in模式下的Get操作相同。

4. Dial-out模式

对于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对象后,可以调用对应的方法获取相应的字段值。

 

 

不同款型规格的资料略有差异, 详细信息请向具体销售和400咨询。H3C保留在没有任何通知或提示的情况下对资料内容进行修改的权利!

新华三官网
联系我们