Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
276
plugins/inputs/gnmi/README.md
Normal file
276
plugins/inputs/gnmi/README.md
Normal file
|
@ -0,0 +1,276 @@
|
|||
# gNMI (gRPC Network Management Interface) Input Plugin
|
||||
|
||||
This plugin consumes telemetry data based on [gNMI][gnmi] subscriptions. TLS is
|
||||
supported for authentication and encryption. This plugin is vendor-agnostic and
|
||||
is supported on any platform that supports the gNMI specification.
|
||||
|
||||
For Cisco devices the plugin has been optimized to support gNMI telemetry as
|
||||
produced by Cisco IOS XR (64-bit) version 6.5.1, Cisco NX-OS 9.3 and
|
||||
Cisco IOS XE 16.12 and later.
|
||||
|
||||
⭐ Telegraf v1.15.0
|
||||
🏷️ network
|
||||
💻 all
|
||||
|
||||
[gnmi]: https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md
|
||||
|
||||
## Service Input <!-- @/docs/includes/service_input.md -->
|
||||
|
||||
This plugin is a service input. Normal plugins gather metrics determined by the
|
||||
interval setting. Service plugins start a service to listen and wait for
|
||||
metrics or events to occur. Service plugins have two key differences from
|
||||
normal plugins:
|
||||
|
||||
1. The global or plugin specific `interval` setting may not apply
|
||||
2. The CLI options of `--test`, `--test-wait`, and `--once` may not produce
|
||||
output for this plugin
|
||||
|
||||
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
||||
|
||||
In addition to the plugin-specific configuration settings, plugins support
|
||||
additional global and plugin configuration settings. These settings are used to
|
||||
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
||||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||
|
||||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
||||
|
||||
## Secret-store support
|
||||
|
||||
This plugin supports secrets from secret-stores for the `username` and
|
||||
`password` options. See the [secret-store documentation][SECRETSTORE] for more
|
||||
details on how to use them.
|
||||
|
||||
[SECRETSTORE]: ../../../docs/CONFIGURATION.md#secret-store-secrets
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml @sample.conf
|
||||
# gNMI telemetry input plugin
|
||||
[[inputs.gnmi]]
|
||||
## Address and port of the gNMI GRPC server
|
||||
addresses = ["10.49.234.114:57777"]
|
||||
|
||||
## define credentials
|
||||
username = "cisco"
|
||||
password = "cisco"
|
||||
|
||||
## gNMI encoding requested (one of: "proto", "json", "json_ietf", "bytes")
|
||||
# encoding = "proto"
|
||||
|
||||
## redial in case of failures after
|
||||
# redial = "10s"
|
||||
|
||||
## gRPC Keepalive settings
|
||||
## See https://pkg.go.dev/google.golang.org/grpc/keepalive
|
||||
## The client will ping the server to see if the transport is still alive if it has
|
||||
## not see any activity for the given time.
|
||||
## If not set, none of the keep-alive setting (including those below) will be applied.
|
||||
## If set and set below 10 seconds, the gRPC library will apply a minimum value of 10s will be used instead.
|
||||
# keepalive_time = ""
|
||||
|
||||
## Timeout for seeing any activity after the keep-alive probe was
|
||||
## sent. If no activity is seen the connection is closed.
|
||||
# keepalive_timeout = ""
|
||||
|
||||
## gRPC Maximum Message Size
|
||||
# max_msg_size = "4MB"
|
||||
|
||||
## Subtree depth for depth extension (disables if < 1)
|
||||
## see https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-depth.md
|
||||
# depth = 0
|
||||
|
||||
## Enable to get the canonical path as field-name
|
||||
# canonical_field_names = false
|
||||
|
||||
## Remove leading slashes and dots in field-name
|
||||
# trim_field_names = false
|
||||
|
||||
## Only receive updates for the state, also suppresses receiving the initial state
|
||||
# updates_only = false
|
||||
|
||||
## Enforces the namespace of the first element as origin for aliases and
|
||||
## response paths, required for backward compatibility.
|
||||
## NOTE: Set to 'false' if possible but be aware that this might change the path tag!
|
||||
# enforce_first_namespace_as_origin = true
|
||||
|
||||
## Guess the path-tag if an update does not contain a prefix-path
|
||||
## Supported values are
|
||||
## none -- do not add a 'path' tag
|
||||
## common path -- use the common path elements of all fields in an update
|
||||
## subscription -- use the subscription path
|
||||
# path_guessing_strategy = "none"
|
||||
|
||||
## Prefix tags from path keys with the path element
|
||||
# prefix_tag_key_with_path = false
|
||||
|
||||
## Optional client-side TLS to authenticate the device
|
||||
## Set to true/false to enforce TLS being enabled/disabled. If not set,
|
||||
## enable TLS only if any of the other options are specified.
|
||||
# tls_enable =
|
||||
## Trusted root certificates for server
|
||||
# tls_ca = "/path/to/cafile"
|
||||
## Used for TLS client certificate authentication
|
||||
# tls_cert = "/path/to/certfile"
|
||||
## Used for TLS client certificate authentication
|
||||
# tls_key = "/path/to/keyfile"
|
||||
## Password for the key file if it is encrypted
|
||||
# tls_key_pwd = ""
|
||||
## Send the specified TLS server name via SNI
|
||||
# tls_server_name = "kubernetes.example.com"
|
||||
## Minimal TLS version to accept by the client
|
||||
# tls_min_version = "TLS12"
|
||||
## List of ciphers to accept, by default all secure ciphers will be accepted
|
||||
## See https://pkg.go.dev/crypto/tls#pkg-constants for supported values.
|
||||
## Use "all", "secure" and "insecure" to add all support ciphers, secure
|
||||
## suites or insecure suites respectively.
|
||||
# tls_cipher_suites = ["secure"]
|
||||
## Renegotiation method, "never", "once" or "freely"
|
||||
# tls_renegotiation_method = "never"
|
||||
## Use TLS but skip chain & host verification
|
||||
# insecure_skip_verify = false
|
||||
|
||||
## gNMI subscription prefix (optional, can usually be left empty)
|
||||
## See: https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md#222-paths
|
||||
# origin = ""
|
||||
# prefix = ""
|
||||
# target = ""
|
||||
|
||||
## Vendor specific options
|
||||
## This defines what vendor specific options to load.
|
||||
## * Juniper Header Extension (juniper_header): some sensors are directly managed by
|
||||
## Linecard, which adds the Juniper GNMI Header Extension. Enabling this
|
||||
## allows the decoding of the Extension header if present. Currently this knob
|
||||
## adds component, component_id & sub_component_id as additional tags
|
||||
# vendor_specific = []
|
||||
|
||||
## YANG model paths for decoding IETF JSON payloads
|
||||
## Model files are loaded recursively from the given directories. Disabled if
|
||||
## no models are specified.
|
||||
# yang_model_paths = []
|
||||
|
||||
## Define additional aliases to map encoding paths to measurement names
|
||||
# [inputs.gnmi.aliases]
|
||||
# ifcounters = "openconfig:/interfaces/interface/state/counters"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
## Name of the measurement that will be emitted
|
||||
name = "ifcounters"
|
||||
|
||||
## Origin and path of the subscription
|
||||
## See: https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md#222-paths
|
||||
##
|
||||
## origin usually refers to a (YANG) data model implemented by the device
|
||||
## and path to a specific substructure inside it that should be subscribed
|
||||
## to (similar to an XPath). YANG models can be found e.g. here:
|
||||
## https://github.com/YangModels/yang/tree/master/vendor/cisco/xr
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/state/counters"
|
||||
|
||||
## Subscription mode ("target_defined", "sample", "on_change") and interval
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
|
||||
## Suppress redundant transmissions when measured values are unchanged
|
||||
# suppress_redundant = false
|
||||
|
||||
## If suppression is enabled, send updates at least every X seconds anyway
|
||||
# heartbeat_interval = "60s"
|
||||
|
||||
## Tag subscriptions are applied as tags to other subscriptions.
|
||||
# [[inputs.gnmi.tag_subscription]]
|
||||
# ## When applying this value as a tag to other metrics, use this tag name
|
||||
# name = "descr"
|
||||
#
|
||||
# ## All other subscription fields are as normal
|
||||
# origin = "openconfig-interfaces"
|
||||
# path = "/interfaces/interface/state"
|
||||
# subscription_mode = "on_change"
|
||||
#
|
||||
# ## Match strategy to use for the tag.
|
||||
# ## Tags are only applied for metrics of the same address. The following
|
||||
# ## settings are valid:
|
||||
# ## unconditional -- always match
|
||||
# ## name -- match by the "name" key
|
||||
# ## This resembles the previous 'tag-only' behavior.
|
||||
# ## elements -- match by the keys in the path filtered by the path
|
||||
# ## parts specified `elements` below
|
||||
# ## By default, 'elements' is used if the 'elements' option is provided,
|
||||
# ## otherwise match by 'name'.
|
||||
# # match = ""
|
||||
#
|
||||
# ## For the 'elements' match strategy, at least one path-element name must
|
||||
# ## be supplied containing at least one key to match on. Multiple path
|
||||
# ## elements can be specified in any order. All given keys must be equal
|
||||
# ## for a match.
|
||||
# # elements = ["description", "interface"]
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
||||
Each configured subscription will emit a different measurement. Each leaf in a
|
||||
GNMI SubscribeResponse Update message will produce a field reading in the
|
||||
measurement. GNMI PathElement keys for leaves will attach tags to the field(s).
|
||||
|
||||
## Example Output
|
||||
|
||||
```text
|
||||
ifcounters,path=openconfig-interfaces:/interfaces/interface/state/counters,host=linux,name=MgmtEth0/RP0/CPU0/0,source=10.49.234.115,descr/description=Foo in-multicast-pkts=0i,out-multicast-pkts=0i,out-errors=0i,out-discards=0i,in-broadcast-pkts=0i,out-broadcast-pkts=0i,in-discards=0i,in-unknown-protos=0i,in-errors=0i,out-unicast-pkts=0i,in-octets=0i,out-octets=0i,last-clear="2019-05-22T16:53:21Z",in-unicast-pkts=0i 1559145777425000000
|
||||
ifcounters,path=openconfig-interfaces:/interfaces/interface/state/counters,host=linux,name=GigabitEthernet0/0/0/0,source=10.49.234.115,descr/description=Bar out-multicast-pkts=0i,out-broadcast-pkts=0i,in-errors=0i,out-errors=0i,in-discards=0i,out-octets=0i,in-unknown-protos=0i,in-unicast-pkts=0i,in-octets=0i,in-multicast-pkts=0i,in-broadcast-pkts=0i,last-clear="2019-05-22T16:54:50Z",out-unicast-pkts=0i,out-discards=0i 1559145777425000000
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Empty metric-name warning
|
||||
|
||||
Some devices (e.g. Juniper) report spurious data with response paths not
|
||||
corresponding to any subscription. In those cases, Telegraf will not be able
|
||||
to determine the metric name for the response and you get an
|
||||
*empty metric-name warning*
|
||||
|
||||
For example if you subscribe to `/junos/system/linecard/cpu/memory` but the
|
||||
corresponding response arrives with path
|
||||
`/components/component/properties/property/...` To avoid those issues, you can
|
||||
manually map the response to a metric name using the `aliases` option like
|
||||
|
||||
```toml
|
||||
[[inputs.gnmi]]
|
||||
addresses = ["..."]
|
||||
|
||||
[inputs.gnmi.aliases]
|
||||
memory = "/components"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "memory"
|
||||
origin = "openconfig"
|
||||
path = "/junos/system/linecard/cpu/memory"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "60s"
|
||||
```
|
||||
|
||||
If this does *not* solve the issue, please follow the warning instructions and
|
||||
open an issue with the response, your configuration and the metric you expect.
|
||||
|
||||
### Missing `path` tag
|
||||
|
||||
Some devices (e.g. Arista) omit the prefix and specify the path in the update
|
||||
if there is only one value reported. This leads to a missing `path` tag for
|
||||
the resulting metrics. In those cases you should set `path_guessing_strategy`
|
||||
to `subscription` to use the subscription path as `path` tag.
|
||||
|
||||
Other devices might omit the prefix in updates altogether. Here setting
|
||||
`path_guessing_strategy` to `common path` can help to infer the `path` tag by
|
||||
using the part of the path that is common to all values in the update.
|
||||
|
||||
### TLS handshake failure
|
||||
|
||||
When receiving an error like
|
||||
|
||||
```text
|
||||
2024-01-01T00:00:00Z E! [inputs.gnmi] Error in plugin: failed to setup subscription: rpc error: code = Unavailable desc = connection error: desc = "transport: authentication handshake failed: remote error: tls: handshake failure"
|
||||
```
|
||||
|
||||
this might be due to insecure TLS configurations in the GNMI server. Please
|
||||
check the minimum TLS version provided by the server as well as the cipher suite
|
||||
used. You might want to use the `tls_min_version` or `tls_cipher_suites` setting
|
||||
respectively to work-around the issue. Please be careful to not undermine the
|
||||
security of the connection between the plugin and the device!
|
|
@ -0,0 +1,381 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.30.0
|
||||
// protoc v3.12.4
|
||||
// source: GnmiJuniperTelemetryHeaderExtension.proto
|
||||
|
||||
package jnpr_gnmi_extention
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type StreamType int32
|
||||
|
||||
const (
|
||||
StreamType_INITIAL_SYNC StreamType = 0
|
||||
StreamType_ONCHANGE StreamType = 1
|
||||
StreamType_PERIODIC StreamType = 2
|
||||
)
|
||||
|
||||
// Enum value maps for StreamType.
|
||||
var (
|
||||
StreamType_name = map[int32]string{
|
||||
0: "INITIAL_SYNC",
|
||||
1: "ONCHANGE",
|
||||
2: "PERIODIC",
|
||||
}
|
||||
StreamType_value = map[string]int32{
|
||||
"INITIAL_SYNC": 0,
|
||||
"ONCHANGE": 1,
|
||||
"PERIODIC": 2,
|
||||
}
|
||||
)
|
||||
|
||||
func (x StreamType) Enum() *StreamType {
|
||||
p := new(StreamType)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x StreamType) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (StreamType) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_GnmiJuniperTelemetryHeaderExtension_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (StreamType) Type() protoreflect.EnumType {
|
||||
return &file_GnmiJuniperTelemetryHeaderExtension_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x StreamType) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use StreamType.Descriptor instead.
|
||||
func (StreamType) EnumDescriptor() ([]byte, []int) {
|
||||
return file_GnmiJuniperTelemetryHeaderExtension_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
// Present as first gNMI update in all packets
|
||||
type GnmiJuniperTelemetryHeaderExtension struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// router name:export IP address
|
||||
SystemId string `protobuf:"bytes,1,opt,name=system_id,json=systemId,proto3" json:"system_id,omitempty"`
|
||||
// line card / RE (slot number)
|
||||
ComponentId uint32 `protobuf:"varint,2,opt,name=component_id,json=componentId,proto3" json:"component_id,omitempty"`
|
||||
// PFE (if applicable)
|
||||
SubComponentId uint32 `protobuf:"varint,3,opt,name=sub_component_id,json=subComponentId,proto3" json:"sub_component_id,omitempty"`
|
||||
// Internal sensor name
|
||||
SensorName string `protobuf:"bytes,4,opt,name=sensor_name,json=sensorName,proto3" json:"sensor_name,omitempty"`
|
||||
// Sensor path in the subscribe request
|
||||
SubscribedPath string `protobuf:"bytes,5,opt,name=subscribed_path,json=subscribedPath,proto3" json:"subscribed_path,omitempty"`
|
||||
// Internal sensor path in junos
|
||||
StreamedPath string `protobuf:"bytes,6,opt,name=streamed_path,json=streamedPath,proto3" json:"streamed_path,omitempty"`
|
||||
Component string `protobuf:"bytes,7,opt,name=component,proto3" json:"component,omitempty"`
|
||||
// Sequence number, monotonically increasing for each
|
||||
SequenceNumber uint64 `protobuf:"varint,8,opt,name=sequence_number,json=sequenceNumber,proto3" json:"sequence_number,omitempty"`
|
||||
// Payload get timestamp in milliseconds
|
||||
PayloadGetTimestamp int64 `protobuf:"varint,9,opt,name=payload_get_timestamp,json=payloadGetTimestamp,proto3" json:"payload_get_timestamp,omitempty"`
|
||||
// Stream creation timestamp in milliseconds
|
||||
StreamCreationTimestamp int64 `protobuf:"varint,10,opt,name=stream_creation_timestamp,json=streamCreationTimestamp,proto3" json:"stream_creation_timestamp,omitempty"`
|
||||
// [Deprecated] Event timestamp in milliseconds
|
||||
//
|
||||
// Deprecated: Marked as deprecated in GnmiJuniperTelemetryHeaderExtension.proto.
|
||||
EventTimestamp int64 `protobuf:"varint,11,opt,name=event_timestamp,json=eventTimestamp,proto3" json:"event_timestamp,omitempty"`
|
||||
// Export timestamp in milliseconds
|
||||
ExportTimestamp int64 `protobuf:"varint,12,opt,name=export_timestamp,json=exportTimestamp,proto3" json:"export_timestamp,omitempty"`
|
||||
// Subsequence number
|
||||
SubSequenceNumber uint64 `protobuf:"varint,13,opt,name=sub_sequence_number,json=subSequenceNumber,proto3" json:"sub_sequence_number,omitempty"`
|
||||
// End of marker
|
||||
Eom bool `protobuf:"varint,14,opt,name=eom,proto3" json:"eom,omitempty"`
|
||||
// Event publish timestamp in milliseconds
|
||||
EventPublishTimestamp int64 `protobuf:"varint,15,opt,name=event_publish_timestamp,json=eventPublishTimestamp,proto3" json:"event_publish_timestamp,omitempty"`
|
||||
// Stream type of packet
|
||||
StreamId StreamType `protobuf:"varint,16,opt,name=stream_id,json=streamId,proto3,enum=StreamType" json:"stream_id,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) Reset() {
|
||||
*x = GnmiJuniperTelemetryHeaderExtension{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_GnmiJuniperTelemetryHeaderExtension_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GnmiJuniperTelemetryHeaderExtension) ProtoMessage() {}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_GnmiJuniperTelemetryHeaderExtension_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GnmiJuniperTelemetryHeaderExtension.ProtoReflect.Descriptor instead.
|
||||
func (*GnmiJuniperTelemetryHeaderExtension) Descriptor() ([]byte, []int) {
|
||||
return file_GnmiJuniperTelemetryHeaderExtension_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetSystemId() string {
|
||||
if x != nil {
|
||||
return x.SystemId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetComponentId() uint32 {
|
||||
if x != nil {
|
||||
return x.ComponentId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetSubComponentId() uint32 {
|
||||
if x != nil {
|
||||
return x.SubComponentId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetSensorName() string {
|
||||
if x != nil {
|
||||
return x.SensorName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetSubscribedPath() string {
|
||||
if x != nil {
|
||||
return x.SubscribedPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetStreamedPath() string {
|
||||
if x != nil {
|
||||
return x.StreamedPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetComponent() string {
|
||||
if x != nil {
|
||||
return x.Component
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetSequenceNumber() uint64 {
|
||||
if x != nil {
|
||||
return x.SequenceNumber
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetPayloadGetTimestamp() int64 {
|
||||
if x != nil {
|
||||
return x.PayloadGetTimestamp
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetStreamCreationTimestamp() int64 {
|
||||
if x != nil {
|
||||
return x.StreamCreationTimestamp
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Deprecated: Marked as deprecated in GnmiJuniperTelemetryHeaderExtension.proto.
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetEventTimestamp() int64 {
|
||||
if x != nil {
|
||||
return x.EventTimestamp
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetExportTimestamp() int64 {
|
||||
if x != nil {
|
||||
return x.ExportTimestamp
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetSubSequenceNumber() uint64 {
|
||||
if x != nil {
|
||||
return x.SubSequenceNumber
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetEom() bool {
|
||||
if x != nil {
|
||||
return x.Eom
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetEventPublishTimestamp() int64 {
|
||||
if x != nil {
|
||||
return x.EventPublishTimestamp
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GnmiJuniperTelemetryHeaderExtension) GetStreamId() StreamType {
|
||||
if x != nil {
|
||||
return x.StreamId
|
||||
}
|
||||
return StreamType_INITIAL_SYNC
|
||||
}
|
||||
|
||||
var File_GnmiJuniperTelemetryHeaderExtension_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_GnmiJuniperTelemetryHeaderExtension_proto_rawDesc = []byte{
|
||||
0x0a, 0x29, 0x47, 0x6e, 0x6d, 0x69, 0x4a, 0x75, 0x6e, 0x69, 0x70, 0x65, 0x72, 0x54, 0x65, 0x6c,
|
||||
0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65,
|
||||
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb1, 0x05, 0x0a, 0x23,
|
||||
0x47, 0x6e, 0x6d, 0x69, 0x4a, 0x75, 0x6e, 0x69, 0x70, 0x65, 0x72, 0x54, 0x65, 0x6c, 0x65, 0x6d,
|
||||
0x65, 0x74, 0x72, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x64,
|
||||
0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e,
|
||||
0x74, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f,
|
||||
0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x73,
|
||||
0x75, 0x62, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1f, 0x0a,
|
||||
0x0b, 0x73, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27,
|
||||
0x0a, 0x0f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x74,
|
||||
0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
|
||||
0x62, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x72, 0x65, 0x61,
|
||||
0x6d, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
|
||||
0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x09,
|
||||
0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x65,
|
||||
0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x08, 0x20,
|
||||
0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x4e, 0x75, 0x6d,
|
||||
0x62, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x67,
|
||||
0x65, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01,
|
||||
0x28, 0x03, 0x52, 0x13, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x47, 0x65, 0x74, 0x54, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3a, 0x0a, 0x19, 0x73, 0x74, 0x72, 0x65, 0x61,
|
||||
0x6d, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73,
|
||||
0x74, 0x61, 0x6d, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x17, 0x73, 0x74, 0x72, 0x65,
|
||||
0x61, 0x6d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||
0x61, 0x6d, 0x70, 0x12, 0x2b, 0x0a, 0x0f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01,
|
||||
0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||
0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73,
|
||||
0x74, 0x61, 0x6d, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2e, 0x0a, 0x13, 0x73,
|
||||
0x75, 0x62, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62,
|
||||
0x65, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x73, 0x75, 0x62, 0x53, 0x65, 0x71,
|
||||
0x75, 0x65, 0x6e, 0x63, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x65,
|
||||
0x6f, 0x6d, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x65, 0x6f, 0x6d, 0x12, 0x36, 0x0a,
|
||||
0x17, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x5f, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15,
|
||||
0x65, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x54, 0x69, 0x6d, 0x65,
|
||||
0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x28, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0b, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61,
|
||||
0x6d, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x2a,
|
||||
0x3a, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a,
|
||||
0x0c, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, 0x00, 0x12,
|
||||
0x0c, 0x0a, 0x08, 0x4f, 0x4e, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x10, 0x01, 0x12, 0x0c, 0x0a,
|
||||
0x08, 0x50, 0x45, 0x52, 0x49, 0x4f, 0x44, 0x49, 0x43, 0x10, 0x02, 0x42, 0x17, 0x5a, 0x15, 0x2e,
|
||||
0x3b, 0x6a, 0x6e, 0x70, 0x72, 0x5f, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_GnmiJuniperTelemetryHeaderExtension_proto_rawDescOnce sync.Once
|
||||
file_GnmiJuniperTelemetryHeaderExtension_proto_rawDescData = file_GnmiJuniperTelemetryHeaderExtension_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_GnmiJuniperTelemetryHeaderExtension_proto_rawDescGZIP() []byte {
|
||||
file_GnmiJuniperTelemetryHeaderExtension_proto_rawDescOnce.Do(func() {
|
||||
file_GnmiJuniperTelemetryHeaderExtension_proto_rawDescData = protoimpl.X.CompressGZIP(file_GnmiJuniperTelemetryHeaderExtension_proto_rawDescData)
|
||||
})
|
||||
return file_GnmiJuniperTelemetryHeaderExtension_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_GnmiJuniperTelemetryHeaderExtension_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_GnmiJuniperTelemetryHeaderExtension_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_GnmiJuniperTelemetryHeaderExtension_proto_goTypes = []interface{}{
|
||||
(StreamType)(0), // 0: StreamType
|
||||
(*GnmiJuniperTelemetryHeaderExtension)(nil), // 1: GnmiJuniperTelemetryHeaderExtension
|
||||
}
|
||||
var file_GnmiJuniperTelemetryHeaderExtension_proto_depIdxs = []int32{
|
||||
0, // 0: GnmiJuniperTelemetryHeaderExtension.stream_id:type_name -> StreamType
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_GnmiJuniperTelemetryHeaderExtension_proto_init() }
|
||||
func file_GnmiJuniperTelemetryHeaderExtension_proto_init() {
|
||||
if File_GnmiJuniperTelemetryHeaderExtension_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_GnmiJuniperTelemetryHeaderExtension_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GnmiJuniperTelemetryHeaderExtension); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_GnmiJuniperTelemetryHeaderExtension_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_GnmiJuniperTelemetryHeaderExtension_proto_goTypes,
|
||||
DependencyIndexes: file_GnmiJuniperTelemetryHeaderExtension_proto_depIdxs,
|
||||
EnumInfos: file_GnmiJuniperTelemetryHeaderExtension_proto_enumTypes,
|
||||
MessageInfos: file_GnmiJuniperTelemetryHeaderExtension_proto_msgTypes,
|
||||
}.Build()
|
||||
File_GnmiJuniperTelemetryHeaderExtension_proto = out.File
|
||||
file_GnmiJuniperTelemetryHeaderExtension_proto_rawDesc = nil
|
||||
file_GnmiJuniperTelemetryHeaderExtension_proto_goTypes = nil
|
||||
file_GnmiJuniperTelemetryHeaderExtension_proto_depIdxs = nil
|
||||
}
|
490
plugins/inputs/gnmi/gnmi.go
Normal file
490
plugins/inputs/gnmi/gnmi.go
Normal file
|
@ -0,0 +1,490 @@
|
|||
//go:generate ../../../tools/config_includer/generator
|
||||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package gnmi
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/gnxi/utils/xpath"
|
||||
"github.com/openconfig/gnmi/proto/gnmi"
|
||||
"github.com/openconfig/gnmi/proto/gnmi_ext"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/internal/choice"
|
||||
common_tls "github.com/influxdata/telegraf/plugins/common/tls"
|
||||
"github.com/influxdata/telegraf/plugins/common/yangmodel"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
// Currently supported GNMI Extensions
|
||||
var supportedExtensions = []string{"juniper_header"}
|
||||
|
||||
// Define the warning to show if we cannot get a metric name.
|
||||
const emptyNameWarning = `Got empty metric-name for response (field %q), usually
|
||||
indicating configuration issues as the response cannot be related to any
|
||||
subscription.Please open an issue on https://github.com/influxdata/telegraf
|
||||
including your device model and the following response data:
|
||||
%+v
|
||||
This message is only printed once.`
|
||||
|
||||
type GNMI struct {
|
||||
Addresses []string `toml:"addresses"`
|
||||
Subscriptions []subscription `toml:"subscription"`
|
||||
TagSubscriptions []tagSubscription `toml:"tag_subscription"`
|
||||
Aliases map[string]string `toml:"aliases"`
|
||||
Encoding string `toml:"encoding"`
|
||||
Origin string `toml:"origin"`
|
||||
Prefix string `toml:"prefix"`
|
||||
Target string `toml:"target"`
|
||||
UpdatesOnly bool `toml:"updates_only"`
|
||||
VendorSpecific []string `toml:"vendor_specific"`
|
||||
Username config.Secret `toml:"username"`
|
||||
Password config.Secret `toml:"password"`
|
||||
Redial config.Duration `toml:"redial"`
|
||||
MaxMsgSize config.Size `toml:"max_msg_size"`
|
||||
Depth int32 `toml:"depth"`
|
||||
Trace bool `toml:"dump_responses"`
|
||||
CanonicalFieldNames bool `toml:"canonical_field_names"`
|
||||
TrimFieldNames bool `toml:"trim_field_names"`
|
||||
PrefixTagKeyWithPath bool `toml:"prefix_tag_key_with_path"`
|
||||
GuessPathTag bool `toml:"guess_path_tag" deprecated:"1.30.0;1.35.0;use 'path_guessing_strategy' instead"`
|
||||
GuessPathStrategy string `toml:"path_guessing_strategy"`
|
||||
EnableTLS bool `toml:"enable_tls" deprecated:"1.27.0;1.35.0;use 'tls_enable' instead"`
|
||||
KeepaliveTime config.Duration `toml:"keepalive_time"`
|
||||
KeepaliveTimeout config.Duration `toml:"keepalive_timeout"`
|
||||
YangModelPaths []string `toml:"yang_model_paths"`
|
||||
EnforceFirstNamespaceAsOrigin bool `toml:"enforce_first_namespace_as_origin"`
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
common_tls.ClientConfig
|
||||
|
||||
// Internal state
|
||||
internalAliases map[*pathInfo]string
|
||||
decoder *yangmodel.Decoder
|
||||
cancel context.CancelFunc
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
type subscription struct {
|
||||
Name string `toml:"name"`
|
||||
Origin string `toml:"origin"`
|
||||
Path string `toml:"path"`
|
||||
SubscriptionMode string `toml:"subscription_mode"`
|
||||
SampleInterval config.Duration `toml:"sample_interval"`
|
||||
SuppressRedundant bool `toml:"suppress_redundant"`
|
||||
HeartbeatInterval config.Duration `toml:"heartbeat_interval"`
|
||||
TagOnly bool `toml:"tag_only" deprecated:"1.25.0;1.35.0;please use 'tag_subscription's instead"`
|
||||
|
||||
fullPath *gnmi.Path
|
||||
}
|
||||
|
||||
type tagSubscription struct {
|
||||
subscription
|
||||
Match string `toml:"match"`
|
||||
Elements []string `toml:"elements"`
|
||||
}
|
||||
|
||||
func (*GNMI) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (c *GNMI) Init() error {
|
||||
// Check options
|
||||
switch c.Encoding {
|
||||
case "":
|
||||
c.Encoding = "proto"
|
||||
case "proto", "json", "json_ietf", "bytes":
|
||||
// Do nothing, those are valid
|
||||
default:
|
||||
return fmt.Errorf("unsupported encoding %s", c.Encoding)
|
||||
}
|
||||
|
||||
if time.Duration(c.Redial) <= 0 {
|
||||
return errors.New("redial duration must be positive")
|
||||
}
|
||||
|
||||
// Check vendor_specific options configured by user
|
||||
if err := choice.CheckSlice(c.VendorSpecific, supportedExtensions); err != nil {
|
||||
return fmt.Errorf("unsupported vendor_specific option: %w", err)
|
||||
}
|
||||
|
||||
// Check path guessing and handle deprecated option
|
||||
if c.GuessPathTag {
|
||||
if c.GuessPathStrategy == "" {
|
||||
c.GuessPathStrategy = "common path"
|
||||
}
|
||||
if c.GuessPathStrategy != "common path" {
|
||||
return errors.New("conflicting settings between 'guess_path_tag' and 'path_guessing_strategy'")
|
||||
}
|
||||
}
|
||||
switch c.GuessPathStrategy {
|
||||
case "", "none", "common path", "subscription":
|
||||
default:
|
||||
return fmt.Errorf("invalid 'path_guessing_strategy' %q", c.GuessPathStrategy)
|
||||
}
|
||||
|
||||
// Use the new TLS option for enabling
|
||||
// Honor deprecated option
|
||||
enable := (c.ClientConfig.Enable != nil && *c.ClientConfig.Enable) || c.EnableTLS
|
||||
c.ClientConfig.Enable = &enable
|
||||
|
||||
// Split the subscriptions into "normal" and "tag" subscription
|
||||
// and prepare them.
|
||||
for i := len(c.Subscriptions) - 1; i >= 0; i-- {
|
||||
subscription := c.Subscriptions[i]
|
||||
|
||||
// Check the subscription
|
||||
if subscription.Name == "" {
|
||||
return fmt.Errorf("empty 'name' found for subscription %d", i+1)
|
||||
}
|
||||
if subscription.Path == "" {
|
||||
return fmt.Errorf("empty 'path' found for subscription %d", i+1)
|
||||
}
|
||||
|
||||
// Support and convert legacy TagOnly subscriptions
|
||||
if subscription.TagOnly {
|
||||
tagSub := tagSubscription{
|
||||
subscription: subscription,
|
||||
Match: "name",
|
||||
}
|
||||
c.TagSubscriptions = append(c.TagSubscriptions, tagSub)
|
||||
// Remove from the original subscriptions list
|
||||
c.Subscriptions = append(c.Subscriptions[:i], c.Subscriptions[i+1:]...)
|
||||
continue
|
||||
}
|
||||
if err := subscription.buildFullPath(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for idx := range c.TagSubscriptions {
|
||||
if err := c.TagSubscriptions[idx].buildFullPath(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.TagSubscriptions[idx].TagOnly != c.TagSubscriptions[0].TagOnly {
|
||||
return errors.New("do not mix legacy tag_only subscriptions and tag subscriptions")
|
||||
}
|
||||
switch c.TagSubscriptions[idx].Match {
|
||||
case "":
|
||||
if len(c.TagSubscriptions[idx].Elements) > 0 {
|
||||
c.TagSubscriptions[idx].Match = "elements"
|
||||
} else {
|
||||
c.TagSubscriptions[idx].Match = "name"
|
||||
}
|
||||
case "unconditional":
|
||||
case "name":
|
||||
case "elements":
|
||||
if len(c.TagSubscriptions[idx].Elements) == 0 {
|
||||
return errors.New("tag_subscription must have at least one element")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown match type %q for tag-subscription %q", c.TagSubscriptions[idx].Match, c.TagSubscriptions[idx].Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Invert explicit alias list and prefill subscription names
|
||||
c.internalAliases = make(map[*pathInfo]string, len(c.Subscriptions)+len(c.Aliases)+len(c.TagSubscriptions))
|
||||
for _, s := range c.Subscriptions {
|
||||
if err := s.buildAlias(c.internalAliases, c.EnforceFirstNamespaceAsOrigin); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, s := range c.TagSubscriptions {
|
||||
if err := s.buildAlias(c.internalAliases, c.EnforceFirstNamespaceAsOrigin); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for alias, encodingPath := range c.Aliases {
|
||||
path := newInfoFromString(encodingPath)
|
||||
if c.EnforceFirstNamespaceAsOrigin {
|
||||
path.enforceFirstNamespaceAsOrigin()
|
||||
}
|
||||
c.internalAliases[path] = alias
|
||||
}
|
||||
c.Log.Debugf("Internal alias mapping: %+v", c.internalAliases)
|
||||
|
||||
// Warn about configures insecure cipher suites
|
||||
insecure := common_tls.InsecureCiphers(c.ClientConfig.TLSCipherSuites)
|
||||
if len(insecure) > 0 {
|
||||
c.Log.Warnf("Configured insecure cipher suites: %s", strings.Join(insecure, ","))
|
||||
}
|
||||
|
||||
// Check the TLS configuration
|
||||
if _, err := c.ClientConfig.TLSConfig(); err != nil {
|
||||
if errors.Is(err, common_tls.ErrCipherUnsupported) {
|
||||
secure, insecure := common_tls.Ciphers()
|
||||
c.Log.Info("Supported secure ciphers:")
|
||||
for _, name := range secure {
|
||||
c.Log.Infof(" %s", name)
|
||||
}
|
||||
c.Log.Info("Supported insecure ciphers:")
|
||||
for _, name := range insecure {
|
||||
c.Log.Infof(" %s", name)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Load the YANG models if specified by the user
|
||||
if len(c.YangModelPaths) > 0 {
|
||||
decoder, err := yangmodel.NewDecoder(c.YangModelPaths...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating YANG model decoder failed: %w", err)
|
||||
}
|
||||
c.decoder = decoder
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GNMI) Start(acc telegraf.Accumulator) error {
|
||||
// Validate configuration
|
||||
request, err := c.newSubscribeRequest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate TLS config if enabled
|
||||
tlscfg, err := c.ClientConfig.TLSConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare the context, optionally with credentials
|
||||
var ctx context.Context
|
||||
ctx, c.cancel = context.WithCancel(context.Background())
|
||||
|
||||
if !c.Username.Empty() {
|
||||
usernameSecret, err := c.Username.Get()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting username failed: %w", err)
|
||||
}
|
||||
username := usernameSecret.String()
|
||||
usernameSecret.Destroy()
|
||||
|
||||
passwordSecret, err := c.Password.Get()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting password failed: %w", err)
|
||||
}
|
||||
password := passwordSecret.String()
|
||||
passwordSecret.Destroy()
|
||||
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, "username", username, "password", password)
|
||||
}
|
||||
|
||||
// Create a goroutine for each device, dial and subscribe
|
||||
c.wg.Add(len(c.Addresses))
|
||||
for _, addr := range c.Addresses {
|
||||
go func(addr string) {
|
||||
defer c.wg.Done()
|
||||
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
acc.AddError(fmt.Errorf("unable to parse address %s: %w", addr, err))
|
||||
return
|
||||
}
|
||||
h := handler{
|
||||
host: host,
|
||||
port: port,
|
||||
aliases: c.internalAliases,
|
||||
tagsubs: c.TagSubscriptions,
|
||||
maxMsgSize: int(c.MaxMsgSize),
|
||||
vendorExt: c.VendorSpecific,
|
||||
tagStore: newTagStore(c.TagSubscriptions),
|
||||
trace: c.Trace,
|
||||
canonicalFieldNames: c.CanonicalFieldNames,
|
||||
trimSlash: c.TrimFieldNames,
|
||||
tagPathPrefix: c.PrefixTagKeyWithPath,
|
||||
guessPathStrategy: c.GuessPathStrategy,
|
||||
decoder: c.decoder,
|
||||
enforceFirstNamespaceAsOrigin: c.EnforceFirstNamespaceAsOrigin,
|
||||
log: c.Log,
|
||||
ClientParameters: keepalive.ClientParameters{
|
||||
Time: time.Duration(c.KeepaliveTime),
|
||||
Timeout: time.Duration(c.KeepaliveTimeout),
|
||||
PermitWithoutStream: false,
|
||||
},
|
||||
}
|
||||
for ctx.Err() == nil {
|
||||
if err := h.subscribeGNMI(ctx, acc, tlscfg, request); err != nil && ctx.Err() == nil {
|
||||
acc.AddError(err)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-time.After(time.Duration(c.Redial)):
|
||||
}
|
||||
}
|
||||
}(addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*GNMI) Gather(telegraf.Accumulator) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GNMI) Stop() {
|
||||
c.cancel()
|
||||
c.wg.Wait()
|
||||
}
|
||||
|
||||
func (s *subscription) buildSubscription() (*gnmi.Subscription, error) {
|
||||
gnmiPath, err := parsePath(s.Origin, s.Path, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode, ok := gnmi.SubscriptionMode_value[strings.ToUpper(s.SubscriptionMode)]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid subscription mode %s", s.SubscriptionMode)
|
||||
}
|
||||
return &gnmi.Subscription{
|
||||
Path: gnmiPath,
|
||||
Mode: gnmi.SubscriptionMode(mode),
|
||||
HeartbeatInterval: uint64(time.Duration(s.HeartbeatInterval).Nanoseconds()),
|
||||
SampleInterval: uint64(time.Duration(s.SampleInterval).Nanoseconds()),
|
||||
SuppressRedundant: s.SuppressRedundant,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create a new gNMI SubscribeRequest
|
||||
func (c *GNMI) newSubscribeRequest() (*gnmi.SubscribeRequest, error) {
|
||||
// Create subscription objects
|
||||
subscriptions := make([]*gnmi.Subscription, 0, len(c.Subscriptions)+len(c.TagSubscriptions))
|
||||
for _, subscription := range c.TagSubscriptions {
|
||||
sub, err := subscription.buildSubscription()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subscriptions = append(subscriptions, sub)
|
||||
}
|
||||
for _, subscription := range c.Subscriptions {
|
||||
sub, err := subscription.buildSubscription()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subscriptions = append(subscriptions, sub)
|
||||
}
|
||||
|
||||
// Construct subscribe request
|
||||
gnmiPath, err := parsePath(c.Origin, c.Prefix, c.Target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Do not provide an empty prefix. Required for Huawei NE40 router v8.21
|
||||
// (and possibly others). See https://github.com/influxdata/telegraf/issues/12273.
|
||||
if gnmiPath.Origin == "" && gnmiPath.Target == "" && len(gnmiPath.Elem) == 0 {
|
||||
gnmiPath = nil
|
||||
}
|
||||
|
||||
if c.Encoding != "proto" && c.Encoding != "json" && c.Encoding != "json_ietf" && c.Encoding != "bytes" {
|
||||
return nil, fmt.Errorf("unsupported encoding %s", c.Encoding)
|
||||
}
|
||||
|
||||
var extensions []*gnmi_ext.Extension
|
||||
if c.Depth > 0 {
|
||||
extensions = []*gnmi_ext.Extension{
|
||||
{
|
||||
Ext: &gnmi_ext.Extension_Depth{
|
||||
Depth: &gnmi_ext.Depth{
|
||||
Level: uint32(c.Depth),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return &gnmi.SubscribeRequest{
|
||||
Request: &gnmi.SubscribeRequest_Subscribe{
|
||||
Subscribe: &gnmi.SubscriptionList{
|
||||
Prefix: gnmiPath,
|
||||
Mode: gnmi.SubscriptionList_STREAM,
|
||||
Encoding: gnmi.Encoding(gnmi.Encoding_value[strings.ToUpper(c.Encoding)]),
|
||||
Subscription: subscriptions,
|
||||
UpdatesOnly: c.UpdatesOnly,
|
||||
},
|
||||
},
|
||||
Extension: extensions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ParsePath from XPath-like string to gNMI path structure
|
||||
func parsePath(origin, pathToParse, target string) (*gnmi.Path, error) {
|
||||
gnmiPath, err := xpath.ToGNMIPath(pathToParse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gnmiPath.Origin = origin
|
||||
gnmiPath.Target = target
|
||||
return gnmiPath, err
|
||||
}
|
||||
|
||||
func (s *subscription) buildFullPath(c *GNMI) error {
|
||||
var err error
|
||||
if s.fullPath, err = xpath.ToGNMIPath(s.Path); err != nil {
|
||||
return err
|
||||
}
|
||||
s.fullPath.Origin = s.Origin
|
||||
s.fullPath.Target = c.Target
|
||||
if c.Prefix != "" {
|
||||
prefix, err := xpath.ToGNMIPath(c.Prefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.fullPath.Elem = append(prefix.Elem, s.fullPath.Elem...)
|
||||
if s.Origin == "" && c.Origin != "" {
|
||||
s.fullPath.Origin = c.Origin
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *subscription) buildAlias(aliases map[*pathInfo]string, enforceFirstNamespaceAsOrigin bool) error {
|
||||
// Build the subscription path without keys
|
||||
path, err := parsePath(s.Origin, s.Path, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info := newInfoFromPathWithoutKeys(path)
|
||||
if enforceFirstNamespaceAsOrigin {
|
||||
info.enforceFirstNamespaceAsOrigin()
|
||||
}
|
||||
|
||||
// If the user didn't provide a measurement name, use last path element
|
||||
name := s.Name
|
||||
if name == "" && len(info.segments) > 0 {
|
||||
name = info.segments[len(info.segments)-1].id
|
||||
}
|
||||
if name != "" {
|
||||
aliases[info] = name
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("gnmi", func() telegraf.Input {
|
||||
return &GNMI{
|
||||
Redial: config.Duration(10 * time.Second),
|
||||
EnforceFirstNamespaceAsOrigin: true,
|
||||
}
|
||||
})
|
||||
// Backwards compatible alias:
|
||||
inputs.Add("cisco_telemetry_gnmi", func() telegraf.Input {
|
||||
return &GNMI{
|
||||
Redial: config.Duration(10 * time.Second),
|
||||
EnforceFirstNamespaceAsOrigin: true,
|
||||
}
|
||||
})
|
||||
}
|
1229
plugins/inputs/gnmi/gnmi_test.go
Normal file
1229
plugins/inputs/gnmi/gnmi_test.go
Normal file
File diff suppressed because it is too large
Load diff
351
plugins/inputs/gnmi/handler.go
Normal file
351
plugins/inputs/gnmi/handler.go
Normal file
|
@ -0,0 +1,351 @@
|
|||
package gnmi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/openconfig/gnmi/proto/gnmi"
|
||||
"github.com/openconfig/gnmi/proto/gnmi_ext"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/internal/choice"
|
||||
"github.com/influxdata/telegraf/metric"
|
||||
"github.com/influxdata/telegraf/plugins/common/yangmodel"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/gnmi/extensions/jnpr_gnmi_extention"
|
||||
"github.com/influxdata/telegraf/selfstat"
|
||||
)
|
||||
|
||||
const eidJuniperTelemetryHeader = 1
|
||||
|
||||
type handler struct {
|
||||
host string
|
||||
port string
|
||||
aliases map[*pathInfo]string
|
||||
tagsubs []tagSubscription
|
||||
maxMsgSize int
|
||||
emptyNameWarnShown bool
|
||||
vendorExt []string
|
||||
tagStore *tagStore
|
||||
trace bool
|
||||
canonicalFieldNames bool
|
||||
trimSlash bool
|
||||
tagPathPrefix bool
|
||||
guessPathStrategy string
|
||||
decoder *yangmodel.Decoder
|
||||
enforceFirstNamespaceAsOrigin bool
|
||||
log telegraf.Logger
|
||||
keepalive.ClientParameters
|
||||
}
|
||||
|
||||
// SubscribeGNMI and extract telemetry data
|
||||
func (h *handler) subscribeGNMI(ctx context.Context, acc telegraf.Accumulator, tlscfg *tls.Config, request *gnmi.SubscribeRequest) error {
|
||||
var creds credentials.TransportCredentials
|
||||
if tlscfg != nil {
|
||||
creds = credentials.NewTLS(tlscfg)
|
||||
} else {
|
||||
creds = insecure.NewCredentials()
|
||||
}
|
||||
opts := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(creds),
|
||||
}
|
||||
|
||||
if h.maxMsgSize > 0 {
|
||||
opts = append(opts, grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallRecvMsgSize(h.maxMsgSize),
|
||||
))
|
||||
}
|
||||
|
||||
if h.ClientParameters.Time > 0 {
|
||||
opts = append(opts, grpc.WithKeepaliveParams(h.ClientParameters))
|
||||
}
|
||||
|
||||
// Used to report the status of the TCP connection to the device. If the
|
||||
// GNMI connection goes down, but TCP is still up this will still report
|
||||
// connected until the TCP connection times out.
|
||||
connectStat := selfstat.Register("gnmi", "grpc_connection_status", map[string]string{"source": h.host})
|
||||
defer connectStat.Set(0)
|
||||
|
||||
address := net.JoinHostPort(h.host, h.port)
|
||||
client, err := grpc.NewClient(address, opts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to dial: %w", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
subscribeClient, err := gnmi.NewGNMIClient(client).Subscribe(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to setup subscription: %w", err)
|
||||
}
|
||||
|
||||
// If io.EOF is returned, the stream may have ended and stream status
|
||||
// can be determined by calling Recv.
|
||||
if err := subscribeClient.Send(request); err != nil && !errors.Is(err, io.EOF) {
|
||||
return fmt.Errorf("failed to send subscription request: %w", err)
|
||||
}
|
||||
connectStat.Set(1)
|
||||
h.log.Debugf("Connection to gNMI device %s established", address)
|
||||
|
||||
defer h.log.Debugf("Connection to gNMI device %s closed", address)
|
||||
for ctx.Err() == nil {
|
||||
var reply *gnmi.SubscribeResponse
|
||||
if reply, err = subscribeClient.Recv(); err != nil {
|
||||
if !errors.Is(err, io.EOF) && ctx.Err() == nil {
|
||||
return fmt.Errorf("aborted gNMI subscription: %w", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if h.trace {
|
||||
buf, err := protojson.Marshal(reply)
|
||||
if err != nil {
|
||||
h.log.Debugf("Marshal failed: %v", err)
|
||||
} else {
|
||||
t := reply.GetUpdate().GetTimestamp()
|
||||
h.log.Debugf("Got update_%v: %s", t, string(buf))
|
||||
}
|
||||
}
|
||||
if response, ok := reply.Response.(*gnmi.SubscribeResponse_Update); ok {
|
||||
h.handleSubscribeResponseUpdate(acc, response, reply.GetExtension())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle SubscribeResponse_Update message from gNMI and parse contained telemetry data
|
||||
func (h *handler) handleSubscribeResponseUpdate(acc telegraf.Accumulator, response *gnmi.SubscribeResponse_Update, extension []*gnmi_ext.Extension) {
|
||||
grouper := metric.NewSeriesGrouper()
|
||||
timestamp := time.Unix(0, response.Update.Timestamp)
|
||||
|
||||
// Extract tags from potential extension in the update notification
|
||||
headerTags := make(map[string]string)
|
||||
for _, ext := range extension {
|
||||
currentExt := ext.GetRegisteredExt().Msg
|
||||
if currentExt == nil {
|
||||
break
|
||||
}
|
||||
|
||||
switch ext.GetRegisteredExt().Id {
|
||||
case eidJuniperTelemetryHeader:
|
||||
// Juniper Header extension
|
||||
// Decode it only if user requested it
|
||||
if choice.Contains("juniper_header", h.vendorExt) {
|
||||
juniperHeader := &jnpr_gnmi_extention.GnmiJuniperTelemetryHeaderExtension{}
|
||||
if err := proto.Unmarshal(currentExt, juniperHeader); err != nil {
|
||||
h.log.Errorf("unmarshal gnmi Juniper Header extension failed: %v", err)
|
||||
} else {
|
||||
// Add only relevant Tags from the Juniper Header extension.
|
||||
// These are required for aggregation
|
||||
headerTags["component_id"] = strconv.FormatUint(uint64(juniperHeader.GetComponentId()), 10)
|
||||
headerTags["component"] = juniperHeader.GetComponent()
|
||||
headerTags["sub_component_id"] = strconv.FormatUint(uint64(juniperHeader.GetSubComponentId()), 10)
|
||||
}
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the path part valid for the whole set of updates if any
|
||||
prefix := newInfoFromPath(response.Update.Prefix)
|
||||
if h.enforceFirstNamespaceAsOrigin {
|
||||
prefix.enforceFirstNamespaceAsOrigin()
|
||||
}
|
||||
|
||||
// Add info to the tags
|
||||
headerTags["source"] = h.host
|
||||
if !prefix.empty() {
|
||||
headerTags["path"] = prefix.fullPath()
|
||||
}
|
||||
|
||||
// Process and remove tag-updates from the response first so we can
|
||||
// add all available tags to the metrics later.
|
||||
var valueFields []updateField
|
||||
for _, update := range response.Update.Update {
|
||||
fullPath := prefix.append(update.Path)
|
||||
if h.enforceFirstNamespaceAsOrigin {
|
||||
prefix.enforceFirstNamespaceAsOrigin()
|
||||
}
|
||||
if update.Path.Origin != "" {
|
||||
fullPath.origin = update.Path.Origin
|
||||
}
|
||||
|
||||
fields, err := h.newFieldsFromUpdate(fullPath, update)
|
||||
if err != nil {
|
||||
h.log.Errorf("Processing update %v failed: %v", update, err)
|
||||
}
|
||||
|
||||
// Prepare tags from prefix
|
||||
tags := make(map[string]string, len(headerTags))
|
||||
for key, val := range headerTags {
|
||||
tags[key] = val
|
||||
}
|
||||
for key, val := range fullPath.tags(h.tagPathPrefix) {
|
||||
tags[key] = val
|
||||
}
|
||||
|
||||
// TODO: Handle each field individually to allow in-JSON tags
|
||||
var tagUpdate bool
|
||||
for _, tagSub := range h.tagsubs {
|
||||
if !fullPath.equalsPathNoKeys(tagSub.fullPath) {
|
||||
continue
|
||||
}
|
||||
h.log.Debugf("Tag-subscription update for %q: %+v", tagSub.Name, update)
|
||||
if err := h.tagStore.insert(tagSub, fullPath, fields, tags); err != nil {
|
||||
h.log.Errorf("Inserting tag failed: %v", err)
|
||||
}
|
||||
tagUpdate = true
|
||||
break
|
||||
}
|
||||
if !tagUpdate {
|
||||
valueFields = append(valueFields, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
// Some devices do not provide a prefix, so do some guesswork based
|
||||
// on the paths of the fields
|
||||
if headerTags["path"] == "" && h.guessPathStrategy == "common path" {
|
||||
if prefixPath := guessPrefixFromUpdate(valueFields); prefixPath != "" {
|
||||
headerTags["path"] = prefixPath
|
||||
}
|
||||
}
|
||||
|
||||
// Parse individual update message and create measurements
|
||||
for _, field := range valueFields {
|
||||
if field.path.empty() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Prepare tags from prefix
|
||||
fieldTags := field.path.tags(h.tagPathPrefix)
|
||||
tags := make(map[string]string, len(headerTags)+len(fieldTags))
|
||||
for key, val := range headerTags {
|
||||
tags[key] = val
|
||||
}
|
||||
for key, val := range fieldTags {
|
||||
tags[key] = val
|
||||
}
|
||||
|
||||
// Add the tags derived via tag-subscriptions
|
||||
for k, v := range h.tagStore.lookup(field.path, tags) {
|
||||
tags[k] = v
|
||||
}
|
||||
|
||||
// Lookup alias for the metric
|
||||
aliasPath, name := h.lookupAlias(field.path)
|
||||
if name == "" {
|
||||
h.log.Debugf("No measurement alias for gNMI path: %s", field.path)
|
||||
if !h.emptyNameWarnShown {
|
||||
if buf, err := json.Marshal(response); err == nil {
|
||||
h.log.Warnf(emptyNameWarning, field.path, string(buf))
|
||||
} else {
|
||||
h.log.Warnf(emptyNameWarning, field.path, response.Update)
|
||||
}
|
||||
h.emptyNameWarnShown = true
|
||||
}
|
||||
}
|
||||
|
||||
aliasInfo := newInfoFromString(aliasPath)
|
||||
if h.enforceFirstNamespaceAsOrigin {
|
||||
aliasInfo.enforceFirstNamespaceAsOrigin()
|
||||
}
|
||||
|
||||
if tags["path"] == "" && h.guessPathStrategy == "subscription" {
|
||||
tags["path"] = aliasInfo.String()
|
||||
}
|
||||
|
||||
// Group metrics
|
||||
var key string
|
||||
if h.canonicalFieldNames {
|
||||
// Strip the origin is any for the field names
|
||||
field.path.origin = ""
|
||||
key = field.path.String()
|
||||
key = strings.ReplaceAll(key, "-", "_")
|
||||
} else {
|
||||
// If the alias is a subpath of the field path and the alias is
|
||||
// shorter than the full path to avoid an empty key, then strip the
|
||||
// common part of the field is prefixed with the alias path. Note
|
||||
// the origins can match or be empty and be considered equal.
|
||||
if relative := aliasInfo.relative(field.path, true); relative != "" {
|
||||
key = relative
|
||||
} else {
|
||||
// Otherwise use the last path element as the field key
|
||||
key = field.path.base()
|
||||
}
|
||||
key = strings.ReplaceAll(key, "-", "_")
|
||||
}
|
||||
if h.trimSlash {
|
||||
key = strings.TrimLeft(key, "/.")
|
||||
}
|
||||
if key == "" {
|
||||
h.log.Errorf("Invalid empty path %q with alias %q", field.path.String(), aliasPath)
|
||||
continue
|
||||
}
|
||||
grouper.Add(name, tags, timestamp, key, field.value)
|
||||
}
|
||||
|
||||
// Add grouped measurements
|
||||
for _, metricToAdd := range grouper.Metrics() {
|
||||
acc.AddMetric(metricToAdd)
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find the alias for the given path
|
||||
type aliasCandidate struct {
|
||||
path, alias string
|
||||
}
|
||||
|
||||
func (h *handler) lookupAlias(info *pathInfo) (aliasPath, alias string) {
|
||||
candidates := make([]aliasCandidate, 0)
|
||||
for i, a := range h.aliases {
|
||||
if !i.isSubPathOf(info) {
|
||||
continue
|
||||
}
|
||||
candidates = append(candidates, aliasCandidate{i.String(), a})
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// Reverse sort the candidates by path length so we can use the longest match
|
||||
sort.SliceStable(candidates, func(i, j int) bool {
|
||||
return len(candidates[i].path) > len(candidates[j].path)
|
||||
})
|
||||
|
||||
return candidates[0].path, candidates[0].alias
|
||||
}
|
||||
|
||||
func guessPrefixFromUpdate(fields []updateField) string {
|
||||
if len(fields) == 0 {
|
||||
return ""
|
||||
}
|
||||
if len(fields) == 1 {
|
||||
return fields[0].path.dir()
|
||||
}
|
||||
segments := make([]segment, 0, len(fields[0].path.segments))
|
||||
commonPath := &pathInfo{
|
||||
origin: fields[0].path.origin,
|
||||
segments: append(segments, fields[0].path.segments...),
|
||||
}
|
||||
for _, f := range fields[1:] {
|
||||
commonPath.keepCommonPart(f.path)
|
||||
}
|
||||
if commonPath.empty() {
|
||||
return ""
|
||||
}
|
||||
return commonPath.String()
|
||||
}
|
403
plugins/inputs/gnmi/path.go
Normal file
403
plugins/inputs/gnmi/path.go
Normal file
|
@ -0,0 +1,403 @@
|
|||
package gnmi
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/openconfig/gnmi/proto/gnmi"
|
||||
)
|
||||
|
||||
type keySegment struct {
|
||||
name string
|
||||
path string
|
||||
kv map[string]string
|
||||
}
|
||||
|
||||
type segment struct {
|
||||
namespace string
|
||||
id string
|
||||
}
|
||||
|
||||
type pathInfo struct {
|
||||
origin string
|
||||
target string
|
||||
segments []segment
|
||||
keyValues []keySegment
|
||||
}
|
||||
|
||||
func newInfoFromString(path string) *pathInfo {
|
||||
if path == "" {
|
||||
return &pathInfo{}
|
||||
}
|
||||
|
||||
parts := strings.Split(path, "/")
|
||||
|
||||
var origin string
|
||||
if strings.HasSuffix(parts[0], ":") {
|
||||
origin = strings.TrimSuffix(parts[0], ":")
|
||||
parts = parts[1:]
|
||||
}
|
||||
|
||||
info := &pathInfo{origin: origin}
|
||||
for _, part := range parts {
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
info.segments = append(info.segments, segment{id: part})
|
||||
}
|
||||
info.normalize()
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
func newInfoFromPathWithoutKeys(path *gnmi.Path) *pathInfo {
|
||||
info := &pathInfo{
|
||||
origin: path.Origin,
|
||||
segments: make([]segment, 0, len(path.Elem)),
|
||||
}
|
||||
for _, elem := range path.Elem {
|
||||
if elem.Name == "" {
|
||||
continue
|
||||
}
|
||||
info.segments = append(info.segments, segment{id: elem.Name})
|
||||
}
|
||||
info.normalize()
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
func newInfoFromPath(paths ...*gnmi.Path) *pathInfo {
|
||||
if len(paths) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
info := &pathInfo{}
|
||||
if paths[0] != nil {
|
||||
info.origin = paths[0].Origin
|
||||
info.target = paths[0].Target
|
||||
}
|
||||
|
||||
for _, p := range paths {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
for _, elem := range p.Elem {
|
||||
if elem.Name != "" {
|
||||
info.segments = append(info.segments, segment{id: elem.Name})
|
||||
}
|
||||
|
||||
if len(elem.Key) == 0 {
|
||||
continue
|
||||
}
|
||||
keyInfo := keySegment{
|
||||
name: elem.Name,
|
||||
path: info.String(),
|
||||
kv: make(map[string]string, len(elem.Key)),
|
||||
}
|
||||
for k, v := range elem.Key {
|
||||
keyInfo.kv[k] = v
|
||||
}
|
||||
info.keyValues = append(info.keyValues, keyInfo)
|
||||
}
|
||||
}
|
||||
info.normalize()
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
func (pi *pathInfo) empty() bool {
|
||||
return len(pi.segments) == 0
|
||||
}
|
||||
|
||||
func (pi *pathInfo) append(paths ...*gnmi.Path) *pathInfo {
|
||||
// Copy the existing info
|
||||
segments := make([]segment, 0, len(pi.segments))
|
||||
path := &pathInfo{
|
||||
origin: pi.origin,
|
||||
target: pi.target,
|
||||
segments: append(segments, pi.segments...),
|
||||
keyValues: make([]keySegment, 0, len(pi.keyValues)),
|
||||
}
|
||||
for _, elem := range pi.keyValues {
|
||||
keyInfo := keySegment{
|
||||
name: elem.name,
|
||||
path: elem.path,
|
||||
kv: make(map[string]string, len(elem.kv)),
|
||||
}
|
||||
for k, v := range elem.kv {
|
||||
keyInfo.kv[k] = v
|
||||
}
|
||||
path.keyValues = append(path.keyValues, keyInfo)
|
||||
}
|
||||
|
||||
// Add the new segments
|
||||
for _, p := range paths {
|
||||
for _, elem := range p.Elem {
|
||||
if elem.Name != "" {
|
||||
path.segments = append(path.segments, segment{id: elem.Name})
|
||||
}
|
||||
|
||||
if len(elem.Key) == 0 {
|
||||
continue
|
||||
}
|
||||
keyInfo := keySegment{
|
||||
name: elem.Name,
|
||||
path: path.String(),
|
||||
kv: make(map[string]string, len(elem.Key)),
|
||||
}
|
||||
for k, v := range elem.Key {
|
||||
keyInfo.kv[k] = v
|
||||
}
|
||||
path.keyValues = append(path.keyValues, keyInfo)
|
||||
}
|
||||
}
|
||||
path.normalize()
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
func (pi *pathInfo) appendSegments(segments ...string) *pathInfo {
|
||||
// Copy the existing info
|
||||
seg := make([]segment, 0, len(segments))
|
||||
path := &pathInfo{
|
||||
origin: pi.origin,
|
||||
target: pi.target,
|
||||
segments: append(seg, pi.segments...),
|
||||
keyValues: make([]keySegment, 0, len(pi.keyValues)),
|
||||
}
|
||||
for _, elem := range pi.keyValues {
|
||||
keyInfo := keySegment{
|
||||
name: elem.name,
|
||||
path: elem.path,
|
||||
kv: make(map[string]string, len(elem.kv)),
|
||||
}
|
||||
for k, v := range elem.kv {
|
||||
keyInfo.kv[k] = v
|
||||
}
|
||||
path.keyValues = append(path.keyValues, keyInfo)
|
||||
}
|
||||
|
||||
// Add the new segments
|
||||
for _, s := range segments {
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
path.segments = append(path.segments, segment{id: s})
|
||||
}
|
||||
path.normalize()
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
func (pi *pathInfo) normalize() {
|
||||
if len(pi.segments) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Extract namespaces from segments
|
||||
for i, s := range pi.segments {
|
||||
if ns, id, found := strings.Cut(s.id, ":"); found {
|
||||
pi.segments[i].namespace = ns
|
||||
pi.segments[i].id = id
|
||||
}
|
||||
}
|
||||
|
||||
// Remove empty segments
|
||||
segments := make([]segment, 0, len(pi.segments))
|
||||
for _, s := range pi.segments {
|
||||
if s.id != "" {
|
||||
segments = append(segments, s)
|
||||
}
|
||||
}
|
||||
pi.segments = segments
|
||||
}
|
||||
|
||||
func (pi *pathInfo) enforceFirstNamespaceAsOrigin() {
|
||||
if len(pi.segments) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Some devices supply the origin as part of the first path element,
|
||||
// so try to find and extract it there.
|
||||
if pi.segments[0].namespace != "" {
|
||||
pi.origin = pi.segments[0].namespace
|
||||
pi.segments[0].namespace = ""
|
||||
}
|
||||
}
|
||||
|
||||
func (pi *pathInfo) equalsPathNoKeys(path *gnmi.Path) bool {
|
||||
if len(pi.segments) != len(path.Elem) {
|
||||
return false
|
||||
}
|
||||
for i, s := range pi.segments {
|
||||
if s.id != path.Elem[i].Name {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (pi *pathInfo) isSubPathOf(path *pathInfo) bool {
|
||||
// If both set an origin it has to match. Otherwise we ignore the origin
|
||||
if pi.origin != "" && path.origin != "" && pi.origin != path.origin {
|
||||
return false
|
||||
}
|
||||
|
||||
// The "parent" path should have the same length or be shorter than the
|
||||
// sub-path to have a chance to match
|
||||
if len(pi.segments) > len(path.segments) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Compare the elements and exit if we find a mismatch
|
||||
for i, p := range pi.segments {
|
||||
ps := path.segments[i]
|
||||
if p.namespace != "" && ps.namespace != "" && p.namespace != ps.namespace {
|
||||
return false
|
||||
}
|
||||
if p.id != ps.id {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (pi *pathInfo) relative(path *pathInfo, withNamespace bool) string {
|
||||
if !pi.isSubPathOf(path) || len(pi.segments) == len(path.segments) {
|
||||
return ""
|
||||
}
|
||||
|
||||
segments := path.segments[len(pi.segments):len(path.segments)]
|
||||
var r string
|
||||
if withNamespace && segments[0].namespace != "" {
|
||||
r = segments[0].namespace + ":" + segments[0].id
|
||||
} else {
|
||||
r = segments[0].id
|
||||
}
|
||||
for _, s := range segments[1:] {
|
||||
if withNamespace && s.namespace != "" {
|
||||
r += "/" + s.namespace + ":" + s.id
|
||||
} else {
|
||||
r += "/" + s.id
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (pi *pathInfo) keepCommonPart(path *pathInfo) {
|
||||
shortestLen := len(pi.segments)
|
||||
if len(path.segments) < shortestLen {
|
||||
shortestLen = len(path.segments)
|
||||
}
|
||||
|
||||
// Compare the elements and stop as soon as they do mismatch
|
||||
var matchLen int
|
||||
for i, p := range pi.segments[:shortestLen] {
|
||||
if p != path.segments[i] {
|
||||
break
|
||||
}
|
||||
matchLen = i + 1
|
||||
}
|
||||
if matchLen < 1 {
|
||||
pi.segments = nil
|
||||
return
|
||||
}
|
||||
pi.segments = pi.segments[:matchLen]
|
||||
}
|
||||
|
||||
func (pi *pathInfo) dir() string {
|
||||
if len(pi.segments) <= 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var dir string
|
||||
if pi.origin != "" {
|
||||
dir = pi.origin + ":"
|
||||
}
|
||||
for _, s := range pi.segments[:len(pi.segments)-1] {
|
||||
if s.namespace != "" {
|
||||
dir += "/" + s.namespace + ":" + s.id
|
||||
} else {
|
||||
dir += "/" + s.id
|
||||
}
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
func (pi *pathInfo) base() string {
|
||||
if len(pi.segments) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
s := pi.segments[len(pi.segments)-1]
|
||||
if s.namespace != "" {
|
||||
return s.namespace + ":" + s.id
|
||||
}
|
||||
return s.id
|
||||
}
|
||||
|
||||
func (pi *pathInfo) path() (origin, path string) {
|
||||
if len(pi.segments) == 0 {
|
||||
return pi.origin, "/"
|
||||
}
|
||||
|
||||
for _, s := range pi.segments {
|
||||
path += "/" + s.id
|
||||
}
|
||||
|
||||
return pi.origin, path
|
||||
}
|
||||
|
||||
func (pi *pathInfo) fullPath() string {
|
||||
var path string
|
||||
if pi.origin != "" {
|
||||
path = pi.origin + ":"
|
||||
}
|
||||
if len(pi.segments) == 0 {
|
||||
return path
|
||||
}
|
||||
|
||||
for _, s := range pi.segments {
|
||||
if s.namespace != "" {
|
||||
path += "/" + s.namespace + ":" + s.id
|
||||
} else {
|
||||
path += "/" + s.id
|
||||
}
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
func (pi *pathInfo) String() string {
|
||||
if len(pi.segments) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
origin, path := pi.path()
|
||||
if origin != "" {
|
||||
return origin + ":" + path
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func (pi *pathInfo) tags(pathPrefix bool) map[string]string {
|
||||
tags := make(map[string]string, len(pi.keyValues))
|
||||
for _, s := range pi.keyValues {
|
||||
var prefix string
|
||||
if pathPrefix && s.name != "" {
|
||||
prefix = s.name + "_"
|
||||
}
|
||||
for k, v := range s.kv {
|
||||
key := strings.ReplaceAll(prefix+k, "-", "_")
|
||||
|
||||
// Use short-form of key if possible
|
||||
if _, exists := tags[key]; !exists {
|
||||
tags[key] = v
|
||||
continue
|
||||
}
|
||||
tags[s.path+"/"+key] = v
|
||||
}
|
||||
}
|
||||
|
||||
return tags
|
||||
}
|
158
plugins/inputs/gnmi/sample.conf
Normal file
158
plugins/inputs/gnmi/sample.conf
Normal file
|
@ -0,0 +1,158 @@
|
|||
# gNMI telemetry input plugin
|
||||
[[inputs.gnmi]]
|
||||
## Address and port of the gNMI GRPC server
|
||||
addresses = ["10.49.234.114:57777"]
|
||||
|
||||
## define credentials
|
||||
username = "cisco"
|
||||
password = "cisco"
|
||||
|
||||
## gNMI encoding requested (one of: "proto", "json", "json_ietf", "bytes")
|
||||
# encoding = "proto"
|
||||
|
||||
## redial in case of failures after
|
||||
# redial = "10s"
|
||||
|
||||
## gRPC Keepalive settings
|
||||
## See https://pkg.go.dev/google.golang.org/grpc/keepalive
|
||||
## The client will ping the server to see if the transport is still alive if it has
|
||||
## not see any activity for the given time.
|
||||
## If not set, none of the keep-alive setting (including those below) will be applied.
|
||||
## If set and set below 10 seconds, the gRPC library will apply a minimum value of 10s will be used instead.
|
||||
# keepalive_time = ""
|
||||
|
||||
## Timeout for seeing any activity after the keep-alive probe was
|
||||
## sent. If no activity is seen the connection is closed.
|
||||
# keepalive_timeout = ""
|
||||
|
||||
## gRPC Maximum Message Size
|
||||
# max_msg_size = "4MB"
|
||||
|
||||
## Subtree depth for depth extension (disables if < 1)
|
||||
## see https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-depth.md
|
||||
# depth = 0
|
||||
|
||||
## Enable to get the canonical path as field-name
|
||||
# canonical_field_names = false
|
||||
|
||||
## Remove leading slashes and dots in field-name
|
||||
# trim_field_names = false
|
||||
|
||||
## Only receive updates for the state, also suppresses receiving the initial state
|
||||
# updates_only = false
|
||||
|
||||
## Enforces the namespace of the first element as origin for aliases and
|
||||
## response paths, required for backward compatibility.
|
||||
## NOTE: Set to 'false' if possible but be aware that this might change the path tag!
|
||||
# enforce_first_namespace_as_origin = true
|
||||
|
||||
## Guess the path-tag if an update does not contain a prefix-path
|
||||
## Supported values are
|
||||
## none -- do not add a 'path' tag
|
||||
## common path -- use the common path elements of all fields in an update
|
||||
## subscription -- use the subscription path
|
||||
# path_guessing_strategy = "none"
|
||||
|
||||
## Prefix tags from path keys with the path element
|
||||
# prefix_tag_key_with_path = false
|
||||
|
||||
## Optional client-side TLS to authenticate the device
|
||||
## Set to true/false to enforce TLS being enabled/disabled. If not set,
|
||||
## enable TLS only if any of the other options are specified.
|
||||
# tls_enable =
|
||||
## Trusted root certificates for server
|
||||
# tls_ca = "/path/to/cafile"
|
||||
## Used for TLS client certificate authentication
|
||||
# tls_cert = "/path/to/certfile"
|
||||
## Used for TLS client certificate authentication
|
||||
# tls_key = "/path/to/keyfile"
|
||||
## Password for the key file if it is encrypted
|
||||
# tls_key_pwd = ""
|
||||
## Send the specified TLS server name via SNI
|
||||
# tls_server_name = "kubernetes.example.com"
|
||||
## Minimal TLS version to accept by the client
|
||||
# tls_min_version = "TLS12"
|
||||
## List of ciphers to accept, by default all secure ciphers will be accepted
|
||||
## See https://pkg.go.dev/crypto/tls#pkg-constants for supported values.
|
||||
## Use "all", "secure" and "insecure" to add all support ciphers, secure
|
||||
## suites or insecure suites respectively.
|
||||
# tls_cipher_suites = ["secure"]
|
||||
## Renegotiation method, "never", "once" or "freely"
|
||||
# tls_renegotiation_method = "never"
|
||||
## Use TLS but skip chain & host verification
|
||||
# insecure_skip_verify = false
|
||||
|
||||
## gNMI subscription prefix (optional, can usually be left empty)
|
||||
## See: https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md#222-paths
|
||||
# origin = ""
|
||||
# prefix = ""
|
||||
# target = ""
|
||||
|
||||
## Vendor specific options
|
||||
## This defines what vendor specific options to load.
|
||||
## * Juniper Header Extension (juniper_header): some sensors are directly managed by
|
||||
## Linecard, which adds the Juniper GNMI Header Extension. Enabling this
|
||||
## allows the decoding of the Extension header if present. Currently this knob
|
||||
## adds component, component_id & sub_component_id as additional tags
|
||||
# vendor_specific = []
|
||||
|
||||
## YANG model paths for decoding IETF JSON payloads
|
||||
## Model files are loaded recursively from the given directories. Disabled if
|
||||
## no models are specified.
|
||||
# yang_model_paths = []
|
||||
|
||||
## Define additional aliases to map encoding paths to measurement names
|
||||
# [inputs.gnmi.aliases]
|
||||
# ifcounters = "openconfig:/interfaces/interface/state/counters"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
## Name of the measurement that will be emitted
|
||||
name = "ifcounters"
|
||||
|
||||
## Origin and path of the subscription
|
||||
## See: https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md#222-paths
|
||||
##
|
||||
## origin usually refers to a (YANG) data model implemented by the device
|
||||
## and path to a specific substructure inside it that should be subscribed
|
||||
## to (similar to an XPath). YANG models can be found e.g. here:
|
||||
## https://github.com/YangModels/yang/tree/master/vendor/cisco/xr
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/state/counters"
|
||||
|
||||
## Subscription mode ("target_defined", "sample", "on_change") and interval
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
|
||||
## Suppress redundant transmissions when measured values are unchanged
|
||||
# suppress_redundant = false
|
||||
|
||||
## If suppression is enabled, send updates at least every X seconds anyway
|
||||
# heartbeat_interval = "60s"
|
||||
|
||||
## Tag subscriptions are applied as tags to other subscriptions.
|
||||
# [[inputs.gnmi.tag_subscription]]
|
||||
# ## When applying this value as a tag to other metrics, use this tag name
|
||||
# name = "descr"
|
||||
#
|
||||
# ## All other subscription fields are as normal
|
||||
# origin = "openconfig-interfaces"
|
||||
# path = "/interfaces/interface/state"
|
||||
# subscription_mode = "on_change"
|
||||
#
|
||||
# ## Match strategy to use for the tag.
|
||||
# ## Tags are only applied for metrics of the same address. The following
|
||||
# ## settings are valid:
|
||||
# ## unconditional -- always match
|
||||
# ## name -- match by the "name" key
|
||||
# ## This resembles the previous 'tag-only' behavior.
|
||||
# ## elements -- match by the keys in the path filtered by the path
|
||||
# ## parts specified `elements` below
|
||||
# ## By default, 'elements' is used if the 'elements' option is provided,
|
||||
# ## otherwise match by 'name'.
|
||||
# # match = ""
|
||||
#
|
||||
# ## For the 'elements' match strategy, at least one path-element name must
|
||||
# ## be supplied containing at least one key to match on. Multiple path
|
||||
# ## elements can be specified in any order. All given keys must be equal
|
||||
# ## for a match.
|
||||
# # elements = ["description", "interface"]
|
135
plugins/inputs/gnmi/sample.conf.in
Normal file
135
plugins/inputs/gnmi/sample.conf.in
Normal file
|
@ -0,0 +1,135 @@
|
|||
# gNMI telemetry input plugin
|
||||
[[inputs.gnmi]]
|
||||
## Address and port of the gNMI GRPC server
|
||||
addresses = ["10.49.234.114:57777"]
|
||||
|
||||
## define credentials
|
||||
username = "cisco"
|
||||
password = "cisco"
|
||||
|
||||
## gNMI encoding requested (one of: "proto", "json", "json_ietf", "bytes")
|
||||
# encoding = "proto"
|
||||
|
||||
## redial in case of failures after
|
||||
# redial = "10s"
|
||||
|
||||
## gRPC Keepalive settings
|
||||
## See https://pkg.go.dev/google.golang.org/grpc/keepalive
|
||||
## The client will ping the server to see if the transport is still alive if it has
|
||||
## not see any activity for the given time.
|
||||
## If not set, none of the keep-alive setting (including those below) will be applied.
|
||||
## If set and set below 10 seconds, the gRPC library will apply a minimum value of 10s will be used instead.
|
||||
# keepalive_time = ""
|
||||
|
||||
## Timeout for seeing any activity after the keep-alive probe was
|
||||
## sent. If no activity is seen the connection is closed.
|
||||
# keepalive_timeout = ""
|
||||
|
||||
## gRPC Maximum Message Size
|
||||
# max_msg_size = "4MB"
|
||||
|
||||
## Subtree depth for depth extension (disables if < 1)
|
||||
## see https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-depth.md
|
||||
# depth = 0
|
||||
|
||||
## Enable to get the canonical path as field-name
|
||||
# canonical_field_names = false
|
||||
|
||||
## Remove leading slashes and dots in field-name
|
||||
# trim_field_names = false
|
||||
|
||||
## Only receive updates for the state, also suppresses receiving the initial state
|
||||
# updates_only = false
|
||||
|
||||
## Enforces the namespace of the first element as origin for aliases and
|
||||
## response paths, required for backward compatibility.
|
||||
## NOTE: Set to 'false' if possible but be aware that this might change the path tag!
|
||||
# enforce_first_namespace_as_origin = true
|
||||
|
||||
## Guess the path-tag if an update does not contain a prefix-path
|
||||
## Supported values are
|
||||
## none -- do not add a 'path' tag
|
||||
## common path -- use the common path elements of all fields in an update
|
||||
## subscription -- use the subscription path
|
||||
# path_guessing_strategy = "none"
|
||||
|
||||
## Prefix tags from path keys with the path element
|
||||
# prefix_tag_key_with_path = false
|
||||
|
||||
## Optional client-side TLS to authenticate the device
|
||||
{{template "/plugins/common/tls/client.conf"}}
|
||||
|
||||
## gNMI subscription prefix (optional, can usually be left empty)
|
||||
## See: https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md#222-paths
|
||||
# origin = ""
|
||||
# prefix = ""
|
||||
# target = ""
|
||||
|
||||
## Vendor specific options
|
||||
## This defines what vendor specific options to load.
|
||||
## * Juniper Header Extension (juniper_header): some sensors are directly managed by
|
||||
## Linecard, which adds the Juniper GNMI Header Extension. Enabling this
|
||||
## allows the decoding of the Extension header if present. Currently this knob
|
||||
## adds component, component_id & sub_component_id as additional tags
|
||||
# vendor_specific = []
|
||||
|
||||
## YANG model paths for decoding IETF JSON payloads
|
||||
## Model files are loaded recursively from the given directories. Disabled if
|
||||
## no models are specified.
|
||||
# yang_model_paths = []
|
||||
|
||||
## Define additional aliases to map encoding paths to measurement names
|
||||
# [inputs.gnmi.aliases]
|
||||
# ifcounters = "openconfig:/interfaces/interface/state/counters"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
## Name of the measurement that will be emitted
|
||||
name = "ifcounters"
|
||||
|
||||
## Origin and path of the subscription
|
||||
## See: https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md#222-paths
|
||||
##
|
||||
## origin usually refers to a (YANG) data model implemented by the device
|
||||
## and path to a specific substructure inside it that should be subscribed
|
||||
## to (similar to an XPath). YANG models can be found e.g. here:
|
||||
## https://github.com/YangModels/yang/tree/master/vendor/cisco/xr
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/state/counters"
|
||||
|
||||
## Subscription mode ("target_defined", "sample", "on_change") and interval
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
|
||||
## Suppress redundant transmissions when measured values are unchanged
|
||||
# suppress_redundant = false
|
||||
|
||||
## If suppression is enabled, send updates at least every X seconds anyway
|
||||
# heartbeat_interval = "60s"
|
||||
|
||||
## Tag subscriptions are applied as tags to other subscriptions.
|
||||
# [[inputs.gnmi.tag_subscription]]
|
||||
# ## When applying this value as a tag to other metrics, use this tag name
|
||||
# name = "descr"
|
||||
#
|
||||
# ## All other subscription fields are as normal
|
||||
# origin = "openconfig-interfaces"
|
||||
# path = "/interfaces/interface/state"
|
||||
# subscription_mode = "on_change"
|
||||
#
|
||||
# ## Match strategy to use for the tag.
|
||||
# ## Tags are only applied for metrics of the same address. The following
|
||||
# ## settings are valid:
|
||||
# ## unconditional -- always match
|
||||
# ## name -- match by the "name" key
|
||||
# ## This resembles the previous 'tag-only' behavior.
|
||||
# ## elements -- match by the keys in the path filtered by the path
|
||||
# ## parts specified `elements` below
|
||||
# ## By default, 'elements' is used if the 'elements' option is provided,
|
||||
# ## otherwise match by 'name'.
|
||||
# # match = ""
|
||||
#
|
||||
# ## For the 'elements' match strategy, at least one path-element name must
|
||||
# ## be supplied containing at least one key to match on. Multiple path
|
||||
# ## elements can be specified in any order. All given keys must be equal
|
||||
# ## for a match.
|
||||
# # elements = ["description", "interface"]
|
188
plugins/inputs/gnmi/tag_store.go
Normal file
188
plugins/inputs/gnmi/tag_store.go
Normal file
|
@ -0,0 +1,188 @@
|
|||
package gnmi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/influxdata/telegraf/internal"
|
||||
)
|
||||
|
||||
type tagStore struct {
|
||||
unconditional map[string]string
|
||||
names map[string]map[string]string
|
||||
elements elementsStore
|
||||
}
|
||||
|
||||
type elementsStore struct {
|
||||
required [][]string
|
||||
tags map[string]map[string]string
|
||||
}
|
||||
|
||||
func newTagStore(subs []tagSubscription) *tagStore {
|
||||
store := tagStore{
|
||||
unconditional: make(map[string]string),
|
||||
names: make(map[string]map[string]string),
|
||||
elements: elementsStore{
|
||||
required: make([][]string, 0, len(subs)),
|
||||
tags: make(map[string]map[string]string),
|
||||
},
|
||||
}
|
||||
for _, s := range subs {
|
||||
if s.Match == "elements" {
|
||||
store.elements.required = append(store.elements.required, s.Elements)
|
||||
}
|
||||
}
|
||||
|
||||
return &store
|
||||
}
|
||||
|
||||
// Store tags extracted from TagSubscriptions
|
||||
func (s *tagStore) insert(subscription tagSubscription, path *pathInfo, values []updateField, tags map[string]string) error {
|
||||
switch subscription.Match {
|
||||
case "unconditional":
|
||||
for _, f := range values {
|
||||
tagName := subscription.Name
|
||||
if len(f.path.segments) > 0 {
|
||||
key := f.path.base()
|
||||
key = strings.ReplaceAll(key, "-", "_")
|
||||
tagName += "/" + key
|
||||
}
|
||||
sv, err := internal.ToString(f.value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("conversion error for %v: %w", f.value, err)
|
||||
}
|
||||
if sv == "" {
|
||||
delete(s.unconditional, tagName)
|
||||
} else {
|
||||
s.unconditional[tagName] = sv
|
||||
}
|
||||
}
|
||||
case "name":
|
||||
// Get the lookup key
|
||||
key, found := tags["name"]
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Make sure we have a valid map for the key
|
||||
if _, exists := s.names[key]; !exists {
|
||||
s.names[key] = make(map[string]string)
|
||||
}
|
||||
|
||||
// Add the values
|
||||
for _, f := range values {
|
||||
tagName := subscription.Name
|
||||
if len(f.path.segments) > 0 {
|
||||
key := f.path.base()
|
||||
key = strings.ReplaceAll(key, "-", "_")
|
||||
tagName += "/" + key
|
||||
}
|
||||
sv, err := internal.ToString(f.value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("conversion error for %v: %w", f.value, err)
|
||||
}
|
||||
if sv == "" {
|
||||
delete(s.names[key], tagName)
|
||||
} else {
|
||||
s.names[key][tagName] = sv
|
||||
}
|
||||
}
|
||||
case "elements":
|
||||
key, match := getElementsKeys(path, subscription.Elements)
|
||||
if !match || len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Make sure we have a valid map for the key
|
||||
if _, exists := s.elements.tags[key]; !exists {
|
||||
s.elements.tags[key] = make(map[string]string)
|
||||
}
|
||||
|
||||
// Add the values
|
||||
for _, f := range values {
|
||||
tagName := subscription.Name
|
||||
if len(f.path.segments) > 0 {
|
||||
key := f.path.base()
|
||||
key = strings.ReplaceAll(key, "-", "_")
|
||||
tagName += "/" + key
|
||||
}
|
||||
sv, err := internal.ToString(f.value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("conversion error for %v: %w", f.value, err)
|
||||
}
|
||||
if sv == "" {
|
||||
delete(s.elements.tags[key], tagName)
|
||||
} else {
|
||||
s.elements.tags[key][tagName] = sv
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown match strategy %q", subscription.Match)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *tagStore) lookup(path *pathInfo, metricTags map[string]string) map[string]string {
|
||||
// Add all unconditional tags
|
||||
tags := make(map[string]string, len(s.unconditional))
|
||||
for k, v := range s.unconditional {
|
||||
tags[k] = v
|
||||
}
|
||||
|
||||
// Match names
|
||||
key, found := metricTags["name"]
|
||||
if found {
|
||||
for k, v := range s.names[key] {
|
||||
tags[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Match elements
|
||||
for _, requiredKeys := range s.elements.required {
|
||||
key, match := getElementsKeys(path, requiredKeys)
|
||||
if !match {
|
||||
continue
|
||||
}
|
||||
for k, v := range s.elements.tags[key] {
|
||||
tags[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
func getElementsKeys(path *pathInfo, elements []string) (string, bool) {
|
||||
// Search for the required path elements and collect a ordered
|
||||
// list of their values to in the form
|
||||
// elementName1={keyA=valueA,keyB=valueB,...},...,elementNameN={keyY=valueY,keyZ=valueZ}
|
||||
// where each elements' key-value list is enclosed in curly brackets.
|
||||
keyParts := make([]string, 0, len(elements))
|
||||
for _, requiredElement := range elements {
|
||||
var found bool
|
||||
var elementKVs []string
|
||||
for _, segment := range path.keyValues {
|
||||
if segment.name == requiredElement {
|
||||
for k, v := range segment.kv {
|
||||
elementKVs = append(elementKVs, k+"="+v)
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// The element was not found, but all must match
|
||||
if !found {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// We need to order the element's key-value pairs as the map
|
||||
// returns elements in random order
|
||||
sort.Strings(elementKVs)
|
||||
|
||||
// Collect the element
|
||||
keyParts = append(keyParts, requiredElement+"={"+strings.Join(elementKVs, ",")+"}")
|
||||
}
|
||||
return strings.Join(keyParts, ","), true
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
interfaces,name=eth42,path=openconfig-interfaces:/interfaces/interface,source=127.0.0.1 /interfaces/interface/descr="eth42",/interfaces/interface/config/id=42425u,/interfaces/interface/state/in_pkts=5678u,/interfaces/interface/state/out_pkts=5125u 1673608605875353770
|
|
@ -0,0 +1,79 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1673608605875353770",
|
||||
"prefix": {
|
||||
"origin": "openconfig-interfaces",
|
||||
"elem": [
|
||||
{
|
||||
"name": "interfaces"
|
||||
},
|
||||
{
|
||||
"name": "interface",
|
||||
"key":{"name":"eth42"}
|
||||
}
|
||||
],
|
||||
"target": "subscription"
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "descr"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"stringVal": "eth42"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "config"
|
||||
},
|
||||
{
|
||||
"name": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "42425"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "in-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "5678"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "out-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "5125"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
|
@ -0,0 +1,23 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
name_override = "gnmi"
|
||||
redial = "10s"
|
||||
canonical_field_names = true
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interfaces"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/descr"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interfaces"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/config/id"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interfaces"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/state"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
|
@ -0,0 +1 @@
|
|||
interfaces,name=eth42,path=openconfig-interfaces:/interfaces/interface,source=127.0.0.1 interfaces/interface/descr="eth42",interfaces/interface/config/id=42425u,interfaces/interface/state/in_pkts=5678u,interfaces/interface/state/out_pkts=5125u 1673608605875353770
|
|
@ -0,0 +1,79 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1673608605875353770",
|
||||
"prefix": {
|
||||
"origin": "openconfig-interfaces",
|
||||
"elem": [
|
||||
{
|
||||
"name": "interfaces"
|
||||
},
|
||||
{
|
||||
"name": "interface",
|
||||
"key":{"name":"eth42"}
|
||||
}
|
||||
],
|
||||
"target": "subscription"
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "descr"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"stringVal": "eth42"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "config"
|
||||
},
|
||||
{
|
||||
"name": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "42425"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "in-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "5678"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "out-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "5125"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
|
@ -0,0 +1,24 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
name_override = "gnmi"
|
||||
redial = "10s"
|
||||
canonical_field_names = true
|
||||
trim_field_names = true
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interfaces"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/descr"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interfaces"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/config/id"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interfaces"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/state"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
1
plugins/inputs/gnmi/testcases/issue_11011/expected.out
Normal file
1
plugins/inputs/gnmi/testcases/issue_11011/expected.out
Normal file
|
@ -0,0 +1 @@
|
|||
oc-neigh-state,source=127.0.0.1,neighbor_address=192.0.2.1,name=default,oc-neigh-desc/description=EXAMPLE-PEER,/network-instances/network-instance/protocols/protocol/name=BGP,identifier=BGP session_state="ESTABLISHED" 1543236572000000000
|
116
plugins/inputs/gnmi/testcases/issue_11011/responses.json
Normal file
116
plugins/inputs/gnmi/testcases/issue_11011/responses.json
Normal file
|
@ -0,0 +1,116 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1543236571000000000",
|
||||
"prefix": {},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "network-instances"
|
||||
},
|
||||
{
|
||||
"name": "network-instance",
|
||||
"key": {
|
||||
"name": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "protocols"
|
||||
},
|
||||
{
|
||||
"name": "protocol",
|
||||
"key": {
|
||||
"identifier": "BGP",
|
||||
"name": "BGP"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bgp"
|
||||
},
|
||||
{
|
||||
"name": "neighbors"
|
||||
},
|
||||
{
|
||||
"name": "neighbor",
|
||||
"key": {
|
||||
"neighbor_address": "192.0.2.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "description"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"stringVal": "EXAMPLE-PEER"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"syncResponse": true
|
||||
},
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1543236572000000000",
|
||||
"prefix": {},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "network-instances"
|
||||
},
|
||||
{
|
||||
"name": "network-instance",
|
||||
"key": {
|
||||
"name": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "protocols"
|
||||
},
|
||||
{
|
||||
"name": "protocol",
|
||||
"key": {
|
||||
"identifier": "BGP",
|
||||
"name": "BGP"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bgp"
|
||||
},
|
||||
{
|
||||
"name": "neighbors"
|
||||
},
|
||||
{
|
||||
"name": "neighbor",
|
||||
"key": {
|
||||
"neighbor_address": "192.0.2.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "session-state"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"stringVal": "ESTABLISHED"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"syncResponse": true
|
||||
}
|
||||
]
|
14
plugins/inputs/gnmi/testcases/issue_11011/telegraf.conf
Normal file
14
plugins/inputs/gnmi/testcases/issue_11011/telegraf.conf
Normal file
|
@ -0,0 +1,14 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["127.0.0.1"]
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "oc-neigh-state"
|
||||
origin = "openconfig"
|
||||
path = "/network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/state/session-state"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
[[inputs.gnmi.tag_subscription]]
|
||||
name = "oc-neigh-desc"
|
||||
origin = "openconfig"
|
||||
path = "/network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/state/description"
|
||||
subscription_mode = "on_change"
|
||||
elements = ["network-instance", "protocol", "neighbor"]
|
1
plugins/inputs/gnmi/testcases/issue_11778/expected.out
Normal file
1
plugins/inputs/gnmi/testcases/issue_11778/expected.out
Normal file
|
@ -0,0 +1 @@
|
|||
ifcounters,path=openconfig-interfaces:/interfaces/interface/state,name=eth42,descr/description=eth42,source=127.0.0.1 counters=5678i 1673608605875353770
|
49
plugins/inputs/gnmi/testcases/issue_11778/responses.json
Normal file
49
plugins/inputs/gnmi/testcases/issue_11778/responses.json
Normal file
|
@ -0,0 +1,49 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1673608605875353770",
|
||||
"prefix": {
|
||||
"origin": "openconfig-interfaces",
|
||||
"elem": [
|
||||
{
|
||||
"name": "interfaces"
|
||||
},
|
||||
{
|
||||
"name": "interface",
|
||||
"key":{"name":"eth42"}
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
}
|
||||
],
|
||||
"target": "subscription"
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "counters"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"intVal": "5678"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "description"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"stringVal": "eth42"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
15
plugins/inputs/gnmi/testcases/issue_11778/telegraf.conf
Normal file
15
plugins/inputs/gnmi/testcases/issue_11778/telegraf.conf
Normal file
|
@ -0,0 +1,15 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
name_override = "gnmi"
|
||||
redial = "10s"
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "ifcounters"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/state/counters"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
[[inputs.gnmi.tag_subscription]]
|
||||
name = "descr"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/state/description"
|
||||
subscription_mode = "on_change"
|
|
@ -0,0 +1 @@
|
|||
ifcounters,path=openconfig-interfaces:/interfaces/interface/state,name=eth42,descr/description=eth42,source=127.0.0.1 counters=5678i 1673608605875353770
|
|
@ -0,0 +1,49 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1673608605875353770",
|
||||
"prefix": {
|
||||
"origin": "openconfig-interfaces",
|
||||
"elem": [
|
||||
{
|
||||
"name": "interfaces"
|
||||
},
|
||||
{
|
||||
"name": "interface",
|
||||
"key":{"name":"eth42"}
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
}
|
||||
],
|
||||
"target": "subscription"
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "counters"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"intVal": "5678"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "description"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"stringVal": "eth42"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
|
@ -0,0 +1,16 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["rt-01:nnnnn", "rt-02:nnnnn"]
|
||||
name_override = "gnmi"
|
||||
redial = "10s"
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "ifcounters"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/state/counters"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "descr"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/state/description"
|
||||
subscription_mode = "on_change"
|
||||
tag_only = true
|
7
plugins/inputs/gnmi/testcases/issue_12831/expected.out
Normal file
7
plugins/inputs/gnmi/testcases/issue_12831/expected.out
Normal file
|
@ -0,0 +1,7 @@
|
|||
optical_channel,component_name=CM-1,source=127.0.0.1 cpu/openconfig_platform_cpu:utilization/state/avg=13u,cpu/openconfig_platform_cpu:utilization/state/instant=16u,cpu/openconfig_platform_cpu:utilization/state/interval=3600000000000u,cpu/openconfig_platform_cpu:utilization/state/max=17u,cpu/openconfig_platform_cpu:utilization/state/min=8u,name="CM-1",state/memory/available=5041100000u,state/memory/utilized=3099808000u,state/temperature/avg=20.5,state/temperature/instant=20.399999618530273,state/temperature/interval=3600000000000u,state/temperature/max=20.700000762939453,state/temperature/min=20.399999618530273 1714598222912553000
|
||||
optical_channel,component_name=AP-1,source=127.0.0.1 name="AP-1",state/temperature/avg=34.099998474121094,state/temperature/instant=34.29999923706055,state/temperature/interval=3600000000000u,state/temperature/max=34.599998474121094,state/temperature/min=33.599998474121094 1714598222912553000
|
||||
optical_channel,component_name=LM-1,source=127.0.0.1 name="LM-1",state/temperature/avg=40.79999923706055,state/temperature/instant=40.599998474121094,state/temperature/interval=3600000000000u,state/temperature/max=41.400001525878906,state/temperature/min=40.400001525878906 1714598222912553000
|
||||
optical_channel,component_name=LM-1,source=127.0.0.1,subcomponent_name=PORT-1-1 subcomponents/subcomponent/config/name="PORT-1-1",subcomponents/subcomponent/name="PORT-1-1",subcomponents/subcomponent/state/name="PORT-1-1" 1714598222912553000
|
||||
optical_channel,component_name=LM-1,source=127.0.0.1,subcomponent_name=PORT-1-2 subcomponents/subcomponent/config/name="PORT-1-2",subcomponents/subcomponent/name="PORT-1-2",subcomponents/subcomponent/state/name="PORT-1-2" 1714598222912553000
|
||||
optical_channel,component_name=LM-1,source=127.0.0.1,subcomponent_name=PORT-1-4 subcomponents/subcomponent/config/name="PORT-1-4",subcomponents/subcomponent/name="PORT-1-4",subcomponents/subcomponent/state/name="PORT-1-4" 1714598222912553000
|
||||
optical_channel,component_name=LM-1,source=127.0.0.1,subcomponent_name=PORT-1-12 subcomponents/subcomponent/config/name="PORT-1-12",subcomponents/subcomponent/name="PORT-1-12",subcomponents/subcomponent/state/name="PORT-1-12" 1714598222912553000
|
1101
plugins/inputs/gnmi/testcases/issue_12831/responses.json
Normal file
1101
plugins/inputs/gnmi/testcases/issue_12831/responses.json
Normal file
File diff suppressed because it is too large
Load diff
10
plugins/inputs/gnmi/testcases/issue_12831/telegraf.conf
Normal file
10
plugins/inputs/gnmi/testcases/issue_12831/telegraf.conf
Normal file
|
@ -0,0 +1,10 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
prefix_tag_key_with_path = true
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "optical_channel"
|
||||
path = "/components/component[name=*]"
|
||||
subscription_mode = "sample"
|
||||
origin = "openconfig-platform"
|
||||
sample_interval = "10s"
|
1
plugins/inputs/gnmi/testcases/issue_12931/expected.out
Normal file
1
plugins/inputs/gnmi/testcases/issue_12931/expected.out
Normal file
|
@ -0,0 +1 @@
|
|||
interface,name=ethernet-1/1,source=127.0.0.1 interface=1i,transceiver=2i,ethernet=3i,ethernet/flow_control="false",sflow="disable" 1679648530391910312
|
94
plugins/inputs/gnmi/testcases/issue_12931/responses.json
Normal file
94
plugins/inputs/gnmi/testcases/issue_12931/responses.json
Normal file
|
@ -0,0 +1,94 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1679648530391910312",
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"origin": "srl_nokia-interfaces",
|
||||
"elem": [
|
||||
{
|
||||
"name": "interface",
|
||||
"key":{"name":"ethernet-1/1"}
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"intVal": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "srl_nokia-interfaces",
|
||||
"elem": [
|
||||
{
|
||||
"name": "interface",
|
||||
"key":{"name":"ethernet-1/1"}
|
||||
},
|
||||
{
|
||||
"name": "transceiver"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"intVal": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "srl_nokia-interfaces",
|
||||
"elem": [
|
||||
{
|
||||
"name": "interface",
|
||||
"key":{"name":"ethernet-1/1"}
|
||||
},
|
||||
{
|
||||
"name": "ethernet"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"intVal": "3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "srl_nokia-interfaces",
|
||||
"elem": [
|
||||
{
|
||||
"name": "interface",
|
||||
"key":{"name":"ethernet-1/1"}
|
||||
},
|
||||
{
|
||||
"name": "ethernet"
|
||||
},
|
||||
{
|
||||
"name": "flow-control"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"stringVal": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "srl_nokia-interfaces",
|
||||
"elem": [
|
||||
{
|
||||
"name": "interface",
|
||||
"key":{"name":"ethernet-1/1"}
|
||||
},
|
||||
{
|
||||
"name": "sflow"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"stringVal": "disable"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
10
plugins/inputs/gnmi/testcases/issue_12931/telegraf.conf
Normal file
10
plugins/inputs/gnmi/testcases/issue_12931/telegraf.conf
Normal file
|
@ -0,0 +1,10 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
name_override = "gnmi"
|
||||
redial = "10s"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interface"
|
||||
path = "/interface"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
119
plugins/inputs/gnmi/testcases/issue_13052/expected.out
Normal file
119
plugins/inputs/gnmi/testcases/issue_13052/expected.out
Normal file
|
@ -0,0 +1,119 @@
|
|||
components,/components/component/properties/property/name=mem-util-kernel-size,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=671088216u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=187929304u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-utilization,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=28i 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-ifd-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=97240u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-ifd-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=442u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-ifd-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-ifd-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-ifl-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=95272u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-ifl-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=132u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-ifl-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-ifl-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-iff-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=38640u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-iff-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=178u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-iff-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-iff-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-rt-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=82592u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-rt-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=914609u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-rt-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=914255u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-rt-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-rtt-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=7212u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-rtt-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=25u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-rtt-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-rtt-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-nh-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=180544u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-nh-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=519u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-nh-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=21u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-nh-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-filter-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=144380u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-filter-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=2674u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-filter-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=739u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-filter-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-cos-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=41488u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-cos-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=383u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-cos-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=53u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-cos-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-cos-halp-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=482008u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-cos-halp-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=102u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-cos-halp-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-cos-halp-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-rt-mac-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=20u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-rt-mac-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=1u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-rt-mac-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-rt-mac-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-fpdl-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=364u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-fpdl-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=16u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-fpdl-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=9u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-fpdl-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-fpb-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=56u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-fpb-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=4u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-fpb-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=2u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-fpb-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-fpb-syms-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=104u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-fpb-syms-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=4u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-fpb-syms-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=2u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-fpb-syms-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-agent-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=19548u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-agent-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=1679742u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-agent-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=1679691u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-agent-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-an-stats-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=5503680u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-an-stats-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=10192u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-an-stats-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-an-stats-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-resolve-bind-point-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=72u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-resolve-bind-point-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=12u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-resolve-bind-point-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=6u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-resolve-bind-point-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-entry-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=95436u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-entry-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=2812u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-entry-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=1435u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-entry-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-tcam-entry-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=33920u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-tcam-entry-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=546u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-tcam-entry-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-tcam-entry-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-id-entry-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=448u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-id-entry-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=40u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-id-entry-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=24u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-id-entry-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-bmap-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=9216u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-bmap-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=62u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-bmap-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=24u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-bmap-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-hw-hdl-rule-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=49172u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-hw-hdl-rule-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=2257u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-hw-hdl-rule-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=1140u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-hw-hdl-rule-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-lagmatch-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=72u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-lagmatch-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=2u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-lagmatch-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-lagmatch-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-cntr-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=372008u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-cntr-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=4444u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-cntr-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=1628u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-cntr-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-policer-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=192544u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-policer-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=600u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-policer-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=240u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-policer-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-misc-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=984u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-misc-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=83u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-misc-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=1u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-dfw-misc-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-halp-unknown-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=92u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-halp-unknown-allocations,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=3u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-halp-unknown-frees,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=2u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-kernel-halp-unknown-allocations-failed,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=0u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-dma-size,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=67108864u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-dma-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=4655312u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-dma-utilization,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=6i 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-pkt-dma-desc-size,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=12582912u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-pkt-dma-desc-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=10257712u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-pkt-dma-desc-utilization,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=81i 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-bcm-sdk-size,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=268435456u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-bcm-sdk-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=157163104u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-bcm-sdk-utilization,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=58i 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-packet-dma-size,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=113246208u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-packet-dma-bytes-allocated,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=56030424u 1688641479762000000
|
||||
components,/components/component/properties/property/name=mem-util-packet-dma-utilization,name=FPC0:CPU0,path=/components/component/properties,source=127.0.0.1 component/properties/property/state/value=49i 1688641479762000000
|
2524
plugins/inputs/gnmi/testcases/issue_13052/responses.json
Normal file
2524
plugins/inputs/gnmi/testcases/issue_13052/responses.json
Normal file
File diff suppressed because it is too large
Load diff
13
plugins/inputs/gnmi/testcases/issue_13052/telegraf.conf
Normal file
13
plugins/inputs/gnmi/testcases/issue_13052/telegraf.conf
Normal file
|
@ -0,0 +1,13 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
redial = "10s"
|
||||
|
||||
[inputs.gnmi.aliases]
|
||||
components = "/components"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "memory"
|
||||
origin = "openconfig"
|
||||
path = "/junos/system/linecard/cpu/memory"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "60s"
|
1
plugins/inputs/gnmi/testcases/issue_13512/expected.out
Normal file
1
plugins/inputs/gnmi/testcases/issue_13512/expected.out
Normal file
|
@ -0,0 +1 @@
|
|||
interfaces,name=eth42,path=openconfig-interfaces:/interfaces/interface,source=127.0.0.1 descr="eth42",id=42425u,in_pkts=5678u,out_pkts=5125u 1673608605875353770
|
79
plugins/inputs/gnmi/testcases/issue_13512/responses.json
Normal file
79
plugins/inputs/gnmi/testcases/issue_13512/responses.json
Normal file
|
@ -0,0 +1,79 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1673608605875353770",
|
||||
"prefix": {
|
||||
"origin": "openconfig-interfaces",
|
||||
"elem": [
|
||||
{
|
||||
"name": "interfaces"
|
||||
},
|
||||
{
|
||||
"name": "interface",
|
||||
"key":{"name":"eth42"}
|
||||
}
|
||||
],
|
||||
"target": "subscription"
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "descr"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"stringVal": "eth42"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "config"
|
||||
},
|
||||
{
|
||||
"name": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "42425"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "in-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "5678"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "out-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "5125"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
22
plugins/inputs/gnmi/testcases/issue_13512/telegraf.conf
Normal file
22
plugins/inputs/gnmi/testcases/issue_13512/telegraf.conf
Normal file
|
@ -0,0 +1,22 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
name_override = "gnmi"
|
||||
redial = "10s"
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interfaces"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/descr"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interfaces"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/config/id"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interfaces"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/interfaces/interface/state"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
1
plugins/inputs/gnmi/testcases/issue_14044/expected.out
Normal file
1
plugins/inputs/gnmi/testcases/issue_14044/expected.out
Normal file
|
@ -0,0 +1 @@
|
|||
ifdesc,name=FourHundredGigE0/2/0/3,path=openconfig-interfaces:/interfaces/interface/state,source=127.0.0.1 description="REDACTED" 1696324083211000000
|
33
plugins/inputs/gnmi/testcases/issue_14044/responses.json
Normal file
33
plugins/inputs/gnmi/testcases/issue_14044/responses.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1696324083211000000",
|
||||
"prefix": {
|
||||
"origin": "openconfig-interfaces"
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "interfaces"
|
||||
},
|
||||
{
|
||||
"name": "interface",
|
||||
"key": {
|
||||
"name": "FourHundredGigE0/2/0/3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"json_ietf_val": "eyJkZXNjcmlwdGlvbiI6IlJFREFDVEVEIn0="
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
13
plugins/inputs/gnmi/testcases/issue_14044/telegraf.conf
Normal file
13
plugins/inputs/gnmi/testcases/issue_14044/telegraf.conf
Normal file
|
@ -0,0 +1,13 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
name_override = "gnmi"
|
||||
redial = "10s"
|
||||
encoding = "json_ietf"
|
||||
path_guessing_strategy = "common path"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "ifdesc"
|
||||
origin = "openconfig-interfaces"
|
||||
path = '/interfaces/interface[name=FourHundredGigE*]/state/description'
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "60s"
|
1
plugins/inputs/gnmi/testcases/issue_14063/expected.out
Normal file
1
plugins/inputs/gnmi/testcases/issue_14063/expected.out
Normal file
|
@ -0,0 +1 @@
|
|||
ifcounters,path=oc-if:/interfaces/oc-if:interface/oc-if:state/oc-if:counters,source=127.0.0.1 in_1024_to_1518_octet_pkts=0u,in_128_to_255_octet_pkts=0u,in_1519_to_2047_octet_pkts=0u,in_2048_to_4095_octet_pkts=0u,in_256_to_511_octet_pkts=0u,in_4096_to_9216_octet_pkts=0u,in_512_to_1023_octet_pkts=0u,in_64_octet_pkts=0u,in_65_to_127_octet_pkts=0u,in_broadcast_pkts=0u,in_crc_error_pkts=0u,in_discards=0u,in_discards_octets=0u,in_dropped_octets=0u,in_dropped_pkts=0u,in_errors=0u,in_jabber_pkts=0u,in_multicast_pkts=0u,in_octets=0u,in_oversize_pkts=0u,in_pkts=0u,in_undersize_pkts=0u,in_unicast_pkts=0u,last_clear=1691859140059797458u,link_flap_events=0u,name="\\\"1\\",out_1519_to_2047_octet_pkts=0u,out_2048_to_4095_octet_pkts=0u,out_4096_to_9216_octet_pkts=0u,out_broadcast_pkts=0u,out_errors=0u,out_multicast_pkts=0u,out_octets=0u,out_pkts=0u,out_unicast_pkts=0u 1696617695101000000
|
445
plugins/inputs/gnmi/testcases/issue_14063/responses.json
Normal file
445
plugins/inputs/gnmi/testcases/issue_14063/responses.json
Normal file
|
@ -0,0 +1,445 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1696617695101000000",
|
||||
"prefix": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "oc-if:interfaces"
|
||||
},
|
||||
{
|
||||
"name": "oc-if:interface"
|
||||
},
|
||||
{
|
||||
"name": "oc-if:state"
|
||||
},
|
||||
{
|
||||
"name": "oc-if:counters"
|
||||
}
|
||||
]
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-1024-to-1518-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-128-to-255-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-1519-to-2047-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-2048-to-4095-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-256-to-511-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-4096-to-9216-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-512-to-1023-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-64-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-65-to-127-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-broadcast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-crc-error-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-discards"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-discards-octets"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-dropped-octets"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-dropped-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-errors"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-jabber-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-multicast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-octets"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-oversize-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-undersize-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-unicast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "last-clear"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "1691859140059797458"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "link-flap-events"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "name"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"stringVal": "\\\"1\\"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-1519-to-2047-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-2048-to-4095-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-4096-to-9216-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-broadcast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-errors"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-multicast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-octets"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-unicast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
11
plugins/inputs/gnmi/testcases/issue_14063/telegraf.conf
Normal file
11
plugins/inputs/gnmi/testcases/issue_14063/telegraf.conf
Normal file
|
@ -0,0 +1,11 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
name_override = "gnmi"
|
||||
redial = "10s"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "ifcounters"
|
||||
origin = "openconfig-interfaces"
|
||||
path = "/oc-if:interfaces/oc-if:interface/oc-if:state/oc-if:counters"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "30s"
|
1
plugins/inputs/gnmi/testcases/issue_14530/expected.out
Normal file
1
plugins/inputs/gnmi/testcases/issue_14530/expected.out
Normal file
|
@ -0,0 +1 @@
|
|||
ifcounters,name=Ethernet35,path=/interfaces/interface/state/counters,source=127.0.0.1 in_broadcast_pkts=0u,in_discards=0u,in_errors=0u,in_fcs_errors=0u,in_multicast_pkts=0u,in_octets=0u,in_pkts=0u,in_unicast_pkts=0u,out_broadcast_pkts=0u,out_discards=0u,out_errors=0u,out_multicast_pkts=0u,out_octets=0u,out_pkts=0u,out_unicast_pkts=0u 1704442117721474264
|
208
plugins/inputs/gnmi/testcases/issue_14530/responses.json
Normal file
208
plugins/inputs/gnmi/testcases/issue_14530/responses.json
Normal file
|
@ -0,0 +1,208 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1704442117721474264",
|
||||
"prefix": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "interfaces"
|
||||
},
|
||||
{
|
||||
"name": "interface",
|
||||
"key": {
|
||||
"name": "Ethernet35"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "counters"
|
||||
}
|
||||
]
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-broadcast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-discards"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-errors"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-fcs-errors"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-multicast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-octets"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-unicast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-broadcast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-discards"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-errors"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-multicast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-octets"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-unicast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
14
plugins/inputs/gnmi/testcases/issue_14530/telegraf.conf
Normal file
14
plugins/inputs/gnmi/testcases/issue_14530/telegraf.conf
Normal file
|
@ -0,0 +1,14 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
encoding = "json_ietf"
|
||||
tagexclude = ["path"]
|
||||
|
||||
[inputs.gnmi.tags]
|
||||
test_tag = "test"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "ifcounters"
|
||||
origin = "openconfig"
|
||||
path = "/interfaces/interface/state/counters"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
2
plugins/inputs/gnmi/testcases/issue_14833/expected.out
Normal file
2
plugins/inputs/gnmi/testcases/issue_14833/expected.out
Normal file
|
@ -0,0 +1,2 @@
|
|||
interfaces-counter,name=AppGigabitEthernet1/0/1,source=127.0.0.1 discontinuity_time="2023-11-15T05:50:40+00:00" 1708069483623763000
|
||||
interfaces-counter,name=FortyGigabitEthernet1/1/1,source=127.0.0.1 discontinuity_time="2023-11-15T05:50:40+00:00" 1708069483623763000
|
51
plugins/inputs/gnmi/testcases/issue_14833/responses.json
Normal file
51
plugins/inputs/gnmi/testcases/issue_14833/responses.json
Normal file
|
@ -0,0 +1,51 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1708069483623763000",
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "Cisco-IOS-XE-interfaces-oper:interfaces"
|
||||
},
|
||||
{
|
||||
"name": "interface",
|
||||
"key": {
|
||||
"name": "AppGigabitEthernet1/0/1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "statistics"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"jsonIetfVal": "eyJkaXNjb250aW51aXR5LXRpbWUiOiIyMDIzLTExLTE1VDA1OjUwOjQwKzAwOjAwIn0K"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "Cisco-IOS-XE-interfaces-oper:interfaces"
|
||||
},
|
||||
{
|
||||
"name": "interface",
|
||||
"key": {
|
||||
"name": "FortyGigabitEthernet1/1/1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "statistics"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"jsonIetfVal": "eyJkaXNjb250aW51aXR5LXRpbWUiOiIyMDIzLTExLTE1VDA1OjUwOjQwKzAwOjAwIn0K"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
9
plugins/inputs/gnmi/testcases/issue_14833/telegraf.conf
Normal file
9
plugins/inputs/gnmi/testcases/issue_14833/telegraf.conf
Normal file
|
@ -0,0 +1,9 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interfaces-counter"
|
||||
origin = "rfc7951"
|
||||
path = "/Cisco-IOS-XE-interfaces-oper:interfaces/interface/statistics"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "5s"
|
4
plugins/inputs/gnmi/testcases/issue_14946/expected.out
Normal file
4
plugins/inputs/gnmi/testcases/issue_14946/expected.out
Normal file
|
@ -0,0 +1,4 @@
|
|||
gnmi_sys_memory,path=/system/memory/state,source=127.0.0.1 reserved=6359478272u,used=3479629824u 1709737743568119333
|
||||
gnmi_sys_memory,path=/system/memory/state,source=127.0.0.1 used=3479527424u 1709737753565697718
|
||||
gnmi_sys_cpu,index=ALL,path=/system/cpus/cpu/state,source=127.0.0.1 hardware_interrupt/min_time=1709805333568034887u 1709805333566280930
|
||||
gnmi_sys_cpu,index=ALL,path=/system/cpus/cpu/state,source=127.0.0.1 hardware_interrupt/min_time=1709805343567684412u,idle/avg=89u,idle/instant=90u 1709805343565718902
|
182
plugins/inputs/gnmi/testcases/issue_14946/responses.json
Normal file
182
plugins/inputs/gnmi/testcases/issue_14946/responses.json
Normal file
|
@ -0,0 +1,182 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1709737743568119333",
|
||||
"prefix": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "system"
|
||||
},
|
||||
{
|
||||
"name": "memory"
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
}
|
||||
]
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "reserved"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "6359478272"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "used"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "3479629824"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1709737753565697718",
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "system"
|
||||
},
|
||||
{
|
||||
"name": "memory"
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "used"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "3479527424"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1709805333566280930",
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "system"
|
||||
},
|
||||
{
|
||||
"name": "cpus"
|
||||
},
|
||||
{
|
||||
"name": "cpu",
|
||||
"key": {
|
||||
"index": "ALL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "hardware-interrupt"
|
||||
},
|
||||
{
|
||||
"name": "min-time"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "1709805333568034887"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1709805343565718902",
|
||||
"prefix": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "system"
|
||||
},
|
||||
{
|
||||
"name": "cpus"
|
||||
},
|
||||
{
|
||||
"name": "cpu",
|
||||
"key": {
|
||||
"index": "ALL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
}
|
||||
]
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "hardware-interrupt"
|
||||
},
|
||||
{
|
||||
"name": "min-time"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "1709805343567684412"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "idle"
|
||||
},
|
||||
{
|
||||
"name": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "89"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "idle"
|
||||
},
|
||||
{
|
||||
"name": "instant"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "90"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
15
plugins/inputs/gnmi/testcases/issue_14946/telegraf.conf
Normal file
15
plugins/inputs/gnmi/testcases/issue_14946/telegraf.conf
Normal file
|
@ -0,0 +1,15 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
path_guessing_strategy = "subscription"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "gnmi_sys_cpu"
|
||||
path = "/system/cpus/cpu/state"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "gnmi_sys_memory"
|
||||
path = "/system/memory/state"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
|
@ -0,0 +1,4 @@
|
|||
gnmi_sys_memory,path=/system/memory/state,source=127.0.0.1 /system/memory/state/reserved=6359478272u,/system/memory/state/used=3479629824u 1709737743568119333
|
||||
gnmi_sys_memory,source=127.0.0.1 /system/memory/state/used=3479527424u 1709737753565697718
|
||||
gnmi_sys_cpu,index=ALL,source=127.0.0.1 /system/cpus/cpu/state/hardware_interrupt/min_time=1709805333568034887u 1709805333566280930
|
||||
gnmi_sys_cpu,index=ALL,path=/system/cpus/cpu/state,source=127.0.0.1 /system/cpus/cpu/state/hardware_interrupt/min_time=1709805343567684412u,/system/cpus/cpu/state/idle/avg=89u,/system/cpus/cpu/state/idle/instant=90u 1709805343565718902
|
|
@ -0,0 +1,182 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1709737743568119333",
|
||||
"prefix": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "system"
|
||||
},
|
||||
{
|
||||
"name": "memory"
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
}
|
||||
]
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "reserved"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "6359478272"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "used"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "3479629824"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1709737753565697718",
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "system"
|
||||
},
|
||||
{
|
||||
"name": "memory"
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "used"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "3479527424"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1709805333566280930",
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "system"
|
||||
},
|
||||
{
|
||||
"name": "cpus"
|
||||
},
|
||||
{
|
||||
"name": "cpu",
|
||||
"key": {
|
||||
"index": "ALL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "hardware-interrupt"
|
||||
},
|
||||
{
|
||||
"name": "min-time"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "1709805333568034887"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1709805343565718902",
|
||||
"prefix": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "system"
|
||||
},
|
||||
{
|
||||
"name": "cpus"
|
||||
},
|
||||
{
|
||||
"name": "cpu",
|
||||
"key": {
|
||||
"index": "ALL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
}
|
||||
]
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "hardware-interrupt"
|
||||
},
|
||||
{
|
||||
"name": "min-time"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "1709805343567684412"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "idle"
|
||||
},
|
||||
{
|
||||
"name": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "89"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "idle"
|
||||
},
|
||||
{
|
||||
"name": "instant"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "90"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
|
@ -0,0 +1,15 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
canonical_field_names = true
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "gnmi_sys_cpu"
|
||||
path = "/system/cpus/cpu/state"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "gnmi_sys_memory"
|
||||
path = "/system/memory/state"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
5
plugins/inputs/gnmi/testcases/issue_15046/expected.out
Normal file
5
plugins/inputs/gnmi/testcases/issue_15046/expected.out
Normal file
|
@ -0,0 +1,5 @@
|
|||
psu,name=PowerSupply1/A,path=openconfig:/components/component/power-supply/state,source=127.0.0.1 openconfig_platform_psu:capacity=715,openconfig_platform_psu:enabled=true,openconfig_platform_psu:input_current=0.47099998593330383,openconfig_platform_psu:input_voltage=208.5,openconfig_platform_psu:output_current=1.2029999494552612,openconfig_platform_psu:output_power=68.625,openconfig_platform_psu:output_voltage=56.367000579833984 1711178737105194000
|
||||
psu,name=PowerSupply1/B,path=openconfig:/components/component/power-supply/state,source=127.0.0.1 openconfig_platform_psu:capacity=715,openconfig_platform_psu:enabled=true,openconfig_platform_psu:input_current=0.3930000066757202,openconfig_platform_psu:input_voltage=209.75,openconfig_platform_psu:output_current=0.9380000233650208,openconfig_platform_psu:output_power=51.875,openconfig_platform_psu:output_voltage=56.367000579833984 1711178737105194000
|
||||
temp,name=InletTempSensor1,path=openconfig:/components/component/state/temperature,source=127.0.0.1 alarm_severity="openconfig-alarm-types:MINOR",alarm_status=false,alarm_threshold=0,avg=24.000000,instant=35.000000,interval=180000000000u,max=36.000000,min=0.000000 1715838159171548000
|
||||
temp,name=OutletTempSensor1,path=openconfig:/components/component/state/temperature,source=127.0.0.1 alarm_severity="openconfig-alarm-types:MINOR",alarm_status=false,alarm_threshold=0,avg=29.000000,instant=44.000000,interval=180000000000u,max=44.000000,min=0.000000 1715838159171548000
|
||||
temp,name=HotSpotTempSensor1,path=openconfig:/components/component/state/temperature,source=127.0.0.1 alarm_severity="openconfig-alarm-types:MINOR",alarm_status=false,alarm_threshold=0,avg=39.000000,instant=58.000000,interval=180000000000u,max=59.000000,min=0.000000 1715838159171548000
|
202
plugins/inputs/gnmi/testcases/issue_15046/models/LICENSE
Normal file
202
plugins/inputs/gnmi/testcases/issue_15046/models/LICENSE
Normal file
|
@ -0,0 +1,202 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<!-- markdownlint-disable -->
|
||||
Files extracted from https://github.com/openconfig/public
|
|
@ -0,0 +1,109 @@
|
|||
module openconfig-alarm-types {
|
||||
|
||||
yang-version "1";
|
||||
|
||||
// namespace
|
||||
namespace "http://openconfig.net/yang/alarms/types";
|
||||
|
||||
prefix "oc-alarm-types";
|
||||
|
||||
// import some basic types
|
||||
import openconfig-extensions { prefix oc-ext; }
|
||||
|
||||
// meta
|
||||
organization "OpenConfig working group";
|
||||
|
||||
contact
|
||||
"OpenConfig working group
|
||||
www.openconfig.net";
|
||||
|
||||
description
|
||||
"This module defines operational state data related to alarms
|
||||
that the device is reporting.
|
||||
|
||||
This model reuses some data items defined in the draft IETF
|
||||
YANG Alarm Module:
|
||||
https://tools.ietf.org/html/draft-vallin-netmod-alarm-module-02
|
||||
|
||||
Portions of this code were derived from the draft IETF YANG Alarm
|
||||
Module. Please reproduce this note if possible.
|
||||
|
||||
IETF code is subject to the following copyright and license:
|
||||
Copyright (c) IETF Trust and the persons identified as authors of
|
||||
the code.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, is permitted pursuant to, and subject to the license
|
||||
terms contained in, the Simplified BSD License set forth in
|
||||
Section 4.c of the IETF Trust's Legal Provisions Relating
|
||||
to IETF Documents (http://trustee.ietf.org/license-info).";
|
||||
|
||||
oc-ext:openconfig-version "0.2.1";
|
||||
|
||||
// OpenConfig specific extensions for module metadata.
|
||||
oc-ext:regexp-posix;
|
||||
oc-ext:catalog-organization "openconfig";
|
||||
oc-ext:origin "openconfig";
|
||||
|
||||
// identity statements
|
||||
|
||||
identity OPENCONFIG_ALARM_SEVERITY {
|
||||
description
|
||||
"Base identity for alarm severity profiles. Derived
|
||||
identities are based on contents of the draft
|
||||
IETF YANG Alarm Module";
|
||||
reference
|
||||
"IETF YANG Alarm Module: Draft - typedef severity
|
||||
https://tools.ietf.org/html/draft-vallin-netmod-alarm-module-02";
|
||||
|
||||
}
|
||||
|
||||
identity UNKNOWN {
|
||||
base OPENCONFIG_ALARM_SEVERITY;
|
||||
description
|
||||
"Indicates that the severity level could not be determined.
|
||||
This level SHOULD be avoided.";
|
||||
}
|
||||
|
||||
identity MINOR {
|
||||
base OPENCONFIG_ALARM_SEVERITY;
|
||||
description
|
||||
"Indicates the existence of a non-service affecting fault
|
||||
condition and that corrective action should be taken in
|
||||
order to prevent a more serious (for example, service
|
||||
affecting) fault. Such a severity can be reported, for
|
||||
example, when the detected alarm condition is not currently
|
||||
degrading the capacity of the resource";
|
||||
}
|
||||
|
||||
identity WARNING {
|
||||
base OPENCONFIG_ALARM_SEVERITY;
|
||||
description
|
||||
"Indicates the detection of a potential or impending service
|
||||
affecting fault, before any significant effects have been felt.
|
||||
Action should be taken to further diagnose (if necessary) and
|
||||
correct the problem in order to prevent it from becoming a more
|
||||
serious service affecting fault.";
|
||||
}
|
||||
|
||||
identity MAJOR {
|
||||
base OPENCONFIG_ALARM_SEVERITY;
|
||||
description
|
||||
"Indicates that a service affecting condition has developed
|
||||
and an urgent corrective action is required. Such a severity
|
||||
can be reported, for example, when there is a severe
|
||||
degradation in the capability of the resource and its full
|
||||
capability must be restored.";
|
||||
}
|
||||
|
||||
identity CRITICAL {
|
||||
base OPENCONFIG_ALARM_SEVERITY;
|
||||
description
|
||||
"Indicates that a service affecting condition has occurred
|
||||
and an immediate corrective action is required. Such a
|
||||
severity can be reported, for example, when a resource becomes
|
||||
totally out of service and its capability must be restored.";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
module openconfig-extensions {
|
||||
|
||||
yang-version "1";
|
||||
|
||||
// namespace
|
||||
namespace "http://openconfig.net/yang/openconfig-ext";
|
||||
|
||||
prefix "oc-ext";
|
||||
|
||||
// meta
|
||||
organization "OpenConfig working group";
|
||||
|
||||
contact
|
||||
"OpenConfig working group
|
||||
www.openconfig.net";
|
||||
|
||||
description
|
||||
"This module provides extensions to the YANG language to allow
|
||||
OpenConfig specific functionality and meta-data to be defined.";
|
||||
|
||||
oc-ext:openconfig-version "0.5.1";
|
||||
|
||||
// extension statements
|
||||
extension openconfig-version {
|
||||
argument "semver" {
|
||||
yin-element false;
|
||||
}
|
||||
description
|
||||
"The OpenConfig version number for the module. This is
|
||||
expressed as a semantic version number of the form:
|
||||
x.y.z
|
||||
where:
|
||||
* x corresponds to the major version,
|
||||
* y corresponds to a minor version,
|
||||
* z corresponds to a patch version.
|
||||
This version corresponds to the model file within which it is
|
||||
defined, and does not cover the whole set of OpenConfig models.
|
||||
|
||||
Individual YANG modules are versioned independently -- the
|
||||
semantic version is generally incremented only when there is a
|
||||
change in the corresponding file. Submodules should always
|
||||
have the same semantic version as their parent modules.
|
||||
|
||||
A major version number of 0 indicates that this model is still
|
||||
in development (whether within OpenConfig or with industry
|
||||
partners), and is potentially subject to change.
|
||||
|
||||
Following a release of major version 1, all modules will
|
||||
increment major revision number where backwards incompatible
|
||||
changes to the model are made.
|
||||
|
||||
The minor version is changed when features are added to the
|
||||
model that do not impact current clients use of the model.
|
||||
|
||||
The patch-level version is incremented when non-feature changes
|
||||
(such as bugfixes or clarifications to human-readable
|
||||
descriptions that do not impact model functionality) are made
|
||||
that maintain backwards compatibility.
|
||||
|
||||
The version number is stored in the module meta-data.";
|
||||
}
|
||||
|
||||
extension regexp-posix {
|
||||
description
|
||||
"This extension indicates that the regular expressions included
|
||||
within the YANG module specified are conformant with the POSIX
|
||||
regular expression format rather than the W3C standard that is
|
||||
specified by RFC6020 and RFC7950.";
|
||||
}
|
||||
|
||||
extension operational {
|
||||
description
|
||||
"The operational annotation is specified in the context of a
|
||||
grouping, leaf, or leaf-list within a YANG module. It indicates
|
||||
that the nodes within the context are derived state on the device.
|
||||
|
||||
OpenConfig data models divide nodes into the following three categories:
|
||||
|
||||
- intended configuration - these are leaves within a container named
|
||||
'config', and are the writable configuration of a target.
|
||||
- applied configuration - these are leaves within a container named
|
||||
'state' and are the currently running value of the intended configuration.
|
||||
- derived state - these are the values within the 'state' container which
|
||||
are not part of the applied configuration of the device. Typically, they
|
||||
represent state values reflecting underlying operational counters, or
|
||||
protocol statuses.";
|
||||
}
|
||||
|
||||
extension catalog-organization {
|
||||
argument "org" {
|
||||
yin-element false;
|
||||
}
|
||||
description
|
||||
"This extension specifies the organization name that should be used within
|
||||
the module catalogue on the device for the specified YANG module. It stores
|
||||
a pithy string where the YANG organization statement may contain more
|
||||
details.";
|
||||
}
|
||||
|
||||
extension origin {
|
||||
argument "origin" {
|
||||
yin-element false;
|
||||
}
|
||||
description
|
||||
"This extension specifies the name of the origin that the YANG module
|
||||
falls within. This allows multiple overlapping schema trees to be used
|
||||
on a single network element without requiring module based prefixing
|
||||
of paths.";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
module openconfig-platform-psu {
|
||||
|
||||
yang-version "1";
|
||||
|
||||
// namespace
|
||||
namespace "http://openconfig.net/yang/platform/psu";
|
||||
|
||||
prefix "oc-platform-psu";
|
||||
|
||||
// import some basic types
|
||||
import openconfig-extensions { prefix oc-ext; }
|
||||
import openconfig-types { prefix oc-types; }
|
||||
|
||||
|
||||
// meta
|
||||
organization "OpenConfig working group";
|
||||
|
||||
contact
|
||||
"OpenConfig working group
|
||||
www.openconfig.net";
|
||||
|
||||
description
|
||||
"This module defines a schema for power supply components in
|
||||
the OpenConfig platform model.";
|
||||
|
||||
oc-ext:openconfig-version "0.2.1";
|
||||
|
||||
// OpenConfig specific extensions for module metadata.
|
||||
oc-ext:regexp-posix;
|
||||
oc-ext:catalog-organization "openconfig";
|
||||
oc-ext:origin "openconfig";
|
||||
|
||||
grouping psu-config {
|
||||
description
|
||||
"Configuration data for power supply components";
|
||||
|
||||
leaf enabled {
|
||||
type boolean;
|
||||
default true;
|
||||
description
|
||||
"Adminsitrative control on the on/off state of the power
|
||||
supply unit.";
|
||||
}
|
||||
}
|
||||
|
||||
grouping psu-state {
|
||||
description
|
||||
"Operational state data for power supply components";
|
||||
|
||||
leaf capacity {
|
||||
type oc-types:ieeefloat32;
|
||||
units watts;
|
||||
description
|
||||
"Maximum power capacity of the power supply.";
|
||||
}
|
||||
|
||||
leaf input-current {
|
||||
type oc-types:ieeefloat32;
|
||||
units amps;
|
||||
description
|
||||
"The input current draw of the power supply.";
|
||||
}
|
||||
|
||||
leaf input-voltage {
|
||||
type oc-types:ieeefloat32;
|
||||
units volts;
|
||||
description
|
||||
"Input voltage to the power supply.";
|
||||
}
|
||||
|
||||
leaf output-current {
|
||||
type oc-types:ieeefloat32;
|
||||
units amps;
|
||||
description
|
||||
"The output current supplied by the power supply.";
|
||||
}
|
||||
|
||||
leaf output-voltage {
|
||||
type oc-types:ieeefloat32;
|
||||
units volts;
|
||||
description
|
||||
"Output voltage supplied by the power supply.";
|
||||
}
|
||||
|
||||
leaf output-power {
|
||||
type oc-types:ieeefloat32;
|
||||
units watts;
|
||||
description
|
||||
"Output power supplied by the power supply.";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
module openconfig-platform-types {
|
||||
|
||||
yang-version "1";
|
||||
|
||||
// namespace
|
||||
namespace "http://openconfig.net/yang/platform-types";
|
||||
|
||||
prefix "oc-platform-types";
|
||||
|
||||
import openconfig-types { prefix oc-types; }
|
||||
import openconfig-extensions { prefix oc-ext; }
|
||||
|
||||
// meta
|
||||
organization
|
||||
"OpenConfig working group";
|
||||
|
||||
contact
|
||||
"OpenConfig working group
|
||||
www.openconfig.net";
|
||||
|
||||
description
|
||||
"This module defines data types (e.g., YANG identities)
|
||||
to support the OpenConfig component inventory model.";
|
||||
|
||||
oc-ext:openconfig-version "1.6.0";
|
||||
|
||||
// OpenConfig specific extensions for module metadata.
|
||||
oc-ext:regexp-posix;
|
||||
oc-ext:catalog-organization "openconfig";
|
||||
oc-ext:origin "openconfig";
|
||||
|
||||
// grouping statements
|
||||
grouping avg-min-max-instant-stats-precision1-celsius {
|
||||
description
|
||||
"Common grouping for recording temperature values in
|
||||
Celsius with 1 decimal precision. Values include the
|
||||
instantaneous, average, minimum, and maximum statistics";
|
||||
|
||||
leaf instant {
|
||||
type decimal64 {
|
||||
fraction-digits 1;
|
||||
}
|
||||
units celsius;
|
||||
description
|
||||
"The instantaneous value of the statistic.";
|
||||
}
|
||||
|
||||
leaf avg {
|
||||
type decimal64 {
|
||||
fraction-digits 1;
|
||||
}
|
||||
units celsius;
|
||||
description
|
||||
"The arithmetic mean value of the statistic over the
|
||||
sampling period.";
|
||||
}
|
||||
|
||||
leaf min {
|
||||
type decimal64 {
|
||||
fraction-digits 1;
|
||||
}
|
||||
units celsius;
|
||||
description
|
||||
"The minimum value of the statistic over the sampling
|
||||
period";
|
||||
}
|
||||
|
||||
leaf max {
|
||||
type decimal64 {
|
||||
fraction-digits 1;
|
||||
}
|
||||
units celsius;
|
||||
description
|
||||
"The maximum value of the statistic over the sampling
|
||||
period";
|
||||
}
|
||||
|
||||
uses oc-types:stat-interval-state;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
module openconfig-platform {
|
||||
|
||||
yang-version "1";
|
||||
|
||||
// namespace
|
||||
namespace "http://openconfig.net/yang/platform";
|
||||
|
||||
prefix "oc-platform";
|
||||
|
||||
import openconfig-platform-types { prefix oc-platform-types; }
|
||||
import openconfig-extensions { prefix oc-ext; }
|
||||
import openconfig-alarm-types { prefix oc-alarm-types; }
|
||||
|
||||
// meta
|
||||
organization "OpenConfig working group";
|
||||
|
||||
contact
|
||||
"OpenConfig working group
|
||||
www.openconfig.net";
|
||||
|
||||
description
|
||||
"This module defines a data model for representing a system
|
||||
component inventory, which can include hardware or software
|
||||
elements arranged in an arbitrary structure. The primary
|
||||
relationship supported by the model is containment, e.g.,
|
||||
components containing subcomponents.
|
||||
|
||||
It is expected that this model reflects every field replacable
|
||||
unit on the device at a minimum (i.e., additional information
|
||||
may be supplied about non-replacable components).
|
||||
|
||||
Every element in the inventory is termed a 'component' with each
|
||||
component expected to have a unique name and type, and optionally
|
||||
a unique system-assigned identifier and FRU number. The
|
||||
uniqueness is guaranteed by the system within the device.
|
||||
|
||||
Components may have properties defined by the system that are
|
||||
modeled as a list of key-value pairs. These may or may not be
|
||||
user-configurable. The model provides a flag for the system
|
||||
to optionally indicate which properties are user configurable.
|
||||
|
||||
Each component also has a list of 'subcomponents' which are
|
||||
references to other components. Appearance in a list of
|
||||
subcomponents indicates a containment relationship as described
|
||||
above. For example, a linecard component may have a list of
|
||||
references to port components that reside on the linecard.
|
||||
|
||||
This schema is generic to allow devices to express their own
|
||||
platform-specific structure. It may be augmented by additional
|
||||
component type-specific schemas that provide a common structure
|
||||
for well-known component types. In these cases, the system is
|
||||
expected to populate the common component schema, and may
|
||||
optionally also represent the component and its properties in the
|
||||
generic structure.
|
||||
|
||||
The properties for each component may include dynamic values,
|
||||
e.g., in the 'state' part of the schema. For example, a CPU
|
||||
component may report its utilization, temperature, or other
|
||||
physical properties. The intent is to capture all platform-
|
||||
specific physical data in one location, including inventory
|
||||
(presence or absence of a component) and state (physical
|
||||
attributes or status).";
|
||||
|
||||
oc-ext:openconfig-version "0.24.0";
|
||||
|
||||
// OpenConfig specific extensions for module metadata.
|
||||
oc-ext:regexp-posix;
|
||||
oc-ext:catalog-organization "openconfig";
|
||||
oc-ext:origin "openconfig";
|
||||
|
||||
// grouping statements
|
||||
|
||||
grouping platform-component-temp-alarm-state {
|
||||
description
|
||||
"Temperature alarm data for platform components";
|
||||
|
||||
// TODO(aashaikh): consider if these leaves could be in a
|
||||
// reusable grouping (not temperature-specific); threshold
|
||||
// may always need to be units specific.
|
||||
|
||||
leaf alarm-status {
|
||||
type boolean;
|
||||
description
|
||||
"A value of true indicates the alarm has been raised or
|
||||
asserted. The value should be false when the alarm is
|
||||
cleared.";
|
||||
}
|
||||
|
||||
leaf alarm-threshold {
|
||||
type uint32;
|
||||
description
|
||||
"The threshold value that was crossed for this alarm.";
|
||||
}
|
||||
|
||||
leaf alarm-severity {
|
||||
type identityref {
|
||||
base oc-alarm-types:OPENCONFIG_ALARM_SEVERITY;
|
||||
}
|
||||
description
|
||||
"The severity of the current alarm.";
|
||||
}
|
||||
}
|
||||
|
||||
grouping platform-component-temp-state {
|
||||
description
|
||||
"Temperature state data for device components";
|
||||
|
||||
container temperature {
|
||||
description
|
||||
"Temperature in degrees Celsius of the component. Values include
|
||||
the instantaneous, average, minimum, and maximum statistics. If
|
||||
avg/min/max statistics are not supported, the target is expected
|
||||
to just supply the instant value";
|
||||
|
||||
uses oc-platform-types:avg-min-max-instant-stats-precision1-celsius;
|
||||
uses platform-component-temp-alarm-state;
|
||||
}
|
||||
}
|
||||
|
||||
grouping platform-component-top {
|
||||
description
|
||||
"Top-level grouping for components in the device inventory";
|
||||
|
||||
container components {
|
||||
description
|
||||
"Enclosing container for the components in the system.";
|
||||
|
||||
list component {
|
||||
key "name";
|
||||
description
|
||||
"List of components, keyed by component name.";
|
||||
|
||||
leaf name {
|
||||
type leafref {
|
||||
path "../config/name";
|
||||
}
|
||||
description
|
||||
"References the component name";
|
||||
}
|
||||
|
||||
container state {
|
||||
config false;
|
||||
|
||||
description
|
||||
"Operational state data for each component";
|
||||
|
||||
uses platform-component-temp-state;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// data definition statements
|
||||
|
||||
uses platform-component-top;
|
||||
|
||||
|
||||
// augments
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
module openconfig-types {
|
||||
yang-version "1";
|
||||
|
||||
namespace "http://openconfig.net/yang/openconfig-types";
|
||||
|
||||
prefix "oc-types";
|
||||
|
||||
// import statements
|
||||
import openconfig-extensions { prefix oc-ext; }
|
||||
|
||||
// meta
|
||||
organization
|
||||
"OpenConfig working group";
|
||||
|
||||
contact
|
||||
"OpenConfig working group
|
||||
netopenconfig@googlegroups.com";
|
||||
|
||||
description
|
||||
"This module contains a set of general type definitions that
|
||||
are used across OpenConfig models. It can be imported by modules
|
||||
that make use of these types.";
|
||||
|
||||
oc-ext:openconfig-version "1.0.0";
|
||||
|
||||
// OpenConfig specific extensions for module metadata.
|
||||
oc-ext:regexp-posix;
|
||||
oc-ext:catalog-organization "openconfig";
|
||||
oc-ext:origin "openconfig";
|
||||
|
||||
typedef stat-interval {
|
||||
type uint64;
|
||||
units nanoseconds;
|
||||
description
|
||||
"A time interval over which a set of statistics is computed.
|
||||
A common usage is to report the interval over which
|
||||
avg/min/max stats are computed and reported.";
|
||||
}
|
||||
|
||||
grouping stat-interval-state {
|
||||
description
|
||||
"Reusable leaf definition for stats computation interval";
|
||||
|
||||
leaf interval {
|
||||
type oc-types:stat-interval;
|
||||
description
|
||||
"If supported by the system, this reports the time interval
|
||||
over which the min/max/average statistics are computed by
|
||||
the system.";
|
||||
}
|
||||
}
|
||||
|
||||
typedef ieeefloat32 {
|
||||
type binary {
|
||||
length "4";
|
||||
}
|
||||
description
|
||||
"An IEEE 32-bit floating point number. The format of this number
|
||||
is of the form:
|
||||
1-bit sign
|
||||
8-bit exponent
|
||||
23-bit fraction
|
||||
The floating point value is calculated using:
|
||||
(-1)**S * 2**(Exponent-127) * (1+Fraction)";
|
||||
}
|
||||
}
|
141
plugins/inputs/gnmi/testcases/issue_15046/responses.json
Normal file
141
plugins/inputs/gnmi/testcases/issue_15046/responses.json
Normal file
|
@ -0,0 +1,141 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1711178737105194000",
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"origin": "openconfig",
|
||||
"elem": [
|
||||
{
|
||||
"name": "components"
|
||||
},
|
||||
{
|
||||
"name": "component",
|
||||
"key": {
|
||||
"name": "PowerSupply1/A"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "power-supply"
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"jsonIetfVal": "eyJvcGVuY29uZmlnLXBsYXRmb3JtLXBzdTplbmFibGVkIjp0cnVlLCJvcGVuY29uZmlnLXBsYXRmb3JtLXBzdTpjYXBhY2l0eSI6IlJETEFBQT09Iiwib3BlbmNvbmZpZy1wbGF0Zm9ybS1wc3U6aW5wdXQtY3VycmVudCI6IlB2RW02UT09Iiwib3BlbmNvbmZpZy1wbGF0Zm9ybS1wc3U6aW5wdXQtdm9sdGFnZSI6IlExQ0FBQT09Iiwib3BlbmNvbmZpZy1wbGF0Zm9ybS1wc3U6b3V0cHV0LWN1cnJlbnQiOiJQNW43NXc9PSIsIm9wZW5jb25maWctcGxhdGZvcm0tcHN1Om91dHB1dC12b2x0YWdlIjoiUW1GM3p3PT0iLCJvcGVuY29uZmlnLXBsYXRmb3JtLXBzdTpvdXRwdXQtcG93ZXIiOiJRb2xBQUE9PSJ9"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "openconfig",
|
||||
"elem": [
|
||||
{
|
||||
"name": "components"
|
||||
},
|
||||
{
|
||||
"name": "component",
|
||||
"key": {
|
||||
"name": "PowerSupply1/B"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "power-supply"
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"jsonIetfVal": "eyJvcGVuY29uZmlnLXBsYXRmb3JtLXBzdTplbmFibGVkIjp0cnVlLCJvcGVuY29uZmlnLXBsYXRmb3JtLXBzdTpjYXBhY2l0eSI6IlJETEFBQT09Iiwib3BlbmNvbmZpZy1wbGF0Zm9ybS1wc3U6aW5wdXQtY3VycmVudCI6IlBzazNUQT09Iiwib3BlbmNvbmZpZy1wbGF0Zm9ybS1wc3U6aW5wdXQtdm9sdGFnZSI6IlExSEFBQT09Iiwib3BlbmNvbmZpZy1wbGF0Zm9ybS1wc3U6b3V0cHV0LWN1cnJlbnQiOiJQM0FneFE9PSIsIm9wZW5jb25maWctcGxhdGZvcm0tcHN1Om91dHB1dC12b2x0YWdlIjoiUW1GM3p3PT0iLCJvcGVuY29uZmlnLXBsYXRmb3JtLXBzdTpvdXRwdXQtcG93ZXIiOiJRaytBQUE9PSJ9"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1715838159171548000",
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"origin": "openconfig",
|
||||
"elem": [
|
||||
{
|
||||
"name": "components"
|
||||
},
|
||||
{
|
||||
"name": "component",
|
||||
"key": {
|
||||
"name": "InletTempSensor1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "temperature"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"jsonIetfVal": "eyJpbnN0YW50IjoiMzUuMDAwMDAwIiwiYXZnIjoiMjQuMDAwMDAwIiwibWluIjoiMC4wMDAwMDAiLCJtYXgiOiIzNi4wMDAwMDAiLCJpbnRlcnZhbCI6IjE4MDAwMDAwMDAwMCIsImFsYXJtLXN0YXR1cyI6ZmFsc2UsImFsYXJtLXRocmVzaG9sZCI6MCwiYWxhcm0tc2V2ZXJpdHkiOiJvcGVuY29uZmlnLWFsYXJtLXR5cGVzOk1JTk9SIn0="
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "openconfig",
|
||||
"elem": [
|
||||
{
|
||||
"name": "components"
|
||||
},
|
||||
{
|
||||
"name": "component",
|
||||
"key": {
|
||||
"name": "OutletTempSensor1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "temperature"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"jsonIetfVal": "eyJpbnN0YW50IjoiNDQuMDAwMDAwIiwiYXZnIjoiMjkuMDAwMDAwIiwibWluIjoiMC4wMDAwMDAiLCJtYXgiOiI0NC4wMDAwMDAiLCJpbnRlcnZhbCI6IjE4MDAwMDAwMDAwMCIsImFsYXJtLXN0YXR1cyI6ZmFsc2UsImFsYXJtLXRocmVzaG9sZCI6MCwiYWxhcm0tc2V2ZXJpdHkiOiJvcGVuY29uZmlnLWFsYXJtLXR5cGVzOk1JTk9SIn0="
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "openconfig",
|
||||
"elem": [
|
||||
{
|
||||
"name": "components"
|
||||
},
|
||||
{
|
||||
"name": "component",
|
||||
"key": {
|
||||
"name": "HotSpotTempSensor1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"name": "temperature"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"jsonIetfVal": "eyJpbnN0YW50IjoiNTguMDAwMDAwIiwiYXZnIjoiMzkuMDAwMDAwIiwibWluIjoiMC4wMDAwMDAiLCJtYXgiOiI1OS4wMDAwMDAiLCJpbnRlcnZhbCI6IjE4MDAwMDAwMDAwMCIsImFsYXJtLXN0YXR1cyI6ZmFsc2UsImFsYXJtLXRocmVzaG9sZCI6MCwiYWxhcm0tc2V2ZXJpdHkiOiJvcGVuY29uZmlnLWFsYXJtLXR5cGVzOk1JTk9SIn0="
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
18
plugins/inputs/gnmi/testcases/issue_15046/telegraf.conf
Normal file
18
plugins/inputs/gnmi/testcases/issue_15046/telegraf.conf
Normal file
|
@ -0,0 +1,18 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
path_guessing_strategy = "subscription"
|
||||
yang_model_paths = ["testcases/issue_15046/models"]
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "psu"
|
||||
origin = "openconfig"
|
||||
path = "/components/component/power-supply/state"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "60s"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "temp"
|
||||
origin = "openconfig"
|
||||
path = "/components/component/state/temperature"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "60s"
|
1
plugins/inputs/gnmi/testcases/issue_15546/expected.out
Normal file
1
plugins/inputs/gnmi/testcases/issue_15546/expected.out
Normal file
|
@ -0,0 +1 @@
|
|||
event-stats,path=openconfig-system:/system/openconfig-events:event-stats/state,source=127.0.0.1 state/acked=0u,state/cleared=0u,state/events=4u,state/raised=0u 1718942414831832038
|
70
plugins/inputs/gnmi/testcases/issue_15546/responses.json
Normal file
70
plugins/inputs/gnmi/testcases/issue_15546/responses.json
Normal file
|
@ -0,0 +1,70 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": "1718942414831832038",
|
||||
"prefix": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "openconfig-system:system"
|
||||
},
|
||||
{
|
||||
"name": "openconfig-events:event-stats"
|
||||
},
|
||||
{
|
||||
"name": "state"
|
||||
}
|
||||
]
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "acked"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "cleared"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "events"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"elem": [
|
||||
{
|
||||
"name": "raised"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": "0"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
10
plugins/inputs/gnmi/testcases/issue_15546/telegraf.conf
Normal file
10
plugins/inputs/gnmi/testcases/issue_15546/telegraf.conf
Normal file
|
@ -0,0 +1,10 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
prefix_tag_key_with_path = true
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "event-stats"
|
||||
origin = "openconfig-system"
|
||||
path = "/system/event-stats"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "10s"
|
1
plugins/inputs/gnmi/testcases/issue_16476/expected.out
Normal file
1
plugins/inputs/gnmi/testcases/issue_16476/expected.out
Normal file
|
@ -0,0 +1 @@
|
|||
ifcounters,path=Ciena:/oc-if:interfaces/oc-if:interface/oc-if:state/oc-if:counters,source=127.0.0.1 in_1024_to_1518_octet_pkts=15680405047u,in_128_to_255_octet_pkts=12809649942u,in_1519_to_2047_octet_pkts=35815850565u,in_2048_to_4095_octet_pkts=0u,in_256_to_511_octet_pkts=5257910993u,in_4096_to_9216_octet_pkts=0u,in_512_to_1023_octet_pkts=6139561818u,in_64_octet_pkts=4u,in_65_to_127_octet_pkts=146456592549u,in_broadcast_pkts=167166u,in_crc_error_pkts=0u,in_discards=236u,in_discards_octets=31492u,in_dropped_octets=31492u,in_dropped_pkts=236u,in_errors=0u,in_jabber_pkts=0u,in_multicast_pkts=76815719u,in_octets=95890972327359u,in_oversize_pkts=0u,in_pkts=222159970919u,in_undersize_pkts=0u,in_unicast_pkts=222082988034u,last_clear=1679547185677412529u,link_flap_events=0u,name="\"1\"",out_1519_to_2047_octet_pkts=211382493634u,out_2048_to_4095_octet_pkts=0u,out_4096_to_9216_octet_pkts=0u,out_broadcast_pkts=2609579674u,out_errors=0u,out_multicast_pkts=332069076u,out_octets=885293268981054u,out_pkts=677379680498u,out_unicast_pkts=674438031748u 1739206587847000000
|
481
plugins/inputs/gnmi/testcases/issue_16476/responses.json
Normal file
481
plugins/inputs/gnmi/testcases/issue_16476/responses.json
Normal file
|
@ -0,0 +1,481 @@
|
|||
[
|
||||
{
|
||||
"update": {
|
||||
"timestamp": 1739206587847000000,
|
||||
"prefix": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "oc-if:interfaces"
|
||||
},
|
||||
{
|
||||
"name": "oc-if:interface"
|
||||
},
|
||||
{
|
||||
"name": "oc-if:state"
|
||||
},
|
||||
{
|
||||
"name": "oc-if:counters"
|
||||
}
|
||||
]
|
||||
},
|
||||
"update": [
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-1024-to-1518-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 15680405047
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-128-to-255-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 12809649942
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-1519-to-2047-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 35815850565
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-2048-to-4095-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-256-to-511-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 5257910993
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-4096-to-9216-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-512-to-1023-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 6139561818
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-64-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-65-to-127-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 146456592549
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-broadcast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 167166
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-crc-error-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-discards"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 236
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-discards-octets"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 31492
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-dropped-octets"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 31492
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-dropped-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 236
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-errors"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-jabber-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-multicast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 76815719
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-octets"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 95890972327359
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-oversize-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 222159970919
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-undersize-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "in-unicast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 222082988034
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "last-clear"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 1679547185677412529
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "link-flap-events"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "name"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"stringVal": "\"1\""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-1519-to-2047-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 211382493634
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-2048-to-4095-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-4096-to-9216-octet-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-broadcast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 2609579674
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-errors"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-multicast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 332069076
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-octets"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 885293268981054
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 677379680498
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"origin": "Ciena",
|
||||
"elem": [
|
||||
{
|
||||
"name": "out-unicast-pkts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"val": {
|
||||
"uintVal": 674438031748
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
10
plugins/inputs/gnmi/testcases/issue_16476/telegraf.conf
Normal file
10
plugins/inputs/gnmi/testcases/issue_16476/telegraf.conf
Normal file
|
@ -0,0 +1,10 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
enforce_first_namespace_as_origin = false
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "ifcounters"
|
||||
origin = "Ciena"
|
||||
path = "/oc-if:interfaces/oc-if:interface/oc-if:state/oc-if:counters"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "30s"
|
11
plugins/inputs/gnmi/testcases/issue_16515/expected.out
Normal file
11
plugins/inputs/gnmi/testcases/issue_16515/expected.out
Normal file
|
@ -0,0 +1,11 @@
|
|||
qmon,name=xe-0/0/0:0,path=/junos/system/linecard/qmon-sw,source=127.0.0.1 state/parent_ae_name="" 1739811071353000000
|
||||
qmon,name=xe-0/0/0:0,path=/junos/system/linecard/qmon-sw,queue_number=0,source=127.0.0.1 egress/bytes=25084650213u,egress/packets=97812973u,egress/peak_buffer_occupancy_bytes=416u,egress/peak_buffer_occupancy_percent=0u,egress/red_drop_bytes_color_0=0u,egress/red_drop_bytes_color_1=0u,egress/red_drop_bytes_color_2=0u,egress/red_drop_bytes_color_3=0u,egress/red_drop_packets_color_0=0u,egress/red_drop_packets_color_1=0u,egress/red_drop_packets_color_2=0u,egress/red_drop_packets_color_3=0u,egress/tail_drop_bytes=0u,egress/tail_drop_packets=0u 1739811071353000000
|
||||
qmon,name=xe-0/0/0:0,path=/junos/system/linecard/qmon-sw,queue_number=1,source=127.0.0.1 egress/bytes=0u,egress/packets=0u,egress/peak_buffer_occupancy_bytes=0u,egress/peak_buffer_occupancy_percent=0u,egress/red_drop_bytes_color_0=0u,egress/red_drop_bytes_color_1=0u,egress/red_drop_bytes_color_2=0u,egress/red_drop_bytes_color_3=0u,egress/red_drop_packets_color_0=0u,egress/red_drop_packets_color_1=0u,egress/red_drop_packets_color_2=0u,egress/red_drop_packets_color_3=0u,egress/tail_drop_bytes=0u,egress/tail_drop_packets=0u 1739811071353000000
|
||||
qmon,name=xe-0/0/0:0,path=/junos/system/linecard/qmon-sw,queue_number=2,source=127.0.0.1 egress/bytes=10957969881u,egress/packets=105352268u,egress/peak_buffer_occupancy_bytes=208u,egress/peak_buffer_occupancy_percent=0u,egress/red_drop_bytes_color_0=0u,egress/red_drop_bytes_color_1=0u,egress/red_drop_bytes_color_2=0u,egress/red_drop_bytes_color_3=0u,egress/red_drop_packets_color_0=0u,egress/red_drop_packets_color_1=0u,egress/red_drop_packets_color_2=0u,egress/red_drop_packets_color_3=0u,egress/tail_drop_bytes=0u,egress/tail_drop_packets=0u 1739811071353000000
|
||||
qmon,name=xe-0/0/0:0,path=/junos/system/linecard/qmon-sw,queue_number=3,source=127.0.0.1 egress/bytes=0u,egress/packets=0u,egress/peak_buffer_occupancy_bytes=0u,egress/peak_buffer_occupancy_percent=0u,egress/red_drop_bytes_color_0=0u,egress/red_drop_bytes_color_1=0u,egress/red_drop_bytes_color_2=0u,egress/red_drop_bytes_color_3=0u,egress/red_drop_packets_color_0=0u,egress/red_drop_packets_color_1=0u,egress/red_drop_packets_color_2=0u,egress/red_drop_packets_color_3=0u,egress/tail_drop_bytes=0u,egress/tail_drop_packets=0u 1739811071353000000
|
||||
qmon,name=xe-0/0/0:0,path=/junos/system/linecard/qmon-sw,queue_number=4,source=127.0.0.1 egress/bytes=0u,egress/packets=0u,egress/peak_buffer_occupancy_bytes=0u,egress/peak_buffer_occupancy_percent=0u,egress/red_drop_bytes_color_0=0u,egress/red_drop_bytes_color_1=0u,egress/red_drop_bytes_color_2=0u,egress/red_drop_bytes_color_3=0u,egress/red_drop_packets_color_0=0u,egress/red_drop_packets_color_1=0u,egress/red_drop_packets_color_2=0u,egress/red_drop_packets_color_3=0u,egress/tail_drop_bytes=0u,egress/tail_drop_packets=0u 1739811071353000000
|
||||
qmon,name=xe-0/0/0:0,path=/junos/system/linecard/qmon-sw,queue_number=5,source=127.0.0.1 egress/bytes=1020800306u,egress/packets=12027619u,egress/peak_buffer_occupancy_bytes=0u,egress/peak_buffer_occupancy_percent=0u,egress/red_drop_bytes_color_0=0u,egress/red_drop_bytes_color_1=0u,egress/red_drop_bytes_color_2=0u,egress/red_drop_bytes_color_3=0u,egress/red_drop_packets_color_0=0u,egress/red_drop_packets_color_1=0u,egress/red_drop_packets_color_2=0u,egress/red_drop_packets_color_3=0u,egress/tail_drop_bytes=0u,egress/tail_drop_packets=0u 1739811071353000000
|
||||
qmon,name=xe-0/0/0:0,path=/junos/system/linecard/qmon-sw,queue_number=6,source=127.0.0.1 egress/bytes=1257881347u,egress/packets=4773346u,egress/peak_buffer_occupancy_bytes=624u,egress/peak_buffer_occupancy_percent=0u,egress/red_drop_bytes_color_0=0u,egress/red_drop_bytes_color_1=0u,egress/red_drop_bytes_color_2=0u,egress/red_drop_bytes_color_3=0u,egress/red_drop_packets_color_0=0u,egress/red_drop_packets_color_1=0u,egress/red_drop_packets_color_2=0u,egress/red_drop_packets_color_3=0u,egress/tail_drop_bytes=0u,egress/tail_drop_packets=0u 1739811071353000000
|
||||
qmon,name=xe-0/0/0:0,path=/junos/system/linecard/qmon-sw,queue_number=7,source=127.0.0.1 egress/bytes=0u,egress/packets=0u,egress/peak_buffer_occupancy_bytes=0u,egress/peak_buffer_occupancy_percent=0u,egress/red_drop_bytes_color_0=0u,egress/red_drop_bytes_color_1=0u,egress/red_drop_bytes_color_2=0u,egress/red_drop_bytes_color_3=0u,egress/red_drop_packets_color_0=0u,egress/red_drop_packets_color_1=0u,egress/red_drop_packets_color_2=0u,egress/red_drop_packets_color_3=0u,egress/tail_drop_bytes=0u,egress/tail_drop_packets=0u 1739811071353000000
|
||||
qmon,name=xe-0/0/0:0,path=/junos/system/linecard/qmon-sw,queue_number=8,source=127.0.0.1 egress/bytes=8898987190u,egress/packets=75415151u,egress/peak_buffer_occupancy_bytes=208u,egress/peak_buffer_occupancy_percent=0u,egress/red_drop_bytes_color_0=0u,egress/red_drop_bytes_color_1=0u,egress/red_drop_bytes_color_2=0u,egress/red_drop_bytes_color_3=0u,egress/red_drop_packets_color_0=0u,egress/red_drop_packets_color_1=0u,egress/red_drop_packets_color_2=0u,egress/red_drop_packets_color_3=0u,egress/tail_drop_bytes=0u,egress/tail_drop_packets=0u 1739811071353000000
|
||||
qmon,name=xe-0/0/0:0,path=/junos/system/linecard/qmon-sw,queue_number=9,source=127.0.0.1 egress/bytes=16919364168u,egress/packets=157255632u,egress/peak_buffer_occupancy_bytes=208u,egress/peak_buffer_occupancy_percent=0u,egress/red_drop_bytes_color_0=0u,egress/red_drop_bytes_color_1=0u,egress/red_drop_bytes_color_2=0u,egress/red_drop_bytes_color_3=0u,egress/red_drop_packets_color_0=0u,egress/red_drop_packets_color_1=0u,egress/red_drop_packets_color_2=0u,egress/red_drop_packets_color_3=0u,egress/tail_drop_bytes=0u,egress/tail_drop_packets=0u 1739811071353000000
|
2853
plugins/inputs/gnmi/testcases/issue_16515/responses.json
Normal file
2853
plugins/inputs/gnmi/testcases/issue_16515/responses.json
Normal file
File diff suppressed because it is too large
Load diff
9
plugins/inputs/gnmi/testcases/issue_16515/telegraf.conf
Normal file
9
plugins/inputs/gnmi/testcases/issue_16515/telegraf.conf
Normal file
|
@ -0,0 +1,9 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "qmon"
|
||||
origin = "openconfig"
|
||||
path = "/junos/system/linecard/qmon-sw"
|
||||
subscription_mode = "sample"
|
||||
sample_interval = "60s"
|
|
@ -0,0 +1,5 @@
|
|||
interfaces_logical_status,descr/description=Loopback,index=0,name=lo0,path=/interfaces/interface/subinterfaces/subinterface,source=127.0.0.1 oper_status="UP" 1674081667238697959
|
||||
interfaces_logical_status,descr/description=Local:Mgmt,index=312,name=irb,path=/interfaces/interface/subinterfaces/subinterface,source=127.0.0.1 oper_status="UP" 1674081667241404442
|
||||
interfaces_logical_status,descr/description=Core:GRE:abc-def-dmn1-staging:{GRE_Tunnel},index=2,name=gr-0/0/0,path=/interfaces/interface/subinterfaces/subinterface,source=127.0.0.1 oper_status="UP" 1674081667243155079
|
||||
interfaces_logical_status,descr/description=Core:PacketFabric:abc-def-dmn1-staging:{PF-BC-DAL-SFO-12345},index=1410,name=xe-0/1/1,path=/interfaces/interface/subinterfaces/subinterface,source=127.0.0.1 oper_status="UP" 1674081667250570407
|
||||
interfaces_logical_status,descr/description=Core:PacketFabric:uvw-xyz-dmn1-staging:{PF-BC-CHI-SFO-67890},index=16386,name=xe-0/1/5,path=/interfaces/interface/subinterfaces/subinterface,source=127.0.0.1 oper_status="LOWER_LAYER_DOWN" 1674081667251795605
|
|
@ -0,0 +1,13 @@
|
|||
[
|
||||
{"update":{"timestamp":"1674081667224189253", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"lo0"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"0"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"description"}]}, "val":{"stringVal":"Loopback"}}]}},
|
||||
{"update":{"timestamp":"1674081667226968153", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"irb"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"312"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"description"}]}, "val":{"stringVal":"Local:Mgmt"}}]}},
|
||||
{"update":{"timestamp":"1674081667228936729", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"gr-0/0/0"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"3"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"description"}]}, "val":{"stringVal":"Core:GRE:abc-def-dmn1-staging:{GRE_Tunnel}"}}]}},
|
||||
{"update":{"timestamp":"1674081667236178737", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/1"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"1410"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"description"}]}, "val":{"stringVal":"Core:PacketFabric:abc-def-dmn1-staging:{PF-BC-DAL-SFO-12345}"}}]}},
|
||||
{"update":{"timestamp":"1674081667236377628", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/5"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"1412"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"description"}]}, "val":{"stringVal":"Core:PacketFabric:uvw-xyz-dmn1-staging:{PF-BC-CHI-SFO-67890}"}}]}},
|
||||
{"update":{"timestamp":"1674081667238697959", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"lo0"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"0"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"oper-status"}]}, "val":{"stringVal":"UP"}}]}},
|
||||
{"update":{"timestamp":"1674081667241404442", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"irb"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"312"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"oper-status"}]}, "val":{"stringVal":"UP"}}]}},
|
||||
{"update":{"timestamp":"1674081667243155079", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"gr-0/0/0"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"2"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"oper-status"}]}, "val":{"stringVal":"UP"}}]}},
|
||||
{"update":{"timestamp":"1674081667250570407", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/1"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"1410"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"oper-status"}]}, "val":{"stringVal":"UP"}}]}},
|
||||
{"update":{"timestamp":"1674081667251795605", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/5"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"16386"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"oper-status"}]}, "val":{"stringVal":"LOWER_LAYER_DOWN"}}]}},
|
||||
{"syncResponse":true}
|
||||
]
|
|
@ -0,0 +1,15 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
redial = "10s"
|
||||
|
||||
[[inputs.gnmi.tag_subscription]]
|
||||
name = "descr"
|
||||
origin = "openconfig"
|
||||
path = "/interfaces/interface/subinterfaces/subinterface/state/description"
|
||||
subscription_mode = "on_change"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interfaces_logical_status"
|
||||
origin = "openconfig"
|
||||
path = "/interfaces/interface/subinterfaces/subinterface/state/oper-status"
|
||||
subscription_mode = "on_change"
|
|
@ -0,0 +1,5 @@
|
|||
interfaces_logical_status,descr/description=Loopback,index=0,name=lo0,path=/interfaces/interface/subinterfaces/subinterface,source=127.0.0.1 oper_status="UP" 1674081667238697959
|
||||
interfaces_logical_status,descr/description=Local:Mgmt,index=312,name=irb,path=/interfaces/interface/subinterfaces/subinterface,source=127.0.0.1 oper_status="UP" 1674081667241404442
|
||||
interfaces_logical_status,descr/description=Core:GRE:abc-def-dmn1-staging:{GRE_Tunnel},index=2,name=gr-0/0/0,path=/interfaces/interface/subinterfaces/subinterface,source=127.0.0.1 oper_status="UP" 1674081667243155079
|
||||
interfaces_logical_status,descr/description=Core:PacketFabric:abc-def-dmn1-staging:{PF-BC-DAL-SFO-12345},index=1410,name=xe-0/1/1,path=/interfaces/interface/subinterfaces/subinterface,source=127.0.0.1 oper_status="UP" 1674081667250570407
|
||||
interfaces_logical_status,descr/description=Core:PacketFabric:uvw-xyz-dmn1-staging:{PF-BC-CHI-SFO-67890},index=16386,name=xe-0/1/5,path=/interfaces/interface/subinterfaces/subinterface,source=127.0.0.1 oper_status="LOWER_LAYER_DOWN" 1674081667251795605
|
|
@ -0,0 +1,13 @@
|
|||
[
|
||||
{"update":{"timestamp":"1674081667224189253", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"lo0"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"0"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"description"}]}, "val":{"stringVal":"Loopback"}}]}},
|
||||
{"update":{"timestamp":"1674081667226968153", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"irb"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"312"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"description"}]}, "val":{"stringVal":"Local:Mgmt"}}]}},
|
||||
{"update":{"timestamp":"1674081667228936729", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"gr-0/0/0"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"3"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"description"}]}, "val":{"stringVal":"Core:GRE:abc-def-dmn1-staging:{GRE_Tunnel}"}}]}},
|
||||
{"update":{"timestamp":"1674081667236178737", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/1"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"1410"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"description"}]}, "val":{"stringVal":"Core:PacketFabric:abc-def-dmn1-staging:{PF-BC-DAL-SFO-12345}"}}]}},
|
||||
{"update":{"timestamp":"1674081667236377628", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/5"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"1412"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"description"}]}, "val":{"stringVal":"Core:PacketFabric:uvw-xyz-dmn1-staging:{PF-BC-CHI-SFO-67890}"}}]}},
|
||||
{"update":{"timestamp":"1674081667238697959", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"lo0"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"0"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"oper-status"}]}, "val":{"stringVal":"UP"}}]}},
|
||||
{"update":{"timestamp":"1674081667241404442", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"irb"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"312"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"oper-status"}]}, "val":{"stringVal":"UP"}}]}},
|
||||
{"update":{"timestamp":"1674081667243155079", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"gr-0/0/0"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"2"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"oper-status"}]}, "val":{"stringVal":"UP"}}]}},
|
||||
{"update":{"timestamp":"1674081667250570407", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/1"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"1410"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"oper-status"}]}, "val":{"stringVal":"UP"}}]}},
|
||||
{"update":{"timestamp":"1674081667251795605", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/5"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"16386"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"oper-status"}]}, "val":{"stringVal":"LOWER_LAYER_DOWN"}}]}},
|
||||
{"syncResponse":true}
|
||||
]
|
|
@ -0,0 +1,16 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
redial = "10s"
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "descr"
|
||||
origin = "openconfig"
|
||||
path = "/interfaces/interface/subinterfaces/subinterface/state/description"
|
||||
subscription_mode = "on_change"
|
||||
tag_only = true
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interfaces_logical_status"
|
||||
origin = "openconfig"
|
||||
path = "/interfaces/interface/subinterfaces/subinterface/state/oper-status"
|
||||
subscription_mode = "on_change"
|
|
@ -0,0 +1,3 @@
|
|||
interfaces_logical_status,descr/description=Core:PacketFabric:abc-def-dmn1-staging:{PF-BC-DAL-SFO-12345},index=1410,name=xe-0/1/1,path=/interfaces/interface/subinterfaces/subinterface,source=127.0.0.1 oper_status="UP" 1674081667250570407
|
||||
interfaces_logical_status,descr/description=Core:PacketFabric:uvw-xyz-dmn1-staging:{PF-BC-CHI-SFO-67890},index=1412,name=xe-0/1/1,path=/interfaces/interface/subinterfaces/subinterface,source=127.0.0.1 oper_status="UP" 1674081667250784367
|
||||
interfaces_logical_status,index=32767,name=xe-0/1/1,path=/interfaces/interface/subinterfaces/subinterface,source=127.0.0.1 oper_status="UP" 1674081667250994907
|
|
@ -0,0 +1,9 @@
|
|||
[
|
||||
{"update":{"timestamp":"1674081667236178737", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/1"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"1410"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"description"}]}, "val":{"stringVal":"Core:PacketFabric:abc-def-dmn1-staging:{PF-BC-DAL-SFO-12345}"}}]}},
|
||||
{"update":{"timestamp":"1674081667236377628", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/1"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"1412"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"description"}]}, "val":{"stringVal":"Core:PacketFabric:uvw-xyz-dmn1-staging:{PF-BC-CHI-SFO-67890}"}}]}},
|
||||
{"update":{"timestamp":"1674081667236582084", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/1"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"32767"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"description"}]}, "val":{"stringVal":""}}]}},
|
||||
{"update":{"timestamp":"1674081667250570407", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/1"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"1410"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"oper-status"}]}, "val":{"stringVal":"UP"}}]}},
|
||||
{"update":{"timestamp":"1674081667250784367", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/1"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"1412"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"oper-status"}]}, "val":{"stringVal":"UP"}}]}},
|
||||
{"update":{"timestamp":"1674081667250994907", "prefix":{"elem":[{"name":"interfaces"}, {"name":"interface", "key":{"name":"xe-0/1/1"}}, {"name":"subinterfaces"}, {"name":"subinterface", "key":{"index":"32767"}}]}, "update":[{"path":{"elem":[{"name":"state"}, {"name":"oper-status"}]}, "val":{"stringVal":"UP"}}]}},
|
||||
{"syncResponse":true}
|
||||
]
|
|
@ -0,0 +1,16 @@
|
|||
[[inputs.gnmi]]
|
||||
addresses = ["dummy"]
|
||||
redial = "10s"
|
||||
|
||||
[[inputs.gnmi.tag_subscription]]
|
||||
name = "descr"
|
||||
origin = "openconfig"
|
||||
path = "/interfaces/interface/subinterfaces/subinterface/state/description"
|
||||
subscription_mode = "on_change"
|
||||
elements = ["interface", "subinterface"]
|
||||
|
||||
[[inputs.gnmi.subscription]]
|
||||
name = "interfaces_logical_status"
|
||||
origin = "openconfig"
|
||||
path = "/interfaces/interface/subinterfaces/subinterface/state/oper-status"
|
||||
subscription_mode = "on_change"
|
166
plugins/inputs/gnmi/update_fields.go
Normal file
166
plugins/inputs/gnmi/update_fields.go
Normal file
|
@ -0,0 +1,166 @@
|
|||
package gnmi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/openconfig/gnmi/proto/gnmi"
|
||||
"github.com/openconfig/gnmi/value"
|
||||
)
|
||||
|
||||
type keyValuePair struct {
|
||||
key []string
|
||||
value interface{}
|
||||
}
|
||||
|
||||
type updateField struct {
|
||||
path *pathInfo
|
||||
value interface{}
|
||||
}
|
||||
|
||||
func (h *handler) newFieldsFromUpdate(path *pathInfo, update *gnmi.Update) ([]updateField, error) {
|
||||
if update.Val == nil || update.Val.Value == nil {
|
||||
return []updateField{{path: path}}, nil
|
||||
}
|
||||
|
||||
// Apply some special handling for special types
|
||||
switch v := update.Val.Value.(type) {
|
||||
case *gnmi.TypedValue_AsciiVal: // not handled in ToScalar
|
||||
return []updateField{{path, v.AsciiVal}}, nil
|
||||
case *gnmi.TypedValue_JsonVal: // requires special path handling
|
||||
return h.processJSON(path, v.JsonVal)
|
||||
case *gnmi.TypedValue_JsonIetfVal: // requires special path handling
|
||||
return h.processJSONIETF(path, v.JsonIetfVal)
|
||||
}
|
||||
|
||||
// Convert the protobuf "oneof" data to a Golang type.
|
||||
nativeType, err := value.ToScalar(update.Val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []updateField{{path, nativeType}}, nil
|
||||
}
|
||||
|
||||
func (h *handler) processJSON(path *pathInfo, data []byte) ([]updateField, error) {
|
||||
var nested interface{}
|
||||
if err := json.Unmarshal(data, &nested); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse JSON value: %w", err)
|
||||
}
|
||||
|
||||
// Flatten the JSON data to get a key-value map
|
||||
entries := flatten(nested)
|
||||
|
||||
// Create an update-field with the complete path for all entries
|
||||
fields := make([]updateField, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
p := path.appendSegments(entry.key...)
|
||||
if h.enforceFirstNamespaceAsOrigin {
|
||||
p.enforceFirstNamespaceAsOrigin()
|
||||
}
|
||||
|
||||
fields = append(fields, updateField{
|
||||
path: p,
|
||||
value: entry.value,
|
||||
})
|
||||
}
|
||||
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
func (h *handler) processJSONIETF(path *pathInfo, data []byte) ([]updateField, error) {
|
||||
var nested interface{}
|
||||
if err := json.Unmarshal(data, &nested); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse JSON value: %w", err)
|
||||
}
|
||||
|
||||
// Flatten the JSON data to get a key-value map
|
||||
entries := flatten(nested)
|
||||
|
||||
// Lookup the data in the YANG model if any
|
||||
if h.decoder != nil {
|
||||
for i, e := range entries {
|
||||
var namespace, identifier string
|
||||
for _, k := range e.key {
|
||||
if n, _, found := strings.Cut(k, ":"); found {
|
||||
namespace = n
|
||||
}
|
||||
}
|
||||
|
||||
// IETF nodes referencing YANG entries require a namespace
|
||||
if namespace == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if a, b, found := strings.Cut(e.key[len(e.key)-1], ":"); !found {
|
||||
identifier = a
|
||||
} else {
|
||||
identifier = b
|
||||
}
|
||||
|
||||
if decoded, err := h.decoder.DecodeLeafElement(namespace, identifier, e.value); err != nil {
|
||||
h.log.Debugf("Decoding %s:%s failed: %v", namespace, identifier, err)
|
||||
} else {
|
||||
entries[i].value = decoded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fields := make([]updateField, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
p := path.appendSegments(entry.key...)
|
||||
if h.enforceFirstNamespaceAsOrigin {
|
||||
p.enforceFirstNamespaceAsOrigin()
|
||||
}
|
||||
|
||||
// Try to lookup the full path to decode the field according to the
|
||||
// YANG model if any
|
||||
if h.decoder != nil {
|
||||
origin, fieldPath := p.path()
|
||||
if decoded, err := h.decoder.DecodePathElement(origin, fieldPath, entry.value); err != nil {
|
||||
h.log.Debugf("Decoding %s failed: %v", p, err)
|
||||
} else {
|
||||
entry.value = decoded
|
||||
}
|
||||
}
|
||||
|
||||
// Create an update-field with the complete path for all entries
|
||||
fields = append(fields, updateField{
|
||||
path: p,
|
||||
value: entry.value,
|
||||
})
|
||||
}
|
||||
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
func flatten(nested interface{}) []keyValuePair {
|
||||
var values []keyValuePair
|
||||
|
||||
switch n := nested.(type) {
|
||||
case map[string]interface{}:
|
||||
for k, child := range n {
|
||||
for _, c := range flatten(child) {
|
||||
values = append(values, keyValuePair{
|
||||
key: append([]string{k}, c.key...),
|
||||
value: c.value,
|
||||
})
|
||||
}
|
||||
}
|
||||
case []interface{}:
|
||||
for i, child := range n {
|
||||
k := strconv.Itoa(i)
|
||||
for _, c := range flatten(child) {
|
||||
values = append(values, keyValuePair{
|
||||
key: append([]string{k}, c.key...),
|
||||
value: c.value,
|
||||
})
|
||||
}
|
||||
}
|
||||
default:
|
||||
values = append(values, keyValuePair{value: n})
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue