A simple, fluent Go client library for interacting with network devices using the NETCONF protocol (RFC 6241).
- Simple API: Fluent, chainable API design
- Lazy Connection: Automatic connection establishment on first operation
- XML Manipulation: Path-based XML operations using xmldot (like gjson/sjson for JSON)
- Complete NETCONF Support: All standard operations (Get, GetConfig, EditConfig, Lock, Commit, etc.)
- Robust Transport: Built on scrapligo for reliable SSH connectivity and NETCONF protocol handling
- Automatic Retry: Built-in retry logic with exponential backoff for transient errors
- Thread-Safe: Concurrent read operations with synchronized write operations
- Capability Discovery: Automatic capability negotiation and checking
- Transaction Support: Full candidate datastore workflow support
- Structured Logging: Configurable logging with automatic sensitive data redaction
go get github.com/netascode/go-netconf- Go 1.24 or later
- Network device with NETCONF support
package main
import (
"context"
"fmt"
"log"
"github.com/netascode/go-netconf"
)
func main() {
// Create client (connection established lazily on first operation)
client, err := netconf.NewClient(
"192.168.1.1",
netconf.Username("admin"),
netconf.Password("secret"),
netconf.Port(830),
)
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Connection opens automatically on first operation
ctx := context.Background()
filter := netconf.SubtreeFilter("<interfaces/>")
res, err := client.GetConfig(ctx, "running", filter)
if err != nil {
log.Fatal(err)
}
// Parse response using xmldot
ifName := res.Res.Get("data.interfaces.interface.name").String()
fmt.Println("Interface:", ifName)
}The client uses lazy connection - the connection is established automatically on the first operation:
// Create client without connecting
client, err := netconf.NewClient(
"192.168.1.1",
netconf.Username("admin"),
netconf.Password("secret"),
netconf.Port(830),
netconf.MaxRetries(5),
netconf.AttemptTimeout(60*time.Second),
netconf.TotalTimeout(5*time.Minute),
)
// Connection established automatically on first operation
res, err := client.GetConfig(ctx, "running", filter)
// Or open connection explicitly if needed
if err := client.Open(); err != nil {
log.Fatal(err)
}ctx := context.Background()
// Get with filter
filter := netconf.SubtreeFilter("<interfaces/>")
res, err := client.GetConfig(ctx, "running", filter)
// Get with XPath filter
filter := netconf.XPathFilter("/interfaces/interface[name='GigabitEthernet1']")
res, err := client.Get(ctx, filter)
// Get without filter (all data)
res, err := client.GetConfig(ctx, "running", netconf.NoFilter())ctx := context.Background()
// Build configuration using Body builder
config := netconf.Body{}.
Set("interfaces.interface.name", "GigabitEthernet1").
Set("interfaces.interface.description", "WAN Interface").
Set("interfaces.interface.enabled", true).String()
// Apply configuration
res, err := client.EditConfig(ctx, "candidate", config,
netconf.DefaultOperation("merge"),
netconf.TestOption("test-then-set"),
)ctx := context.Background()
// Lock candidate datastore
if _, err := client.Lock(ctx, "candidate"); err != nil {
log.Fatal(err)
}
defer client.Unlock(ctx, "candidate")
// Edit configuration
config := netconf.Body{}.
Set("system.hostname", "router1").String()
_, err = client.EditConfig(ctx, "candidate", config)
if err != nil {
client.Discard(ctx)
log.Fatal(err)
}
// Commit changes
if _, err := client.Commit(ctx); err != nil {
log.Fatal(err)
}The library automatically retries transient errors (lock conflicts, resource exhaustion, transport failures) with exponential backoff:
client, err := netconf.NewClient(
"192.168.1.1",
netconf.Username("admin"),
netconf.Password("secret"),
netconf.MaxRetries(10), // Maximum retry attempts
netconf.BackoffMinDelay(1*time.Second), // Minimum 1 second delay
netconf.BackoffMaxDelay(60*time.Second), // Maximum 60 second delay
netconf.BackoffDelayFactor(1.2), // Exponential factor
)The client uses lazy connection - the connection is established automatically on the first operation. For explicit connection control, use Open() to establish the connection immediately, and IsClosed() to check connection state:
// Create client without connecting
client, err := netconf.NewClient(
"192.168.1.1",
netconf.Username("admin"),
netconf.Password("secret"),
)
if err != nil {
log.Fatal(err)
}
// Connection opens automatically on first operation
res, err := client.GetConfig(ctx, "running", filter)
// Close connection
client.Close()
// Reopen connection (Open is idempotent - safe to call even if already open)
if err := client.Open(); err != nil {
log.Fatal(err)
}
res, err = client.EditConfig(ctx, "candidate", config)// Check if server supports candidate datastore
if client.ServerHasCapability("urn:ietf:params:netconf:capability:candidate:1.0") {
ctx := context.Background()
// Use candidate datastore
client.Lock(ctx, "candidate")
// ...
}
// Get all server capabilities
caps := client.ServerCapabilities()
for _, cap := range caps {
fmt.Println(cap)
}ctx := context.Background()
rpc := `
<get-system-info xmlns="http://example.com/system">
<detail>full</detail>
</get-system-info>
`
res, err := client.RPC(ctx, rpc)| Operation | Description |
|---|---|
| Get | Retrieve configuration and state data |
| GetConfig | Retrieve configuration from datastore |
| EditConfig | Modify configuration |
| CopyConfig | Copy configuration between datastores |
| DeleteConfig | Delete configuration datastore |
| Lock | Lock datastore |
| Unlock | Unlock datastore |
| Commit | Commit candidate to running |
| Discard | Discard candidate changes |
| Validate | Validate configuration (requires :validate capability) |
| RPC | Send custom RPC operations |
See the examples directory for library usage examples:
- basic - Client creation, GetConfig, EditConfig, response parsing
- candidate - Lock/Unlock, Validate, Commit/Discard workflows
- concurrent - Thread-safe concurrent operations
- custom-rpc - Custom RPC operations with RPC() method
- filters - SubtreeFilter, XPathFilter, NoFilter APIs
- logging - Logger configuration and log levels
# Run tests
make test
# Run tests with coverage
make coverage
# Run linters
make lint
# Run all checks
make verifyContributions are welcome! Please feel free to submit issues or pull requests.
This library is built on top of scrapligo for the NETCONF transport layer. Scrapligo provides robust SSH connectivity and NETCONF protocol handling, allowing go-netconf to focus on providing a simple, idiomatic Go API.
Mozilla Public License Version 2.0 - see LICENSE for details.