Documentation
¶
Overview ¶
Package gnmi provides a simple, fluent API for interacting with network devices using the gNMI protocol (gRPC Network Management Interface).
The library provides a high-level client interface that handles connection management, JSON manipulation, error handling with automatic retry logic, and thread-safe operations.
Connection Behavior ¶
The client uses lazy connection pattern - NewClient() validates configuration but does not establish a physical connection. The connection is established automatically on the first RPC call.
Use Ping() to explicitly verify connectivity before operations if needed.
Quick Start ¶
Create a client and perform basic operations:
client, err := gnmi.NewClient(
"192.168.1.1:57400",
gnmi.Username("admin"),
gnmi.Password("secret"),
gnmi.TLS(true),
)
if err != nil {
log.Fatal(err) // Configuration error
}
defer client.Close()
// Optional: verify connection explicitly
if err := client.Ping(ctx); err != nil {
log.Fatal(err) // Connection error
}
// Get operation with paths (auto-connects if needed)
ctx := context.Background()
paths := []string{"/interfaces/interface[name=GigabitEthernet0/0/0/0]/state"}
res, err := client.Get(ctx, paths)
if err != nil {
log.Fatal(err)
}
// Parse response using gjson
value := res.GetValue("notification.0.update.0.val").String()
fmt.Println("Value:", value)
JSON Manipulation ¶
Use the Body builder for constructing JSON payloads:
body := gnmi.Body{}.
Set("config.name", "GigabitEthernet0/0/0/0").
Set("config.description", "WAN Interface").
Set("config.enabled", true).
Set("config.mtu", 9000)
value, err := body.String()
if err != nil {
log.Fatal(err)
}
ops := []gnmi.SetOperation{
gnmi.Update("/interfaces/interface[name=Gi0/0/0/0]/config", value),
}
res, err = client.Set(ctx, ops)
Error Handling ¶
The library automatically retries transient errors with exponential backoff:
client, err := gnmi.NewClient(
"192.168.1.1:57400",
gnmi.Username("admin"),
gnmi.Password("secret"),
gnmi.MaxRetries(5),
gnmi.BackoffMinDelay(1*time.Second),
gnmi.BackoffMaxDelay(60*time.Second),
)
Thread Safety ¶
Read operations (Get, Capabilities) are thread-safe and can be called concurrently. Write operations (Set) are synchronized with a mutex.
Supported Operations ¶
- Get: Retrieve configuration and state data
- Set: Update, replace, or delete configuration
- Capabilities: Discover supported encodings and gNMI version
References ¶
- gNMI Specification: https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md
- gNMI Protocol: https://github.com/openconfig/gnmi/blob/master/proto/gnmi/gnmi.proto
- gjson: https://github.com/tidwall/gjson
- sjson: https://github.com/tidwall/sjson
- gnmic: https://github.com/openconfig/gnmic
Index ¶
- Constants
- Variables
- func BackoffDelayFactor(factor float64) func(*Client)
- func BackoffMaxDelay(duration time.Duration) func(*Client)
- func BackoffMinDelay(duration time.Duration) func(*Client)
- func ConnectTimeout(duration time.Duration) func(*Client)
- func GetEncoding(encoding string) func(*Req)
- func MaxRetries(retries int) func(*Client)
- func OperationTimeout(duration time.Duration) func(*Client)
- func Password(password string) func(*Client)
- func Port(port int) func(*Client)
- func SetEncoding(encoding string) func(*SetOperation)
- func TLS(enabled bool) func(*Client)
- func TLSCA(caPath string) func(*Client)
- func TLSCert(certPath string) func(*Client)
- func TLSKey(keyPath string) func(*Client)
- func Timeout(duration time.Duration) func(*Req)
- func Username(username string) func(*Client)
- func ValidateEncoding(enc string) error
- func VerifyCertificate(verify bool) func(*Client)
- func WithLogger(logger Logger) func(*Client)
- func WithPrettyPrintLogs(enabled bool) func(*Client)
- type Body
- type CapabilitiesRes
- type Client
- func (c *Client) Backoff(attempt int) time.Duration
- func (c *Client) Capabilities(ctx context.Context) (CapabilitiesRes, error)
- func (c *Client) Close() error
- func (c *Client) Disconnect() error
- func (c *Client) Get(ctx context.Context, paths []string, mods ...func(*Req)) (GetRes, error)
- func (c *Client) HasCapability(capability string) bool
- func (c *Client) HasCredentials() bool
- func (c *Client) Ping(ctx context.Context) error
- func (c *Client) ServerCapabilities() []string
- func (c *Client) Set(ctx context.Context, ops []SetOperation, mods ...func(*Req)) (SetRes, error)
- type DefaultLogger
- func (l *DefaultLogger) Debug(ctx context.Context, msg string, keysAndValues ...any)
- func (l *DefaultLogger) Error(ctx context.Context, msg string, keysAndValues ...any)
- func (l *DefaultLogger) Info(ctx context.Context, msg string, keysAndValues ...any)
- func (l *DefaultLogger) Warn(ctx context.Context, msg string, keysAndValues ...any)
- type ErrorModel
- type GetRes
- type GnmiError
- type LogLevel
- type Logger
- type NoOpLogger
- type Req
- type SetOperation
- type SetOperationType
- type SetRes
- type TransientError
Examples ¶
Constants ¶
const ( DefaultPort = 57400 DefaultMaxRetries = 3 DefaultBackoffMinDelay = 1 * time.Second DefaultBackoffMaxDelay = 60 * time.Second DefaultBackoffDelayFactor = 2 DefaultConnectTimeout = 30 * time.Second DefaultOperationTimeout = 15 * time.Second // Matches embedded client behavior DefaultUseTLS = true DefaultVerifyCertificate = true DefaultPrettyPrintLogs = true )
Default client configuration values
const ( MaxJSONSizeForLogging = 1 * 1024 * 1024 // 1MB limit to prevent ReDoS attacks MaxSensitiveFields = 1000 // Max redaction operations to prevent DoS )
Security limits for JSON processing and logging
const ( JSONTooLargeMessage = "[JSON TOO LARGE FOR LOGGING]" JSONTooManySensitiveMsg = "[JSON CONTAINS TOO MANY SENSITIVE FIELDS]" )
Logging message constants
const ( // EncodingJSON uses standard JSON encoding EncodingJSON = "json" // EncodingJSONIETF uses JSON encoding with IETF conventions (default) // This is the recommended encoding for most use cases EncodingJSONIETF = "json_ietf" // EncodingProto uses Protocol Buffer encoding EncodingProto = "proto" // EncodingASCII uses ASCII encoding EncodingASCII = "ascii" // EncodingBytes uses raw byte encoding EncodingBytes = "bytes" )
Encoding constants for gNMI operations
const ( // MaxValueSize is the maximum size for a single value in bytes (10MB) MaxValueSize = 10 * 1024 * 1024 // MaxPathLength is the maximum length for a gNMI path (1024 characters) MaxPathLength = 1024 )
Input validation constants
const MaxLogValueLength = 1024
MaxLogValueLength limits the length of log values to prevent log injection and excessive log file growth. Values longer than this are truncated.
Variables ¶
var TransientErrors = []TransientError{ {Code: uint32(codes.Unavailable)}, {Code: uint32(codes.ResourceExhausted)}, {Code: uint32(codes.DeadlineExceeded)}, {Code: uint32(codes.Aborted)}, }
TransientErrors defines the list of gRPC status codes that should trigger automatic retry
These errors are typically caused by temporary conditions such as:
- Service unavailable (server temporarily down, overloaded)
- Resource exhausted (rate limiting, quota exceeded)
- Deadline exceeded (timeout, slow network)
- Aborted (transaction aborted, try again)
NOTE: codes.Internal is intentionally excluded from this list. While some Internal errors may be transient, codes.Internal is a catch-all error code that includes many permanent failures (bugs, invalid state, etc.). Blindly retrying Internal errors can mask real problems and waste resources. If specific Internal errors are known to be transient, they should be detected and handled explicitly rather than retrying all Internal errors.
Based on gRPC status codes from google.golang.org/grpc/codes
var ValidEncodings = []string{ EncodingJSON, EncodingJSONIETF, EncodingProto, EncodingASCII, EncodingBytes, }
ValidEncodings contains the list of valid encoding values
Functions ¶
func BackoffDelayFactor ¶
BackoffDelayFactor sets the backoff multiplication factor (default: 2.0)
func BackoffMaxDelay ¶
BackoffMaxDelay sets the maximum backoff delay (default: 60s)
func BackoffMinDelay ¶
BackoffMinDelay sets the minimum backoff delay (default: 1s)
func ConnectTimeout ¶
ConnectTimeout sets the connection timeout (default: 30s)
func GetEncoding ¶
GetEncoding returns a request modifier that sets the encoding for Get operations.
Valid encodings: json, json_ietf (default), proto, ascii, bytes
This encoding overrides the default encoding (json_ietf) for this specific request. Note that the device must support the specified encoding.
Common encodings:
- json_ietf: JSON with IETF conventions (recommended, default)
- json: Standard JSON encoding
- proto: Protocol Buffer encoding (binary)
- ascii: ASCII text encoding
- bytes: Raw byte encoding
The modifier validates the encoding at request time. If an invalid encoding is provided, the operation will fail with an error.
Example:
// Get with Protocol Buffer encoding
res, err := client.Get(ctx, []string{"/system/config"},
gnmi.GetEncoding("proto"))
// Get with standard JSON
res, err := client.Get(ctx, []string{"/interfaces"},
gnmi.GetEncoding("json"))
// Combined with timeout
res, err := client.Get(ctx, []string{"/interfaces"},
gnmi.Timeout(30*time.Second),
gnmi.GetEncoding("json_ietf"))
func MaxRetries ¶
MaxRetries sets the maximum number of retry attempts for transient errors (default: 3)
func OperationTimeout ¶
OperationTimeout sets the operation timeout (default: 15s)
func SetEncoding ¶
func SetEncoding(encoding string) func(*SetOperation)
SetEncoding returns a modifier that sets the encoding for individual Set operations.
Valid encodings: json, json_ietf (default), proto, ascii, bytes
This modifier is used with Update() and Replace() operations to specify the encoding for individual operations within a Set request. Each operation can have a different encoding.
Common encodings:
- json_ietf: JSON with IETF conventions (recommended, default)
- json: Standard JSON encoding
- proto: Protocol Buffer encoding (binary)
- ascii: ASCII text encoding
- bytes: Raw byte encoding
Example:
// Set with Protocol Buffer encoding
ops := []gnmi.SetOperation{
gnmi.Update("/interfaces/interface[name=Gi0]/config", protoBytes,
gnmi.SetEncoding("proto")),
}
// Set with standard JSON (non-IETF)
ops := []gnmi.SetOperation{
gnmi.Replace("/system/config", jsonData,
gnmi.SetEncoding("json")),
}
// Default encoding (json_ietf) - no encoding parameter needed
ops := []gnmi.SetOperation{
gnmi.Update("/system/hostname", `{"hostname": "router1"}`),
}
func TLS ¶
TLS enables or disables TLS (default: true)
WARNING: Disabling TLS makes the connection vulnerable to eavesdropping and Man-in-the-Middle attacks. Only use this in isolated testing environments where security is not a concern.
func TLSCA ¶
TLSCA sets the TLS CA certificate file path for server verification
The CA certificate will be loaded and validated when the connection is established. If the CA file cannot be read, an error will be returned during connection.
func TLSCert ¶
TLSCert sets the TLS certificate file path for authentication
The certificate will be loaded and validated when the connection is established. If the certificate file cannot be read, an error will be returned during connection.
func TLSKey ¶
TLSKey sets the TLS private key file path for authentication
The key will be loaded and validated when the connection is established. If the key file cannot be read, an error will be returned during connection.
func Timeout ¶
Timeout returns a request modifier that sets a custom timeout for the operation.
This timeout takes precedence over the context deadline and client's OperationTimeout. Use this to set operation-specific timeouts that differ from the client's default.
The timeout priority model is:
- Request-specific timeout (this modifier) - highest priority
- Context deadline (if already set) - medium priority
- Client.OperationTimeout - fallback default
Example:
// Get with 30 second timeout
res, err := client.Get(ctx, []string{"/interfaces"},
gnmi.Timeout(30*time.Second))
// Set with 2 minute timeout for long-running operation
res, err := client.Set(ctx, ops,
gnmi.Timeout(2*time.Minute))
func ValidateEncoding ¶
ValidateEncoding checks if the encoding is valid
Returns an error if the encoding is not one of the supported values.
Example:
if err := gnmi.ValidateEncoding("json_ietf"); err != nil {
log.Fatal(err)
}
func VerifyCertificate ¶
VerifyCertificate enables or disables TLS certificate verification (default: true)
WARNING: Disabling certificate verification makes the connection vulnerable to Man-in-the-Middle attacks. Only use this in testing environments where security is not a concern.
Example:
client, _ := gnmi.NewClient("192.168.1.1:57400",
gnmi.Username("admin"),
gnmi.Password("secret"),
gnmi.VerifyCertificate(false)) // Insecure, use only for testing
func WithLogger ¶
WithLogger configures a custom logger for the client
By default, the client uses NoOpLogger which discards all log messages. Use this option to enable logging with DefaultLogger or a custom logger.
All JSON content logged at Debug level is automatically redacted to remove sensitive data (passwords, secrets, keys, tokens).
Example (DefaultLogger):
logger := gnmi.NewDefaultLogger(gnmi.LogLevelInfo)
client, _ := gnmi.NewClient("192.168.1.1:57400",
gnmi.Username("admin"),
gnmi.Password("secret"),
gnmi.WithLogger(logger))
Example (Custom Logger):
type SlogAdapter struct {
logger *slog.Logger
}
func (s *SlogAdapter) Debug(ctx context.Context, msg string, keysAndValues ...interface{}) {
s.logger.DebugContext(ctx, msg, keysAndValues...)
}
// ... implement Info, Warn, Error (all with ctx context.Context as first parameter)
client, _ := gnmi.NewClient("192.168.1.1:57400",
gnmi.WithLogger(&SlogAdapter{logger: slog.Default()}))
func WithPrettyPrintLogs ¶
WithPrettyPrintLogs enables/disables JSON pretty printing in logs
When enabled, JSON content in debug logs is formatted for better readability. When disabled (default), raw JSON is logged without formatting.
This only affects Debug-level log output. Disabling pretty printing can improve performance when high-frequency operations are logged.
Default: disabled (false)
Example:
logger := gnmi.NewDefaultLogger(gnmi.LogLevelDebug)
client, _ := gnmi.NewClient("192.168.1.1:57400",
gnmi.Username("admin"),
gnmi.Password("secret"),
gnmi.WithLogger(logger),
gnmi.WithPrettyPrintLogs(true)) // Enable formatting for readability
Types ¶
type Body ¶
type Body struct {
// contains filtered or unexported fields
}
Body provides a fluent interface for building JSON configurations using sjson for path-based manipulation.
The Body builder tracks errors internally to enable method chaining while providing error checking through String() or Err() methods.
Example:
body := gnmi.Body{}.
Set("config.name", "GigabitEthernet0/0/0/0").
Set("config.description", "WAN Interface").
Set("config.enabled", true).
Set("config.mtu", 9000)
value, err := body.String()
if err != nil {
log.Fatal(err)
}
ops := []gnmi.SetOperation{
gnmi.Update("/interfaces/interface[name=Gi0/0/0/0]/config", value),
}
func (Body) Bytes ¶
Bytes returns the JSON byte slice representation and any error encountered during building
This is useful when you need []byte instead of string for efficiency.
Example:
body := gnmi.Body{}.Set("name", "eth0")
jsonBytes, err := body.Bytes()
if err != nil {
log.Fatal(err)
}
func (Body) Delete ¶
Delete removes a value at the specified JSON path and returns a new Body
The path uses dot notation for nested fields (e.g., "config.description").
If an error occurs, the error is stored and returned by String() or Err().
Example:
body := gnmi.Body{}.
Set("name", "eth0").
Set("description", "temp").
Delete("description") // Remove description field
json, err := body.String()
Returns the Body for method chaining.
func (Body) Err ¶
Err returns any error that occurred during the building process
This method allows checking for errors without retrieving the string value.
Example:
body := gnmi.Body{}.Set("config.hostname", "router1")
if err := body.Err(); err != nil {
log.Fatal(err)
}
func (Body) Res ¶
Res returns the JSON string for further processing with gjson
This allows you to query the built JSON using gjson's Get function. If an error occurred during building, this returns an empty string. Use Err() or String() to check for errors.
Example:
body := gnmi.Body{}.Set("config.hostname", "router1")
if body.Err() == nil {
json := body.Res()
hostname := gjson.Get(json, "config.hostname").String()
}
Returns the JSON string that can be queried with gjson.Get.
func (Body) Set ¶
Set sets a value at the specified JSON path and returns a new Body
The path uses dot notation for nested fields (e.g., "config.name"). The value can be any type that sjson supports (string, number, bool, etc.).
If an error occurs, the error is stored and returned by String() or Err(). Once an error occurs, all subsequent operations are no-ops that preserve the error.
Example:
body := gnmi.Body{}.
Set("config.name", "eth0").
Set("config.enabled", true).
Set("config.mtu", 1500)
json, err := body.String()
Returns the Body for method chaining.
func (Body) String ¶
String returns the JSON string representation and any error encountered during building
This method returns both the JSON string and any error that occurred during the building process. If an error occurred during any Set/Delete operation, the error will be returned here.
Example:
body := gnmi.Body{}.Set("config.hostname", "router1")
json, err := body.String()
if err != nil {
log.Fatal(err)
}
type CapabilitiesRes ¶
type CapabilitiesRes struct {
// Version is the gNMI service version
Version string
// Capabilities lists supported capabilities
Capabilities []string
// Models contains supported data models
Models []*gnmi.ModelData
// OK indicates if the operation succeeded
OK bool
// Errors contains any error information
Errors []ErrorModel
}
CapabilitiesRes represents a gNMI Capabilities response
type Client ¶
type Client struct {
// Connection parameters
Target string
Port int
// TLS options
UseTLS bool
VerifyCertificate bool
InsecureSkipVerify bool // Alias for !VerifyCertificate
// Timeout configuration
ConnectTimeout time.Duration
OperationTimeout time.Duration
// Retry configuration
MaxRetries int
BackoffMinDelay time.Duration
BackoffMaxDelay time.Duration
BackoffDelayFactor float64
// contains filtered or unexported fields
}
Client represents a gNMI client connection to a network device
func NewClient ¶
NewClient creates a new gNMI client with the specified target and options
The client creates a gnmic target configuration but does NOT establish a physical connection immediately. The connection is established automatically on the first RPC call (lazy connection). Use Ping() to explicitly verify connectivity if needed.
Example:
client, err := gnmi.NewClient(
"192.168.1.1:57400",
gnmi.Username("admin"),
gnmi.Password("secret"),
gnmi.TLS(true),
gnmi.VerifyCertificate(false),
gnmi.MaxRetries(5),
)
if err != nil {
log.Fatal(err) // Configuration error
}
defer client.Close()
// Optional: verify connection explicitly
if err := client.Ping(ctx); err != nil {
log.Fatal(err) // Connection error
}
// Auto-connect on first use
res, err := client.Get(ctx, paths)
Returns a configured Client or an error if configuration validation fails.
func (*Client) Backoff ¶
Backoff calculates the backoff delay for retry attempt using exponential backoff with jitter
The formula is: delay = min(minDelay * (factor ^ attempt) + jitter, maxDelay) where jitter is a cryptographically secure random value in [0, delay * 0.1].
Security Note: Uses crypto/rand for jitter to prevent timing attack predictability. If crypto/rand fails, falls back to timestamp-based jitter to prevent thundering herd. Timestamp-based jitter is not cryptographically secure but provides sufficient randomness for retry dispersal.
Parameters:
- attempt: The retry attempt number (0-indexed)
Returns the duration to wait before retrying.
func (*Client) Capabilities ¶
func (c *Client) Capabilities(ctx context.Context) (CapabilitiesRes, error)
Capabilities retrieves the gNMI server capabilities
This operation performs a gNMI Capabilities RPC to discover:
- gNMI service version
- Supported encodings (json, json_ietf, proto, etc.)
- Supported data models
The capabilities are stored in the client for later reference. Use HasCapability() to check for specific capabilities.
Example:
ctx := context.Background()
res, err := client.Capabilities(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("gNMI version: %s\n", res.Version)
for _, cap := range res.Capabilities {
fmt.Printf("Encoding: %s\n", cap)
}
func (*Client) Close ¶
Close closes the gNMI session and cleans up resources (terminal operation).
This method is TERMINAL - the client cannot be reused after calling Close(). All resources are released and the target configuration is destroyed.
Use Disconnect() instead if you want to temporarily close the connection while preserving the ability to reconnect later.
This method is typically used in defer statements to ensure proper cleanup:
Example:
client, err := gnmi.NewClient("192.168.1.1:57400", opts...)
if err != nil {
log.Fatal(err)
}
defer client.Close() // Ensure cleanup on function exit
// Use client...
res, err := client.Get(ctx, paths)
Thread-safe: safe to call multiple times (subsequent calls are no-ops).
func (*Client) Disconnect ¶
Disconnect closes the gRPC connection but preserves client configuration.
Unlike Close(), this method allows the client to be reused - subsequent operations will automatically reconnect. This is useful for:
- Connection pooling with idle timeout disconnection
- Long-running applications that need to release connections temporarily
- Testing reconnection logic
The client maintains its target configuration and will reconnect on the next RPC call via the ensureConnected() mechanism.
Example - Disconnect and reuse pattern:
client, _ := gnmi.NewClient("192.168.1.1:57400", opts...)
defer client.Close()
// Use client
_, err := client.Get(ctx, paths)
// Release connection temporarily
client.Disconnect()
// Automatically reconnects on next use
_, err = client.Get(ctx, paths)
Example - Connection pooling with idle timeout:
// Disconnect idle connections periodically
ticker := time.NewTicker(5 * time.Minute)
go func() {
for range ticker.C {
client.Disconnect() // Release idle connection
}
}()
Example - Long-running application pattern:
// Disconnect during maintenance window client.Disconnect() performMaintenance() // Reconnects automatically when needed client.Get(ctx, paths)
Thread-safe: safe for concurrent use with other client methods.
func (*Client) Get ¶
Get performs a gNMI Get operation to retrieve data from the device
Get supports querying multiple paths in a single request. The paths parameter must be a non-empty slice of gNMI path strings. The encoding can be specified via request modifiers, defaulting to json_ietf.
The operation uses RLock for concurrent read access, allowing multiple Get operations to run in parallel. Context timeout follows priority:
- Request-specific timeout (via Timeout modifier)
- Context deadline (if already set)
- Client.OperationTimeout (fallback default)
Example:
ctx := context.Background()
paths := []string{
"/interfaces/interface[name=GigabitEthernet0/0/0/0]/state",
"/system/config/hostname",
}
res, err := client.Get(ctx, paths)
if err != nil {
log.Fatal(err)
}
for _, notif := range res.Notifications {
fmt.Printf("Path: %s\n", notif.Prefix)
}
Returns GetRes with notifications, timestamp, OK status, and any errors.
func (*Client) HasCapability ¶
HasCapability checks if the server supports a specific capability
Example:
if client.HasCapability("gnmi-1.0") {
// Use gNMI 1.0 features
}
func (*Client) HasCredentials ¶
HasCredentials returns true if credentials are configured
This method only indicates if credentials exist without exposing the actual values.
Example:
if client.HasCredentials() {
fmt.Println("Client is configured with credentials")
}
func (*Client) Ping ¶
Ping verifies connectivity by performing a Capabilities RPC
This method establishes a connection if not already connected and performs a gNMI Capabilities request to verify the server is reachable and responsive. This is useful when you want to explicitly verify connectivity before performing other operations.
Example:
client, err := gnmi.NewClient("192.168.1.1:57400", opts...)
if err != nil {
log.Fatal(err) // Configuration error
}
defer client.Close()
// Verify connection before proceeding
if err := client.Ping(ctx); err != nil {
log.Fatal(err) // Connection error
}
// Now confident connection works
res, err := client.Get(ctx, paths)
func (*Client) ServerCapabilities ¶
ServerCapabilities returns the list of capabilities supported by the server
Returns a copy of the capabilities slice to prevent external modification.
Example:
caps := client.ServerCapabilities()
for _, cap := range caps {
fmt.Println(cap)
}
func (*Client) Set ¶
Set performs a gNMI Set operation to configure the device
Set supports multiple update, replace, and delete operations in a single request. The ops parameter must be a non-empty slice of SetOperation structs created via the Update(), Replace(), or Delete() helper functions.
The operation uses Lock for exclusive write access, serializing all Set operations to prevent concurrent writes. Context timeout follows priority:
- Request-specific timeout (via Timeout modifier)
- Context deadline (if already set)
- Client.OperationTimeout (fallback default)
Example:
ctx := context.Background()
ops := []gnmi.SetOperation{
gnmi.Update("/interfaces/interface[name=Gi0/0/0/0]/config/description",
`{"description": "WAN Interface"}`),
gnmi.Replace("/interfaces/interface[name=Gi0/0/0/0]/config/enabled",
`{"enabled": true}`),
gnmi.Delete("/interfaces/interface[name=Gi0/0/0/1]/config"),
}
res, err := client.Set(ctx, ops)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Set operation successful: %v\n", res.OK)
Returns SetRes with response, timestamp, OK status, and any errors.
type DefaultLogger ¶
type DefaultLogger struct {
// contains filtered or unexported fields
}
DefaultLogger wraps Go's standard log package with configurable log level
Log output format: [LEVEL] message key1=value1 key2=value2
Context Parameter Usage:
DefaultLogger does NOT use the context parameter. It is provided to satisfy the Logger interface and enable integration with context-aware logging frameworks.
Custom logger implementations SHOULD use the context to extract trace correlation data such as:
- Request ID / Trace ID for distributed tracing
- User ID or tenant ID for multi-tenant applications
- Deadline information for timeout debugging
Example of context-aware logging in a custom logger:
func (s *SlogAdapter) Debug(ctx context.Context, msg string, keysAndValues ...any) {
// Extract trace ID from context
if traceID := ctx.Value("trace_id"); traceID != nil {
keysAndValues = append(keysAndValues, "trace_id", traceID)
}
s.logger.DebugContext(ctx, msg, keysAndValues...)
}
Example:
logger := gnmi.NewDefaultLogger(gnmi.LogLevelDebug)
client, _ := gnmi.NewClient("192.168.1.1:57400",
gnmi.Username("admin"),
gnmi.Password("secret"),
gnmi.WithLogger(logger))
func NewDefaultLogger ¶
func NewDefaultLogger(level LogLevel) *DefaultLogger
NewDefaultLogger creates a DefaultLogger with the specified log level
func (*DefaultLogger) Debug ¶
func (l *DefaultLogger) Debug(ctx context.Context, msg string, keysAndValues ...any)
Debug logs a debug message with structured key-value pairs
func (*DefaultLogger) Error ¶
func (l *DefaultLogger) Error(ctx context.Context, msg string, keysAndValues ...any)
Error logs an error message with structured key-value pairs
type ErrorModel ¶
type ErrorModel struct {
// Code is the gRPC status code
Code uint32
// Message is the error message
Message string
// Details contains additional error information
Details string
}
ErrorModel represents a gNMI error with gRPC status code
type GetRes ¶
type GetRes struct {
// Notifications contains the gNMI notification messages
Notifications []*gnmi.Notification
// Timestamp is the response timestamp (nanoseconds since Unix epoch)
Timestamp int64
// OK indicates if the operation succeeded
OK bool
// Errors contains any error information
Errors []ErrorModel
}
GetRes represents a gNMI Get response
func (GetRes) GetValue ¶
GetValue retrieves a value from the response notifications using a gjson path. The path follows gjson syntax for querying JSON structures.
Example paths:
- "notification.0.timestamp" - Get notification timestamp
- "notification.0.update.0.path.elem.0.name" - Get path element name
- "notification.0.update.0.val.Value.JsonIetfVal" - Get JSON IETF value (base64 encoded)
Note: The JSON structure uses protobuf JSON marshaling conventions where field names are lowercase and TypedValue.Value is nested with capitalized names.
Returns gjson.Result which can be converted to specific types:
- result.String() for string values
- result.Int() for integer values
- result.Bool() for boolean values
- result.Array() for array values
Example:
res, err := client.Get(ctx, []string{"/interfaces/interface[name=Gi0/0/0/0]/state"})
if err != nil {
log.Fatal(err)
}
timestamp := res.GetValue("notification.0.timestamp").Int()
pathName := res.GetValue("notification.0.update.0.path.elem.0.name").String()
Example ¶
Example test demonstrating the usage pattern
// This example shows how to use GetValue with gjson paths
notification := &gnmi.Notification{
Timestamp: 1234567890,
Update: []*gnmi.Update{
{
Path: &gnmi.Path{
Elem: []*gnmi.PathElem{
{Name: "system"},
{Name: "config"},
},
},
Val: &gnmi.TypedValue{
Value: &gnmi.TypedValue_JsonIetfVal{
JsonIetfVal: []byte(`{"hostname": "router1"}`),
},
},
},
},
}
res := GetRes{
Notifications: []*gnmi.Notification{notification},
Timestamp: 1234567890,
OK: true,
}
// Get the JSON value
jsonValue := res.GetValue("notification.0.update.0.val.json_ietf_val").String()
_ = jsonValue // Use the value
func (GetRes) JSON ¶
JSON returns the response notifications as a formatted JSON string. This is useful for debugging, logging, or custom parsing. Returns an empty string if marshaling fails.
Example:
res, err := client.Get(ctx, []string{"/system/config"})
if err != nil {
log.Fatal(err)
}
fmt.Println(res.JSON()) // Print full response as JSON
type GnmiError ¶
type GnmiError struct {
// Operation name that failed
Operation string
// Errors from gNMI error details
Errors []ErrorModel
// Human-readable error message
Message string
// InternalMsg contains detailed error information for internal logging
InternalMsg string
// Number of retry attempts made
Retries int
// IsTransient indicates if the error is transient and was retried
IsTransient bool
}
GnmiError represents a structured gNMI error with operation context
func (*GnmiError) DetailedError ¶
DetailedError returns the full error message including internal details
This should only be used in secure logging contexts where sensitive information disclosure is acceptable (e.g., server-side logs, debug output).
Example:
if err != nil {
if gnmiErr, ok := err.(*GnmiError); ok {
log.Debug(gnmiErr.DetailedError()) // internal logging
return gnmiErr.Error() // client-facing error
}
}
type LogLevel ¶
type LogLevel int
LogLevel represents the severity threshold for logging
const ( // LogLevelDebug enables all log levels (most verbose) LogLevelDebug LogLevel = iota // LogLevelInfo enables Info, Warn, and Error logs LogLevelInfo // LogLevelWarn enables Warn and Error logs LogLevelWarn // LogLevelError enables only Error logs LogLevelError // LogLevelNone disables all logging LogLevelNone )
type Logger ¶
type Logger interface {
Debug(ctx context.Context, msg string, keysAndValues ...any)
Info(ctx context.Context, msg string, keysAndValues ...any)
Warn(ctx context.Context, msg string, keysAndValues ...any)
Error(ctx context.Context, msg string, keysAndValues ...any)
}
Logger interface for pluggable logging support
All methods receive a context.Context as the first parameter, enabling integration with context-based logging frameworks and distributed tracing.
Implementations should use structured logging with key-value pairs. The go-gnmi library provides two implementations:
- DefaultLogger: Wraps Go's standard log package with configurable log level
- NoOpLogger: Zero-overhead logging when disabled (default)
Context Usage Guidelines:
The context parameter enables trace correlation and debugging:
- Use context.Background() for utility methods and internal operations
- Propagate the user's context from API calls (Get, Set, Subscribe, etc.)
- Extract trace IDs, request IDs, or tenant IDs for correlation
- Check context deadline to log timeout information
When to use context.Background():
- Internal utility methods (validation, formatting, sanitization)
- Constructor methods (NewClient)
- Configuration processing
- Static operations that don't involve user requests
When to propagate user context:
- gNMI operations (Get, Set, Subscribe, Capabilities)
- Network operations (Connect, retry logic)
- Request/response processing
- Any operation initiated by a user API call
Example custom logger integration:
type SlogAdapter struct {
logger *slog.Logger
}
func (s *SlogAdapter) Debug(ctx context.Context, msg string, keysAndValues ...any) {
s.logger.DebugContext(ctx, msg, keysAndValues...)
}
// ... implement other methods
client, _ := gnmi.NewClient("192.168.1.1:57400",
gnmi.Username("admin"),
gnmi.Password("secret"),
gnmi.WithLogger(&SlogAdapter{logger: slog.Default()}))
type NoOpLogger ¶
type NoOpLogger struct{}
NoOpLogger is a no-operation logger that discards all log messages
This logger provides zero overhead when logging is disabled. All methods are no-ops and will be optimized away by the compiler.
This is the default logger used by go-gnmi when no custom logger is configured.
Example:
// Logging is disabled by default (uses NoOpLogger)
client, _ := gnmi.NewClient("192.168.1.1:57400",
gnmi.Username("admin"),
gnmi.Password("secret"))
func (*NoOpLogger) Debug ¶
func (n *NoOpLogger) Debug(_ context.Context, _ string, _ ...any)
Debug discards the log message
func (*NoOpLogger) Error ¶
func (n *NoOpLogger) Error(_ context.Context, _ string, _ ...any)
Error discards the log message
type Req ¶
type Req struct {
// Encoding specifies the data encoding
// Valid values: json, json_ietf (default), proto, ascii, bytes
Encoding string
// Timeout is the request-specific timeout
// Overrides client default timeout if set
Timeout time.Duration
}
Req represents a gNMI request modifier
This struct is used to apply request-specific options via functional modifiers. Operation parameters (paths, operations) are passed directly to methods.
Example:
// Get with custom encoding and timeout
res, err := client.Get(ctx, paths,
gnmi.Encoding("proto"),
gnmi.Timeout(30*time.Second))
type SetOperation ¶
type SetOperation struct {
// OperationType specifies the operation type (update, replace, delete)
OperationType SetOperationType
// Path is the gNMI path
Path string
// Value is the JSON value for Update/Replace operations
// Empty for Delete operations
Value string
// Encoding specifies the value encoding
// Valid values: json, json_ietf (default), proto, ascii, bytes
Encoding string
}
SetOperation represents a single gNMI Set operation (Update, Replace, or Delete)
func Delete ¶
func Delete(path string) SetOperation
Delete creates a SetOperation for deleting a path
Delete operations remove configuration at the specified path.
Parameters:
- path: gNMI path string
Example:
op := gnmi.Delete("/interfaces/interface[name=Gi0/0/0/1]/config")
func Replace ¶
func Replace(path, value string, opts ...func(*SetOperation)) SetOperation
Replace creates a SetOperation for replacing a path with a value
Replace operations remove existing configuration at the path before applying the new value. Use Replace when you need to ensure no old config remains.
The encoding defaults to json_ietf. Use the SetEncoding() modifier to specify a different encoding (json, proto, ascii, bytes).
Parameters:
- path: gNMI path string
- value: JSON-encoded value string
- opts: optional modifiers (SetEncoding, etc.)
Example:
// Default encoding (json_ietf)
op := gnmi.Replace("/interfaces/interface[name=Gi0/0/0/0]/config",
`{"mtu": 9000}`)
// Explicit encoding
op := gnmi.Replace("/system/config", jsonData,
gnmi.SetEncoding("json"))
func Update ¶
func Update(path, value string, opts ...func(*SetOperation)) SetOperation
Update creates a SetOperation for updating a path with a value
Update operations modify existing configuration, creating it if it doesn't exist. This is the most common Set operation type.
The encoding defaults to json_ietf. Use the SetEncoding() modifier to specify a different encoding (json, proto, ascii, bytes).
Parameters:
- path: gNMI path string (e.g., "/interfaces/interface[name=Gi0/0/0/0]/config")
- value: JSON-encoded value string
- opts: optional modifiers (SetEncoding, etc.)
Example:
// Default encoding (json_ietf)
op := gnmi.Update("/system/config/hostname", `{"hostname": "router1"}`)
// Explicit encoding
op := gnmi.Update("/interfaces/interface[name=Gi0]/config", protoBytes,
gnmi.SetEncoding("proto"))
type SetOperationType ¶
type SetOperationType string
SetOperationType represents the type of Set operation
const ( // OperationUpdate modifies existing configuration, creating it if it doesn't exist OperationUpdate SetOperationType = "update" // OperationReplace removes existing configuration before applying new value OperationReplace SetOperationType = "replace" // OperationDelete removes configuration at the specified path OperationDelete SetOperationType = "delete" )
type SetRes ¶
type SetRes struct {
// Response is the gNMI SetResponse
Response *gnmi.SetResponse
// Timestamp is the response timestamp (nanoseconds since Unix epoch)
Timestamp int64
// OK indicates if the operation succeeded
OK bool
// Errors contains any error information
Errors []ErrorModel
}
SetRes represents a gNMI Set response
func (SetRes) GetValue ¶
GetValue retrieves a value from the SetResponse using a gjson path. The path follows gjson syntax for querying JSON structures.
Example paths:
- "response.0.op" - Get operation type (UPDATE, REPLACE, DELETE)
- "response.0.path" - Get path string
- "timestamp" - Get response timestamp
Returns gjson.Result which can be converted to specific types:
- result.String() for string values
- result.Int() for integer values
Example:
ops := []gnmi.SetOperation{
gnmi.Update("/interfaces/interface[name=Gi0/0/0/0]/config/mtu", `{"mtu": 9000}`),
}
res, err := client.Set(ctx, ops)
if err != nil {
log.Fatal(err)
}
op := res.GetValue("response.0.op").String()
func (SetRes) JSON ¶
JSON returns the SetResponse as a formatted JSON string. This is useful for debugging, logging, or custom parsing. Returns an empty string if marshaling fails.
Example:
ops := []gnmi.SetOperation{
gnmi.Update("/system/config/hostname", `{"hostname": "router1"}`),
}
res, err := client.Set(ctx, ops)
if err != nil {
log.Fatal(err)
}
fmt.Println(res.JSON()) // Print full response as JSON
type TransientError ¶
type TransientError struct {
// Code is the gRPC status code to match
Code uint32
}
TransientError defines patterns for detecting transient errors that should be retried
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
basic
command
Package main demonstrates basic go-gnmi API usage.
|
Package main demonstrates basic go-gnmi API usage. |
|
capabilities
command
Package main demonstrates gNMI capability discovery.
|
Package main demonstrates gNMI capability discovery. |
|
concurrent
command
Package main demonstrates concurrent operations with go-gnmi.
|
Package main demonstrates concurrent operations with go-gnmi. |
|
disconnect
command
Package main demonstrates the use of Disconnect() for connection pooling and temporary connection release patterns in go-gnmi.
|
Package main demonstrates the use of Disconnect() for connection pooling and temporary connection release patterns in go-gnmi. |
|
logging
command
Package main demonstrates logging configuration in go-gnmi.
|
Package main demonstrates logging configuration in go-gnmi. |