gRPC is a general open-source RPC software framework that runs over HTTP/2 and features high performance. Both communication parties perform secondary development based on the framework, so that they focus on services and do not need to pay attention to underlying communication implemented by the gRPC software framework.
Telemetry uses the gRPC protocol to report the data encapsulated in an encoding format to the collector. For details about the gRPC protocol stack structure, see Figure 1.
To implement the telemetry function, the device supports the dial-in and dial-out modes based on gRPC.
and only allows the subscribe operation to be performed.
huawei-grpc-dialout.proto is the RPC header file. When a device functions as the client to push data, this file defines the RPC interface. The following table describes the file content and meaning.
huawei-grpc-dialout.proto |
---|
syntax = "proto3"; //The .proto file version is defined as v3. package huawei_dialout; //The package name is huawei_dialout. service gRPCDataservice { //The service name is gRPCDataservice. rpc dataPublish(stream serviceArgs) returns(stream serviceArgs) {}; //The method name is dataPublish, providing the data push method. The working mode is bidirectional stream mode. The input parameter is the serviceArgs data flow. } message serviceArgs { //Message format description. int64 ReqId = 1; //Request ID. oneof MessageData { bytes data = 2; //Sampled data in GPB encoding format is carried. string data_json = 4; //Sampled data in JSON encoding format is carried. } string errors = 3; //Error description. } |
huawei-grpc-dialin.proto is the RPC header file. When a device functions as the server to push data, this file defines the RPC interface. The following table describes the file content and meaning.
huawei-grpc-dialin.proto |
---|
syntax = "proto3"; //The .proto file version is defined as v3. package huawei_dialin; //The package name is huawei_dialin. service gRPCConfigOper { //The service name is gRPCConfigOper. rpc Subscribe (SubsArgs) returns (stream SubsReply) {}; //The method is Subscribe, and the server stream mode is used, providing the dynamic subscription method. The input parameter SubsArgs contains subscribing parameters. rpc Cancel (CancelArgs) returns (CancelReply) {}; //The method is Cancel, and the query and response mode is used, providing the method of canceling dynamic subscription. The input parameter CancelArgs contains unsubscribing parameters. } message Path { //Path message structure. string path = 1; //Subscribed sensor-path. uint32 depth = 2; //Sampling depth of the subscribed sensor-path. The value 1 indicates the current level, and the value 2 indicates the current level and its sub-level, and so on. } message SubsArgs { //Subscribing request parameter. uint64 request_id = 1; //Request ID, which is transferred by the invoker. uint32 encoding = 2; //Encoding type. 0: GPB encoding; 1: JSON encoding. repeated Path path = 5; //Subscribed path structure. uint64 sample_interval = 6; //Sampling interval. uint64 heartbeat_interval = 7; //Redundancy suppression interval. This parameter is valid only when suppress_redundant is set to 1. oneof Suppress { bool suppress_redundant = 8; //Redundancy suppression. The sampled data is not reported if the data content remains unchanged. 0: disabled; 1: enabled. uint64 delay_time = 9; //Delay, which ranges from 100 ms to 60000 ms. When data change occurs, new data will be reported after this delay. The data changes that occur during the delay will be reported together. This prevents massive data generated due to service flapping from being reported. } } message SubsReply { //Subscribing response parameter. uint32 subscription_id = 1; //If the subscribing is successful, the subscription ID is returned. If the subscribing fails, 0 is returned. uint64 request_id = 2; //ID of the subscribing request. string response_code = 3; //Return code. The value 200 indicates a success. oneof MessageData { bytes message = 4; //If an error occurs, the error description is returned. If no error occurs, the GPB encoding data is sent. string message_json = 5; //If no error occurs, the JSON encoding data is sent. } } message CancelArgs { //Unsubscribing request parameter. uint64 request_id = 1; //Request ID, which is transferred by the invoker. uint32 subscription_id = 2; //ID of the subscription to be canceled. } message CancelReply { //Unsubscribing response parameter. uint64 request_id = 1; //Request ID, which is transferred by the invoker. string response_code = 2; //Return code. The value 200 indicates a success. string message = 3; //Error description. } |
huawei-telemetry.proto is the telemetry header definition file. It defines the data header when telemetry data sampled is sent, including key information, such as the sampling path and sampling timestamp. The following table describes the file content and meaning.
huawei-telemetry.proto |
---|
syntax = "proto3"; //The .proto file version is defined as v3. package telemetry; //The package name is telemetry. message Telemetry { //Telemetry message structure definition. string node_id_str = 1; //Device name. string subscription_id_str = 2; //Static subscription name. string sensor_path = 3; //Subscription path. string proto_path = 13; //Message path for the sampling path in the proto file. uint64 collection_id = 4; //Sampling round. uint64 collection_start_time = 5;//Start time of a sampling round. uint64 msg_timestamp = 6; //Timestamp when the message is generated. TelemetryGPBTable data_gpb = 7; //Carried data is defined by TelemetryGPBTable. uint64 collection_end_time = 8; //End time of a sampling round. uint32 current_period = 9; //Sampling precision, in milliseconds. string except_desc = 10; //Exception description, which is reported when a sampling exception occurs. string product_name = 11; //Product name. enum Encoding { Encoding_GPB = 0; //GPB encoding format. Encoding_JSON = 1; //JSON encoding format. }; Encoding encoding = 12; //Data encoding format. If the GPB encoding format is used, the data_gpb field is valid. Otherwise, the data_str field is valid. string data_str = 14; //This field is valid only when a non-GPB encoding format is used. string ne_id = 15; //Unique NE ID, which identifies the NE to which data belongs in the gateway scenario. string software_version = 16; //Software version number. } message TelemetryGPBTable { //TelemetryGPBTable message structure definition. repeated TelemetryRowGPB row = 1; //Array definition. Its member is TelemetryRowGPB structure. repeated DataPath delete = 2; //Deletes a data path. Generator generator = 3; //Data source description, which applies to the OnChange+ service that requires high reliability. } message Generator { uint64 generator_id = 1; //Data source ID. Multiple data sources can provide data concurrently and maintain their own reliability. uint32 generator_sn = 2; //Message sequence number. The sequence numbers of messages sent by each data source must be consecutive. If the sequence numbers are not consecutive, data out-of-synchronization occurs. In this case, the collector must be automatically disconnected from the device and re-connected. The value ranges from 0 to 0xFFFFFFFF and can be reversed. bool generator_sync = 3; //Data source synchronization. The value true indicates that full OnChange data is being synchronized. In addition, if the value is true and no data is contained, the synchronization is complete. } message TelemetryRowGPB { uint64 timestamp = 1; //Timestamp of the current sampling instance. bytes content = 11; //Sampling instance data carried. It is used with the sensor_path field to determine which .proto file is used for encoding. } message DataPath { uint64 timestamp = 1; //Timestamp of the current sampling instance. Path path = 2; //Data tree node, which contains only the data path and key field information. } message Path { repeated PathElem node = 1; // repeated PathElem node = 1; //Data tree node, which contains only the data path and key field information. } message PathElem { string name = 1; //Data tree node name. map<string, string> key = 2; //Key field name and value mapping table of the data tree node. } message TelemetrySelfDefinedEvent { string path = 1; //Sampling path that triggers the customized event, which describes the method of parsing the content. string proto_path = 13; //Message path for the sampling path in the proto file. uint32 level = 2; //Level of the user-defined event. string description = 3; //Description of the user-defined event. string fieldName = 4; //Name of the field that triggers the customized event. uint32 fieldValue = 5; //Value of the field that triggers the customized event. TelemetrySelfDefineThresTable data_threshold = 6; //Threshold filter criteria when the customized event is triggered. enum ThresholdRelation { ThresholdRelation_INVALID = 0; //The relationship between thresholds is not configured. ThresholdRelation_AND = 1; //The relationship between thresholds is And. ThresholdRelation_OR = 2; //The relationship between thresholds is Or. } ThresholdRelation thresholdRelation = 7; //Relationship between threshold filter criteria when the customized event is triggered. bytes content = 8; //Sampled data that triggers the customized event. } message TelemetrySelfDefineThresTable { repeated TelemetryThreshold row = 1; //Multiple thresholds are included. } message TelemetryThreshold { uint32 thresholdValue = 1; //Delivered threshold. enum ThresholdOpType { ThresholdOpType_EQ = 0; //The actual value in the data sent equals to the configured data threshold. ThresholdOpType_GT = 1; //The actual value in the data sent is greater than the configured data threshold. ThresholdOpType_GE = 2; //The actual value in the data sent is greater than or equals to the configured data threshold. ThresholdOpType_LT = 3; //The actual value in the data sent is less than the configured data threshold. ThresholdOpType_LE = 4; //The actual value in the data sent is less than or equals to the configured data threshold. } ThresholdOpType thresholdOpType = 2; //Threshold on the device. } |
As shown in Figure 1, in dial-in mode, gRPC provides data configuration, query, and subscription functions using the Capabilities, Set, Get, and Subscribe methods defined in the gRPC Network Management Interface (gNMI) protocol.
The Capabilities method is used to obtain device model capabilities. Its process is as follows:
The Capabilities packet format is as follows:
++++++++ Recevied get response: ++++++++ supported_models:{ name:"openconfig-interfaces" organization:"OpenConfig workinggroup" version:"2.3.0" } supported_models:{ name:"huawei-ifm" organization:"Huawei Technologies Co.,Ltd." version:"2018-11-23" } supported_encodings:JSON gNMI_version:"0.7.0"
The Set method can be used to deliver configurations to the device. The process is as follows:
Parameter |
Description |
---|---|
DELETE |
Corresponds to NETCONF <delete> and is used to delete configuration data. |
REPLACE |
Corresponds to NETCONF <replace> and is used to replace configuration data. |
UPDATE |
Corresponds to NETCONF <merge> and is used to update or create configuration data. |
If some operations in a request fail, all operations in the request do not take effect.
The Set packet format is as follows:
++++++++ Sending set request: ++++++++ path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" key { key: " ifName" value: "Loopback1" } } }
++++++++ Recevied set response: ++++++++ response : { path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" key { key: "ifName" value: "Loopback1" } } } op: DELETE } timestamp: 159207901002054000
{\n\t\"ifName\":\t\"LoopBack1\", \n\t\"ifPhyType\":\t\"LoopBack\", \n\t\"ifNumber\":\t\"1\", \n\t\"ifAdminStatus\":\t\"up\", \n\t\"ifLinkProtocol\":\t\"invalid\", \n\t\"ifRouterType\":\t\"PtoP\", \n\t\"ifDf\":\tfalse, \n\t\"ifTrapEnable\":\ttrue, \n\t\"ifMtu\":\t1500, \n\t\"l2SubIfFlag\":\tfalse, \n\t\"ifClass\":\t\"mainInterface\", \n\t\"ifNetworkLayerStatus\":\t\"ipv4andipv6up\", \n\t\"ifControlFlap\":\t{\n\t\t\"ifCtrlFlapEnbl\":\tfalse\n\t}, \n\t\"ipv4Config\":\t{\n\t\t\"addrCfgType\":\t\"config\"\n\t}, \n\t\"ipv6Config\":\t{\n\t\t\"enableFlag\":\tfalse, \n\t\t\"autoLinkLocal\":\tfalse\n\t}\n}
++++++++ Sending set request: ++++++++ path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" } val { json_val: "{\n\t\"ifName\":\t\"LoopBack1\", \n\t\"ifPhyType\":\t\"LoopBack\", \n\t\"ifNumber\":\t\"1\", \n\t\"ifAdminStatus\":\t\"up\", \n\t\"ifLinkProtocol\":\t\"invalid\", \n\t\"ifRouterType\":\t\"PtoP\", \n\t\"ifDf\":\tfalse, \n\t\"ifTrapEnable\":\ttrue, \n\t\"ifMtu\":\t1500, \n\t\"l2SubIfFlag\":\tfalse, \n\t\"ifClass\":\t\"mainInterface\", \n\t\"ifNetworkLayerStatus\":\t\"ipv4andipv6up\", \n\t\"ifControlFlap\":\t{\n\t\t\"ifCtrlFlapEnbl\":\tfalse\n\t}, \n\t\"ipv4Config\":\t{\n\t\t\"addrCfgType\":\t\"config\"\n\t}, \n\t\"ipv6Config\":\t{\n\t\t\"enableFlag\":\tfalse, \n\t\t\"autoLinkLocal\":\tfalse\n\t}\n}" } }
++++++++ Recevied set response: ++++++++ response : { path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" } } op: REPLACE } timestamp: 1592245848404176000
{\n\t\"ifPhyType\":\t\"LoopBack\", \n\t\"ifNumber\":\t\"1\", \n\t\"ifAdminStatus\":\t\"up\", \n\t\"ifLinkProtocol\":\t\"invalid\", \n\t\"ifRouterType\":\t\"PtoP\", \n\t\"ifDf\":\tfalse, \n\t\"ifTrapEnable\":\ttrue, \n\t\"ifMtu\":\t1500, \n\t\"l2SubIfFlag\":\tfalse, \n\t\"ifClass\":\t\"mainInterface\", \n\t\"ifNetworkLayerStatus\":\t\"ipv4andipv6up\", \n\t\"ifControlFlap\":\t{\n\t\t\"ifCtrlFlapEnbl\":\tfalse\n\t}, \n\t\"ipv4Config\":\t{\n\t\t\"addrCfgType\":\t\"config\"\n\t}, \n\t\"ipv6Config\":\t{\n\t\t\"enableFlag\":\tfalse, \n\t\t\"autoLinkLocal\":\tfalse\n\t}\n}
++++++++ Sending set request: ++++++++ path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" key { key: "ifName" value: "Loopback1" } } val { json_val: "{\n\t\"ifPhyType\":\t\"LoopBack\", \n\t\"ifNumber\":\t\"1\", \n\t\"ifAdminStatus\":\t\"up\", \n\t\"ifLinkProtocol\":\t\"invalid\", \n\t\"ifRouterType\":\t\"PtoP\", \n\t\"ifDf\":\tfalse, \n\t\"ifTrapEnable\":\ttrue, \n\t\"ifMtu\":\t1500, \n\t\"l2SubIfFlag\":\tfalse, \n\t\"ifClass\":\t\"mainInterface\", \n\t\"ifNetworkLayerStatus\":\t\"ipv4andipv6up\", \n\t\"ifControlFlap\":\t{\n\t\t\"ifCtrlFlapEnbl\":\tfalse\n\t}, \n\t\"ipv4Config\":\t{\n\t\t\"addrCfgType\":\t\"config\"\n\t}, \n\t\"ipv6Config\":\t{\n\t\t\"enableFlag\":\tfalse, \n\t\t\"autoLinkLocal\":\tfalse\n\t}\n}" } }
++++++++ Recevied set response: ++++++++ response : { path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" key { key: "ifName" value: "Loopback1" } } } op: REPLACE } timestamp: 1592246188782490000
++++++++ Sending set request: ++++++++ path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" } val { json_val: "{\"ifName\":\"Loopback1\", \"ifDescr\":\"testForUpdate\"}" } }
++++++++ Recevied set response: ++++++++ response : { path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" } } op: UPDATE } timestamp: 1592246353662332000
++++++++ Sending set request: ++++++++ path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" key { key: "ifName" value: "Loopback1" } } val { json_val: "{\"ifDescr\":\"testForUpdate\"}" } }
++++++++ Recevied set response: ++++++++ response : { path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" key { key: "ifName" value: "Loopback1" } } } op: UPDATE } timestamp: 1592220455502852000
Restrictions on leafref: When a parent node is associated with a child node through leafref, these two nodes must be both in a path or defined in JSON_VAL. Otherwise, the operations on these two nodes will be different and will fail to be executed.
Cause: The operation on nodes in a path is None, while the operation on nodes defined in JSON_VAL is REPLACE or UPDATE. If one of two nodes associated through leafref is in a path and the other is defined in JSON_VAL, operation consistency check will fail.
For example, in the openconfig-telemetry.yang model, the key defined in the parent node list subscription is subscription-name, which is associated with subscription-name in child node config through leafref. The preceding restriction applies to data in config. If the path is set to:
"openconfig-telemetry:telemetry-system/subscriptions/persistent/subscription[subscription-name=abc]
Define JSON_VAL as:
{"config": {"originated-qos-marking": "30","encoding":"openconfig-telemetry-types:ENC_PROTO3"}}
The following error is returned:
The target node of the leafref type and the source node have different operations.
To rectify the error, set the path to:
"openconfig-telemetry:telemetry-system/subscriptions/persistent/subscription[subscription-name=abc]/config"
Define JSON_VAL as:
{"originated-qos-marking": "30","encoding":"openconfig-telemetry-types:ENC_PROTO3"}
In this case, the parent and child nodes associated through leafref are both in a path and can be correctly executed.
You can perform the Get operation to obtain the running status and configuration of a device. The procedure is as follows:
Parameter |
Description |
---|---|
ALL |
Corresponds to NETCONF <get> and includes configuration data and status data. |
CONFIG |
Corresponds to NETCONF <get-config> and includes configuration data. |
If some commands fail to be executed, the query stops after the commands are returned. RPC returns an error and does not return the query result. It waits for the client issues the correct command again.
The Get packet format is as follows:
++++++++ Sending get request: ++++++++ path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" key { key: "ifName" value: "Loopback1" } } } type: CONFIG encoding: JSON
notification { timestamp: 1591901665078236000 update { path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" key { key: "ifName" value: "Loopback1" } } } val { json_val: "{\n\t\"ifPhyType\":\t\"LoopBack\", \n\t\"ifNumber\":\t\"1\", \n\t\"ifAdminStatus\":\t\"up\", \n\t\"ifLinkProtocol\":\t\"invalid\", \n\t\"ifRouterType\":\t\"PtoP\", \n\t\"ifDf\":\tfalse, \n\t\"ifTrapEnable\":\ttrue, \n\t\"ifMtu\":\t1500, \n\t\"l2SubIfFlag\":\tfalse, \n\t\"ifClass\":\t\"mainInterface\", \n\t\"ifNetworkLayerStatus\":\t\"ipv4andipv6up\", \n\t\"ifControlFlap\":\t{\n\t\t\"ifCtrlFlapEnbl\":\tfalse\n\t}, \n\t\"ipv4Config\":\t{\n\t\t\"addrCfgType\":\t\"config\"\n\t}, \n\t\"ipv6Config\":\t{\n\t\t\"enableFlag\":\tfalse, \n\t\t\"autoLinkLocal\":\tfalse\n\t}\n}" } } }
++++++++ Sending get request: ++++++++ path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" key { key: "ifName" value: "Loopback1" } } elem { name: "ifDescr" } } encoding: JSON
notification { timestamp: 1591901665078236000 update { path { elem { name: "huawei-ifm:ifm" } elem { name: "interfaces" } elem { name: "interface" key { key: "ifName" value: "Loopback1" } } elem { name: "ifDescr" } } val { json_val: "\"test\"" } } }
The Subscribe method is used to collect device status and configurations periodically or in event-driven mode. Its process is as follows:
Layer 1 Parameter |
Layer 1 Parameter Description |
Layer 2 Parameter |
Layer 2 Parameter Description |
---|---|---|---|
Prefix |
Prefix. |
path |
Sampling path prefix. |
target |
When the target parameter is set on the client, the response message from the server needs to carry the parameter. |
||
Subscription |
There can be one or more subscription parameters of this level in a Subscribe request. |
path |
Sampling path. |
mode |
Sampling mode. Currently, the following modes are supported:
|
||
sample_interval |
Sampling interval, corresponding to the sampling mode:
|
||
SubscriptionListData |
There can be only one subscription parameter of this level in a Subscribe request. |
marking |
Configured DSCP value. The value ranges from 0 to 63. |
mode |
Subscription mode. Currently, STREAM is supported. |
||
allow_aggregation |
Whether the client allows elements with the aggregation flag to be combined in a Telemetry transmission message. This parameter is valid only in PROTO encoding mode. |
||
encoding |
Encoding mode. Currently, the following modes are supported:
|
||
updates_only |
Only incremental data is sent. This parameter is valid when the device sampling mode is ON_CHANGE+. NOTE:
The device implements ON_CHANGE in either of the following modes:
|
The Subscribe packet format is as follows:
# Subscribe request packet
++++++++ Sending subscribe request: ++++++++ prefix { elem { name: "huawei-debug:debug" } target: "TARGET" } Subscription { path { elem { name: "cpu-infos" } elem { name: "cpu-info" } } mode: SAMPLE sample_interval: 1000 } SubscriptionListData { marking: 63 mode: STREAM allow_aggregation: false encoding: JSON updates_only: false }
# JSON response packet
++++++++ Recevied subscribe response: ++++++++ <subscibegNMI>: update { timestamp: 1626429829051000000 update { path { elem { name: "huawei-debug:debug" } elem { name: "cpu-infos" } elem { name: "cpu-info" } } val { json_val: "[{\"position\":\"17\",\"overload-threshold\":90,\"unoverload-threshold\":75,\"interval\":8,\"index\":17891329,\"system-cpu-usage\":6,\"monitor-number\":48,\"monitor-cycle\":10,\"overload-state-change-time\":\"0000-00-00 00:00:00\",\"current-overload-state\":\"Unoverload\"}]" } } delete { } } <subscibegNMI>: sync_response: true
# PROTO response packet (allow_aggregation = false)
++++++++ Recevied subscribe response: ++++++++ <subscibegNMI>: update { timestamp: 1626430004019000000 prefix { elem { name: "huawei-debug:debug" } elem { name: "cpu-infos" } elem { name: "cpu-info" key { key: "position" value: "17" } } } update { path { elem { name: "overload-threshold" } } val { uint_val: 90 } } update { path { elem { name: "unoverload-threshold" } } val { uint_val: 75 } } update { path { elem { name: "interval" } } val { uint_val: 8 } } update { path { elem { name: "index" } } val { uint_val: 17891329 } } update { path { elem { name: "system-cpu-usage" } } val { uint_val: 3 } } update { path { elem { name: "monitor-number" } } val { uint_val: 48 } } update { path { elem { name: "monitor-cycle" } } val { uint_val: 10 } } update { path { elem { name: "overload-state-change-time" } } val { string_val: "0000-00-00 00:00:00" } } update { path { elem { name: "current-overload-state" } } val { string_val: "Unoverload" } } delete { } } <subscibegNMI>: sync_response: true
# PROTO response packet (allow_aggregation = true)
++++++++ Recevied subscribe response: ++++++++ <subscibegNMI>: update { timestamp: 1626430056356000000 update { path { elem { name: "huawei-debug:debug" } elem { name: "cpu-infos" } elem { name: "cpu-info" } } val { proto_bytes: "0a023137105a184b2008288180c40830033830400a4a13303030302d30302d30302030303a30303a3030520a556e6f7665726c6f6164" } } delete { } } <subscibegNMI>: sync_response: true
rpc error: code = PermissionDenied desc = Need use tls.
rpc error: code = Unauthenticated desc = Invalid authentication credentials.
rpc error: code = InvalidArgument desc = Argument 'Path' error.
rpc error: code = InvalidArgument desc = The JSON value is invalid.
rpc error: code = NotFound desc = Not found.