Documentation
¶
Overview ¶
Package client provides the NETCONF client API.
The central type is Client, which wraps a Session and manages a background dispatcher goroutine. The dispatcher reads <rpc-reply> messages from the transport and delivers them to the matching waiting caller by message-id. This design allows multiple goroutines to issue concurrent RPCs over a single NETCONF session without external coordination.
Observability: errors returned by Do include the message-id. When the dispatcher exits due to a transport error all pending callers receive that error immediately. Client.Err() exposes the dispatcher exit error for post-mortem inspection.
Typed methods (Get, GetConfig, EditConfig, …) call Do, then check the reply via checkReply or checkDataReply. Server-side <rpc-error> elements are decoded by ParseRPCErrors and returned as the first RPCError value so callers can use errors.As to extract structured error details.
Error messages returned by methods on a closed Client name the method so callers can tell whether the session was already closing or an operation was in flight when the transport died.
Observability ¶
Typed method errors always include the operation name in the error chain (e.g. "client: GetConfig: rpc-error: type=… tag=…") so log lines identify both the caller-facing method and the server-side error. errors.As(err, &netconf.RPCError{}) succeeds whenever a server replied with <rpc-error> — callers can extract Tag, Message, Severity etc. checkDataReply surfaces XML decode failures as wrapped errors with the text "decode DataReply" — distinguishable from RPC-level errors.
Index ¶
- type Client
- func AcceptCallHomeSSH(ctx context.Context, ln net.Listener, config *gossh.ClientConfig, ...) (*Client, error)
- func AcceptCallHomeTLS(ctx context.Context, ln net.Listener, config *cryptotls.Config, ...) (*Client, error)
- func Dial(ctx context.Context, addr string, config *gossh.ClientConfig, ...) (*Client, error)
- func DialTLS(ctx context.Context, addr string, config *cryptotls.Config, ...) (*Client, error)
- func NewClient(sess *netconf.Session) *Client
- func (c *Client) CancelCommit(ctx context.Context, persistID string) error
- func (c *Client) Close() error
- func (c *Client) CloseSession(ctx context.Context) error
- func (c *Client) Commit(ctx context.Context, opts *netconf.Commit) error
- func (c *Client) CopyConfig(ctx context.Context, cfg netconf.CopyConfig) error
- func (c *Client) DeleteConfig(ctx context.Context, cfg netconf.DeleteConfig) error
- func (c *Client) DeleteSubscription(ctx context.Context, id subscriptions.SubscriptionID) error
- func (c *Client) DiscardChanges(ctx context.Context) error
- func (c *Client) Do(ctx context.Context, op any) (*netconf.RPCReply, error)
- func (c *Client) DroppedNotifications() uint64
- func (c *Client) EditConfig(ctx context.Context, cfg netconf.EditConfig) error
- func (c *Client) EditData(ctx context.Context, req nmda.EditData) error
- func (c *Client) Err() error
- func (c *Client) EstablishSubscription(ctx context.Context, req subscriptions.EstablishSubscriptionRequest) (subscriptions.SubscriptionID, <-chan *netconf.Notification, error)
- func (c *Client) Get(ctx context.Context, filter *netconf.Filter) (*netconf.DataReply, error)
- func (c *Client) GetConfig(ctx context.Context, source netconf.Datastore, filter *netconf.Filter) (*netconf.DataReply, error)
- func (c *Client) GetData(ctx context.Context, req nmda.GetData) (*netconf.DataReply, error)
- func (c *Client) GetSchema(ctx context.Context, req *monitoring.GetSchemaRequest) ([]byte, error)
- func (c *Client) KillSession(ctx context.Context, sessionID uint32) error
- func (c *Client) KillSubscription(ctx context.Context, id subscriptions.SubscriptionID, reason string) error
- func (c *Client) Lock(ctx context.Context, target netconf.Datastore) error
- func (c *Client) ModifySubscription(ctx context.Context, req subscriptions.ModifySubscriptionRequest) error
- func (c *Client) Notifications() <-chan *netconf.Notification
- func (c *Client) PartialLock(ctx context.Context, selects []string) (*netconf.PartialLockReply, error)
- func (c *Client) PartialUnlock(ctx context.Context, lockID uint32) error
- func (c *Client) RemoteCapabilities() netconf.CapabilitySet
- func (c *Client) SessionID() uint32
- func (c *Client) Subscribe(ctx context.Context, sub netconf.CreateSubscription) (<-chan *netconf.Notification, error)
- func (c *Client) Unlock(ctx context.Context, target netconf.Datastore) error
- func (c *Client) Validate(ctx context.Context, source netconf.Datastore) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a NETCONF client that multiplexes concurrent RPCs over a single Session using a background dispatcher goroutine.
All exported methods are safe for concurrent use from multiple goroutines.
func AcceptCallHomeSSH ¶
func AcceptCallHomeSSH(ctx context.Context, ln net.Listener, config *gossh.ClientConfig, localCaps netconf.CapabilitySet) (*Client, error)
AcceptCallHomeSSH listens on ln for an incoming TCP connection from a NETCONF server performing SSH call home (RFC 8071 §3). It accepts the first connection, runs the SSH client protocol, negotiates the NETCONF hello exchange, and returns a ready-to-use Client.
In call home, the NETCONF server initiates TCP to the NETCONF client. The SSH client/server roles are unchanged — this function runs the SSH client protocol over the accepted connection.
ln must already be bound and listening (the caller creates net.Listen before calling AcceptCallHomeSSH). The caller is responsible for closing ln after use. For a server that accepts multiple call-home connections, call AcceptCallHomeSSH in a loop.
localCaps declares the capabilities this client advertises in the hello.
ctx is checked for cancellation before Accept is called. If ctx is already done, AcceptCallHomeSSH returns immediately with an error. To cancel a blocked Accept, the caller must close ln — the same pattern as the stdlib net.Listener.
func AcceptCallHomeTLS ¶
func AcceptCallHomeTLS(ctx context.Context, ln net.Listener, config *cryptotls.Config, localCaps netconf.CapabilitySet) (*Client, error)
AcceptCallHomeTLS listens on ln for an incoming TCP connection from a NETCONF server performing TLS call home (RFC 8071 §4). It accepts the first connection, runs the TLS client protocol, negotiates the NETCONF hello exchange, and returns a ready-to-use Client.
In call home, the NETCONF server initiates TCP to the NETCONF client. The TLS client/server roles are unchanged — this function runs the TLS client protocol over the accepted connection.
ln must already be bound and listening. The caller is responsible for closing ln. For a server that accepts multiple call-home connections, call AcceptCallHomeTLS in a loop.
localCaps declares the capabilities this client advertises in the hello.
ctx is checked for cancellation before Accept is called. To cancel a blocked Accept, the caller must close ln.
func Dial ¶
func Dial(ctx context.Context, addr string, config *gossh.ClientConfig, localCaps netconf.CapabilitySet) (*Client, error)
Dial opens a TCP connection to addr, performs the SSH handshake, negotiates the NETCONF hello exchange, and returns a ready-to-use Client.
addr must be in "host:port" form (e.g. "192.0.2.1:830"). config carries SSH authentication credentials and timeouts. localCaps declares the capabilities this client advertises in the hello.
ctx controls the TCP connect phase through net.Dialer.DialContext. Once connected, SSH handshake proceeds on the established socket.
On any error, all partially-opened resources are cleaned up before returning.
func DialTLS ¶
func DialTLS(ctx context.Context, addr string, config *cryptotls.Config, localCaps netconf.CapabilitySet) (*Client, error)
DialTLS opens a TCP connection to addr, performs the TLS handshake with mutual X.509 authentication, negotiates the NETCONF hello exchange, and returns a ready-to-use Client.
addr must be in "host:port" form (e.g. "192.0.2.1:6513"). config must supply the client certificate and the server CA pool for mutual TLS as required by RFC 7589. localCaps declares the capabilities this client advertises in the hello.
ctx controls both the TCP dial and TLS handshake.
Observability Impact ¶
Errors from DialTLS carry a "client: DialTLS <addr>:" prefix so log lines identify the layer and the target address. Failures in the three dial steps are distinguishable:
- "client: DialTLS <addr>: context already done: …" — ctx was cancelled
- "client: DialTLS <addr>: tls client: dial <addr>: …" — TLS handshake or TCP-connect failed (underlying error names cert verify failure, refused connection, etc.)
- "client: DialTLS <addr>: NETCONF hello: …" — hello exchange failed after TLS succeeded
Inspection: `go test ./... -v -run TestClient_TLSLoopback` prints the full TLS→hello→GetConfig stack.
func NewClient ¶
NewClient creates a Client around sess and starts the background dispatcher goroutine. The caller must eventually call Close to release resources.
func (*Client) CancelCommit ¶
CancelCommit cancels an ongoing confirmed commit (RFC 6241 §8.4.9). persistID identifies the persistent confirmed commit to cancel; pass "" for a non-persistent cancel-commit. Requires CapabilityConfirmedCommit.
func (*Client) Close ¶
Close shuts down the client. It closes the done channel (so subsequent Do calls return immediately) and then closes the underlying session, which unblocks the dispatcher's Recv call.
Close is idempotent; subsequent calls are no-ops. The first call returns the error from sess.Close(); subsequent calls return nil.
func (*Client) CloseSession ¶
CloseSession requests graceful termination of the session (RFC 6241 §7.8).
func (*Client) Commit ¶
Commit commits the candidate configuration (RFC 6241 §8.3). If opts is nil a plain <commit/> is issued; otherwise opts is used verbatim. Requires CapabilityCandidate.
func (*Client) CopyConfig ¶
CopyConfig copies an entire configuration datastore (RFC 6241 §7.3).
func (*Client) DeleteConfig ¶
DeleteConfig deletes a configuration datastore (RFC 6241 §7.4).
func (*Client) DeleteSubscription ¶
func (c *Client) DeleteSubscription(ctx context.Context, id subscriptions.SubscriptionID) error
DeleteSubscription sends a delete-subscription RPC (RFC 8639 §2.4.3). It gracefully terminates a subscription owned by this session.
Observability Impact ¶
Errors include "client: DeleteSubscription:" prefix.
func (*Client) DiscardChanges ¶
DiscardChanges reverts the candidate to the running configuration (RFC 6241 §8.3.4). Requires CapabilityCandidate.
func (*Client) Do ¶
Do sends the given NETCONF operation to the server and waits for the matching reply, honoring ctx for cancellation.
op must be a value that xml.Marshal can serialise into a valid NETCONF operation element (e.g. netconf.GetConfig{}, netconf.CloseSession{}).
The returned *RPCReply is non-nil on success. The caller is responsible for checking RPCReply.Ok and RPCReply.Body for operation-level errors.
Errors:
- context.Canceled / context.DeadlineExceeded — caller cancelled the context
- transport errors wrapped with the message-id for diagnosis
- "client: closed" if the client was already closed when Do was called
func (*Client) DroppedNotifications ¶
DroppedNotifications returns the number of notifications dropped because the internal notification channel was full.
func (*Client) EditConfig ¶
EditConfig loads configuration into the target datastore (RFC 6241 §7.2).
func (*Client) EditData ¶
EditData applies configuration changes to a writable NMDA datastore (RFC 8526 §3.2).
Observability Impact ¶
Errors include "client: EditData:" prefix.
func (*Client) Err ¶
Err returns the error that caused the dispatcher goroutine to exit, or nil if the dispatcher is still running or exited cleanly via Close.
This method is useful for post-mortem inspection when a Do call returns an unexpected transport error and the caller wants to distinguish a network failure from a protocol error.
func (*Client) EstablishSubscription ¶
func (c *Client) EstablishSubscription(ctx context.Context, req subscriptions.EstablishSubscriptionRequest) (subscriptions.SubscriptionID, <-chan *netconf.Notification, error)
EstablishSubscription sends an establish-subscription RPC (RFC 8639 §2.4.1) and returns the server-assigned subscription ID and the notification channel.
The notification channel is the same as Notifications() — it is created at Client construction time and delivers all RFC 5277 <notification> messages, including subscription lifecycle notifications (subscription-started, subscription-modified, subscription-terminated, subscription-killed).
On success, the server reply is decoded to extract the subscription ID. Callers should read from the returned channel to receive subscription events.
Observability Impact ¶
Errors include "client: EstablishSubscription:" prefix. A server-side <rpc-error> propagates as netconf.RPCError via errors.As. A reply body decode failure surfaces as a wrapped "decode EstablishSubscriptionReply" error.
func (*Client) Get ¶
Get retrieves running configuration and state data (RFC 6241 §7.7). filter is optional; pass nil to request all data.
func (*Client) GetConfig ¶
func (c *Client) GetConfig(ctx context.Context, source netconf.Datastore, filter *netconf.Filter) (*netconf.DataReply, error)
GetConfig retrieves configuration from the specified datastore (RFC 6241 §7.1). filter is optional; pass nil to request the full datastore.
func (*Client) GetData ¶
GetData retrieves data from any NMDA datastore (RFC 8526 §3.1).
Unlike GetConfig (which only accesses the running datastore), GetData can retrieve data from any NMDA datastore including operational, intended, etc.
Observability Impact ¶
Errors include "client: GetData:" prefix. A server-side <rpc-error> propagates as netconf.RPCError via errors.As. A reply body decode failure surfaces as a wrapped "decode DataReply" error.
func (*Client) GetSchema ¶
func (c *Client) GetSchema(ctx context.Context, req *monitoring.GetSchemaRequest) ([]byte, error)
GetSchema retrieves a schema document via the get-schema RPC (RFC 6022 §3).
The server must advertise the ietf-netconf-monitoring capability (monitoring.CapabilityURI) in its hello. req identifies the schema by Identifier (required), Version (optional), and Format (optional; e.g. "yang").
On success, the raw schema bytes (YANG text, YIN XML, or XSD) are returned. The returned []byte is the innerxml content of the <data> element in the server's rpc-reply — typically a YANG module or XSD document.
Observability Impact ¶
Errors include the "client: GetSchema:" prefix, so log lines identify the operation. A server-side <rpc-error> propagates as netconf.RPCError via errors.As, carrying Tag, Type, Severity, and Message fields. An XML decode failure on the <data> body surfaces as a wrapped "client: GetSchema: decode GetSchemaReply:" error, distinguishable from RPC-level errors. Inspection:
go test ./... -run TestClient_GetSchema -v
func (*Client) KillSession ¶
KillSession forces termination of another session (RFC 6241 §7.9).
func (*Client) KillSubscription ¶
func (c *Client) KillSubscription(ctx context.Context, id subscriptions.SubscriptionID, reason string) error
KillSubscription sends a kill-subscription RPC (RFC 8639 §2.4.4). It forcibly terminates any subscription regardless of ownership. Typically used by administrators.
reason is an optional human-readable reason for the termination; pass "" to omit it.
Observability Impact ¶
Errors include "client: KillSubscription:" prefix.
func (*Client) ModifySubscription ¶
func (c *Client) ModifySubscription(ctx context.Context, req subscriptions.ModifySubscriptionRequest) error
ModifySubscription sends a modify-subscription RPC (RFC 8639 §2.4.2). It changes parameters (filter, stop-time) of an existing subscription.
Observability Impact ¶
Errors include "client: ModifySubscription:" prefix.
func (*Client) Notifications ¶
func (c *Client) Notifications() <-chan *netconf.Notification
Notifications returns the read-only channel on which the dispatcher delivers RFC 5277 <notification> messages. The channel is buffered (capacity 64). It is closed when the dispatcher exits (transport error or Close).
Callers should drain the channel promptly — if it fills up, excess notifications are dropped to prevent the dispatcher from stalling. Use DroppedNotifications to inspect the number of dropped events.
func (*Client) PartialLock ¶
func (c *Client) PartialLock(ctx context.Context, selects []string) (*netconf.PartialLockReply, error)
PartialLock locks a subset of the configuration identified by XPath select expressions (RFC 5717 §2.1). Requires CapabilityPartialLock.
selects is a list of XPath 1.0 expressions that identify the configuration nodes to lock. On success, the device returns a lock-id and the list of locked node canonical XPath expressions.
Observability Impact ¶
Errors include "client: PartialLock:" prefix, so log lines identify the operation. A server-side <rpc-error> propagates as netconf.RPCError via errors.As, carrying Tag, Type, Severity, and Message fields. An XML decode failure on the reply body surfaces as a wrapped "decode PartialLockReply" error distinguishable from RPC-level errors. Inspection:
go test ./... -run TestClient_PartialLock -v go test ./... -run TestConformance_PartialLock -v
func (*Client) PartialUnlock ¶
PartialUnlock releases a partial lock previously acquired via PartialLock (RFC 5717 §2.2). Requires CapabilityPartialLock.
lockID must be the lock-id returned by the successful PartialLock call.
Observability Impact ¶
Errors include "client: PartialUnlock:" prefix. Server-side <rpc-error> propagates as netconf.RPCError via errors.As. Inspection:
go test ./... -run TestClient_PartialUnlock -v go test ./... -run TestConformance_PartialUnlock -v
func (*Client) RemoteCapabilities ¶
func (c *Client) RemoteCapabilities() netconf.CapabilitySet
RemoteCapabilities returns the capability URIs advertised by the remote peer in the NETCONF hello exchange.
func (*Client) SessionID ¶
SessionID returns the server-assigned NETCONF session identifier from the hello exchange.
func (*Client) Subscribe ¶
func (c *Client) Subscribe(ctx context.Context, sub netconf.CreateSubscription) (<-chan *netconf.Notification, error)
Subscribe sends a <create-subscription> RPC (RFC 5277 §2.1.1) and returns the notification channel. The channel is the same as Notifications() — it is created at Client construction time, not at Subscribe time, ensuring notifications sent before Subscribe returns are not lost.
On success, the caller should read from the returned channel. On error, the subscription was not established and no notifications will be delivered.