netconf

package module
v0.0.0-...-099fb5e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 26, 2017 License: MIT Imports: 12 Imported by: 0

README

Documentation

I have a lot of documentation to do here, but at least there's a somewhat decent GoDoc, and this example should help too.

package main

import (
	"encoding/xml"
	"io"
	"log"
	"net"
	"strings"

	"github.com/sourcemonk/netconf"
	"golang.org/x/crypto/ssh"
)

func main() {
	log.SetFlags(log.Flags() | log.Lshortfile)

	sshConfig := ssh.ClientConfig{
		User:            "happy_gopher",
		Auth:            []ssh.AuthMethod{ssh.Password("ConcurrencyIsNoParallelism!")},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	client, err := netconf.NewClient(&sshConfig, net.JoinHostPort("1.2.3.4", "830"))
	if err != nil {
		log.Panic(err)
	}

	defer client.Close()

	session, err := client.NewSession()
	if err != nil {
		log.Panic(err)
	}

	defer session.Close()

	var serverHello netconf.HelloMessage

	decoder := session.NewDecoder()
	if err := decoder.DecodeHello(&serverHello); err != nil {
		log.Panic(err)
	}

	if _, err := io.Copy(session, strings.NewReader(netconf.DefaultHelloMessage)); err != nil {
		log.Panic(err)
	}

	type LLDPMethod struct {
		XMLName xml.Name `xml:"get-lldp-neighbors-information"`
	}

	if err := session.NewEncoder().Encode(&LLDPMethod{}); err != nil {
		log.Panic(err)
	}

	type Neighbor struct {
		LocalInterface         string `xml:"lldp-local-interface,omitempty"`
		LocalParentInterface   string `xml:"lldp-local-parent-interface-name,omitempty"`
		LocalPortID            string `xml:"lldp-local-port-id,omitempty"`
		RemoteChassisIDSubtype string `xml:"lldp-remote-chassis-id-subtype,omitempty"`
		RemoteChassisID        string `xml:"lldp-remote-chassis-id,omitempty"`
		RemotePortIDSubtype    string `xml:"lldp-remote-port-id-subtype,omitempty"`
		RemotePortID           string `xml:"lldp-remote-port-id,omitempty"`
		RemotePortDesc         string `xml:"lldp-remote-port-description,omitempty"`
		RemoteSystemName       string `xml:"lldp-remote-system-name,omitempty"`
	}

	type Results struct {
		Neighbors []Neighbor `xml:"lldp-neighbor-information,omitempty"`
	}

	lldpNbrResults := Results{}
	if err := decoder.Decode(&lldpNbrResults); err != nil {
		log.Panic(err)
	}

	log.Printf("%v", lldpNbrResults)
}

Documentation

Index

Constants

View Source
const (
	// MessageSeparator is explicitly invalid XML used to unambiguously separate NETCONF messages.
	MessageSeparator = `]]>]]>`

	// BaseNamespace is the most basic NETCONF namespace, and is the default namespace for all NETCONF methods.
	BaseNamespace = `urn:ietf:params:xml:ns:netconf:base:1.0`
)
View Source
const DefaultHelloMessage = `` /* 200-byte string literal not displayed */

DefaultHelloMessage is this library's default hello sent to the server, when it is not sent manually by the client application.

Variables

GlobalCounter keeps a running count of every NETCONF RPC. It is incremented by the DefaultXMLAttr and DefaultRPCMethodWrapper functions.

GlobalCounter is safe for client applications to access, use, and increment.

Functions

func Marshal

func Marshal(v interface{}) ([]byte, error)

Marshal returns the NETCONF encoding of v, including message separators and enclosing RPC tags.

If the argument's type is *Method, MarshalRPC just calls xml.Marshal internally to build the XML. Otherwise, it wraps its argument in the default *Method before calling xml.Marshal.

A NETCONF message separator and newline is always written to the end of the message.

func NewSession

func NewSession(clientConfig *ssh.ClientConfig, target string) (*Session, *HelloMessage, error)

NewSession creates a new session ready for use with the NETCONF SSH subsystem. It uses the credentials given by the ssh.ClientConfig argument to connect to the target. Hello messages are negotiated, and the server's hello message is returned along with a newly allocated Session pointer.

func Unmarshal

func Unmarshal(data []byte, v interface{}) error

Unmarshal maps the NETCONF RPC reply XML into the given argument, discarding the terminating message separator.

func XMLAttr

func XMLAttr(messageID string) []xml.Attr

XMLAttr returns a slice of xml.Attr containing only one xml.Attr for the RPC's message-id.

func XMLNameTag

func XMLNameTag(namespace string) xml.Name

XMLNameTag returns an xml.Name for an RPC's outer tag. It is appropriately named "rpc", with an attribute that has the given namespace.

Types

type DeadlineError

type DeadlineError struct {
	Op        string
	BeginTime time.Time
	FailTime  time.Time
	Deadline  time.Duration
}

DeadlineError is returned when a read or write deadline is reached.

func (*DeadlineError) Error

func (te *DeadlineError) Error() string

Error implements the error interface.

type DeadlineReader

type DeadlineReader struct {
	// contains filtered or unexported fields
}

DeadlineReader is a decorator for an io.Reader that sets a deadline before every read. It can only be constructed by a ReplyReader's WithDeadline method.

func (*DeadlineReader) AsDecoder

func (dr *DeadlineReader) AsDecoder() *Decoder

func (*DeadlineReader) Read

func (dr *DeadlineReader) Read(b []byte) (n int, err error)

Read sets a deadline before every call to Read, and returns a DeadlineError if reading is not complete before the configured deadline expires. It is recommended that you close the session upon receipt of a DeadlineError, otherwise a subsequent read will return whatever the NETCONF server wrote to its stdout stream after the deadline expired.

type Decoder

type Decoder struct {
	*xml.Decoder
	// contains filtered or unexported fields
}

Decoder embeds an xml.Decoder, but overrides Decode with a custom implementation designed specifically to decode NETCONF RPC replies.

func NewDecoder

func NewDecoder(r io.Reader) *Decoder

NewDecoder buffers the given io.Reader, and wraps it in a Decoder.

func (*Decoder) Decode

func (d *Decoder) Decode(v interface{}) error

Decode wraps the interface{} parameter in a Reply object to capture all of the RPC Reply content. It also searches for errors in the Reply, and returns the first ReplyError found. as a standard error interface.

unmarshals a single NETCONF RPC reply message into the given interface{}.

A full RPC Reply can be obtained Parsing XML as a stream of tokens is still possible using the embedded xml.Decoder. However, Done should be called finished to discard the NETCONF message separator.

func (*Decoder) DecodeHello

func (d *Decoder) DecodeHello(h *HelloMessage) error

DecodeHello handles hello/capabilities messages sent by the NETCONF server. It's a special decode case since the closing tags are named "hello" rather than "rpc-reply".

func (*Decoder) SkipSep

func (d *Decoder) SkipSep() error

SkipSep discarding everything from the underlying buffer until it encounters a NETCONF message separator, or an error.

Since the separator is explicitly designed to be invalid XML, failure to discard it before decoding will cause the standard decoder to fail with a syntax error.

Using this method is only necessary when manually decoding XML tokens as a stream, with DecodeToken, et al.

Calls to SkipSep may block if more bytes have to be read from the underlying net.Conn.

Most uses will call Decode, which calls SkipSep internally.

type Encoder

type Encoder struct {
	*xml.Encoder
	// contains filtered or unexported fields
}

Encoder embeds an xml.Encoder, but overrides Encode with a custom implementation designed specifically to encode NETCONF RPC requests.

func NewEncoder

func NewEncoder(w io.Writer) *Encoder

NewEncoder buffers the given io.Writer, and wraps it into a Encoder.

func (*Encoder) Encode

func (e *Encoder) Encode(v interface{}) error

Encode encodes a single NETCONF RPC, and marshals it into the underlying buffer. Then it writes the NETCONF message separator followed by a newline, and flushes the buffer.

Encoding XML as a stream of tokens is still possible using the underlying xml.Encoder. However, WriteSep must should be called after encoding an RPC.

func (*Encoder) EncodeHello

func (e *Encoder) EncodeHello(h *HelloMessage) error

EncodeHello writes the given hello message to the underlying writer, writes a message separator, and flushes the buffer.

func (*Encoder) WriteSep

func (e *Encoder) WriteSep() error

WriteSep writes a message separator with a trailing newline to the underlying buffered io.Writer, and flushes the buffer before returning. Using this method is only necessary when manually encoding XML tokens as a stream with EncodeToken, et al.

Calls to WriteSep may block depending on the underlying net.Conn.

Most uses will call Encode, which calls WriteSep internally.

type ErrorInfo

type ErrorInfo struct {
	BadAttribute string   `xml:"bad-attribute"` // BadAttribute has the name(s) of the bad, missing, or unexpected attribute(s).
	BadElement   string   `xml:"bad-element"`   // BadElement is the name of the element that should (or does) contain the missing (or bad) attribute.
	BadNamespace string   `xml:"bad-namespace"` // BadNamespace contains the name of the unexpected namespace.
	OkElement    []string `xml:"ok-element"`    // OkElement is the parent element for which all children have completed the requested operation.
	ErrElement   []string `xml:"err-element"`   // ErrElement is the parent element for which all children have failed to complete the requested operation.
	NOPElement   []string `xml:"noop-element"`  // NOPElement is the parent element that identifies all children for which the requested operation was not attempted.
}

ErrorInfo contains protocol or data model specific error content.

type ErrorSeverity

type ErrorSeverity uint

ErrorSeverity identifies severity of the error as either warning, or error.

const (
	ErrorSeverityZero    ErrorSeverity = iota // ErrorSeverityZero represents an uninitialized ErrorSeverity value.
	ErrorSeverityError                        // ErrorSeverityError indicates the severity is on the error level.
	ErrorSeverityUnknown                      // ErrorSeverityUnknown means the ErrorSeverity could not be identified, and may indicate an internal error.
	ErrorSeverityWarning                      // ErrorSeverityWarning is not yet utilized, according to RFC 6241.
)

func (ErrorSeverity) String

func (es ErrorSeverity) String() string

String returns a string representing the ErrorSeverity. If the ErrorSeverity is not known for some erroneous reason, the String will return "unknown".

func (*ErrorSeverity) UnmarshalText

func (es *ErrorSeverity) UnmarshalText(text []byte) error

UnmarshalText sets the receiver to the constant represented by the text argument given. If the text argument does not represent a known ErrorSeverity, it is set to the ErrorSeverityUnknown constant, and an UnmarshalTextError is returned.

type ErrorTag

type ErrorTag uint64

ErrorTag identifies the error condition.

const (
	ErrorTagZero             ErrorTag = iota // ErrorTagZero is an uninitialized ErrorTag value.
	ErrorTagAccessDenied                     // ErrorTagAccessDenied indicates access was denied because authorization failed.
	ErrorTagBadAttribute                     // ErrorTagBadAttribute indicates an attribute value is not correct; e.g., wrong type, out of range, pattern mismatch.
	ErrorTagBadElement                       // ErrorTagBadElement indicates a bad element value was in the RPC. ErrorInfo's BadElement field will contain element's name.
	ErrorTagDataExists                       // ErrorTagDataExists indicates data could not be created because it already exists.
	ErrorTagDataMissing                      // ErrorTagDataMissing indicates data could not be deleted because it doesn't exist.
	ErrorTagInUse                            // ErrorTagInUse indicates the required resource is already in use.
	ErrorTagInvalidValue                     // ErrorTagInvalidValue indicates the RPC specifies an unacceptable value for one or more parameters.
	ErrorTagLockDenied                       // ErrorTagLockDenied indicates the requested lock is denied because it is held by another entity.
	ErrorTagMalformedMessage                 // ErrorTagMalformedMessage indicates a failure to parse the RPC correctly.
	ErrorTagMissingAttribute                 // ErrorTagMissingAttribute indicates an expected attribute is missing.
	ErrorTagMissingElement                   // ErrorTagMissingElement indicates an expected element is missing. ErrorInfo's BadElement field will contain the name of the missing element.
	ErrorTagOpFailed                         // ErrorTagOpFailed indicates failure for some reason not covered by other error conditions.
	ErrorTagOpNotSupported                   // ErrorTagOpNotSupported indicates RPC is not supported by the implementation.
	ErrorTagOpPartial                        // ErrorTagOpPartial indicates the RPC failed partially or was aborted, and cleanup was not performed. ErrorInfo's OkElement, ErrElement, and NOPElement identify elements that succeeded, failed, and were aborted respectively.
	ErrorTagResourceDenied                   // ErrorTagResourceDenied indicates insufficient resources.
	ErrorTagRollbackFailed                   // ErrorTagRollbackFailed indicates the rollback was not completed.
	ErrorTagTooBig                           // ErrorTagTooBig indicates the request or response is too large to handle.
	ErrorTagUnknown                          // ErrorTagUnknown probably indicates an internal error, because the error type could not be identified.
	ErrorTagUnknownAttribute                 // ErrorTagUnknownAttribute indicates an unexpected attribute is present. ErrorInfo's BadAttribute and BadElement field will contain more detail.
	ErrorTagUnknownElement                   // ErrorTagUnknownElement indicates an unexpected element. ErrorInfo's BadElement field will contain its name.
	ErrorTagUnknownNamespace                 // ErrorTagUnknownNamespace indicates an unexpected namespace is present. ErrorInfo's BadElement and BadNamespace fields will contain more detail.
)

func (ErrorTag) Severity

func (et ErrorTag) Severity() ErrorSeverity

Severity returns the severity of this ErrorTag.

func (ErrorTag) String

func (et ErrorTag) String() string

String returns a string representation of this ErrorTag value.

func (*ErrorTag) UnmarshalText

func (et *ErrorTag) UnmarshalText(text []byte) error

UnmarshalText sets the ErrorTag receiver to the constant represented by the text argument given. If the text argument does not represent a known ErrorTag, the ErrorTag is set to the ErrorTagUnknown constant, and an UnmarshalTextError is returned.

type ErrorType

type ErrorType uint64

ErrorType defines the conceptual layer that the error occurred in.

const (
	// ErrorTypeZero represents an uninitialized ErrorType value.
	ErrorTypeZero ErrorType = iota
	// ErrorTypeApplication indicates the error occurred on the Content layer.
	ErrorTypeApplication
	// ErrorTypeProtocol indicates the error occurred on the Operations layer, which defines a set of base protocol operations invoked as RPC methods.
	ErrorTypeProtocol
	// ErrorTypeRPC indicates the error occurred on the Messages layer: the transport-independent framing mechanism for encoding RPCs and notifications.
	ErrorTypeRPC
	// ErrorTypeTransport indicates the error occurred on the Secure Transport layer, which provides a communication path between the client and server.
	ErrorTypeTransport
	// ErrorTypeUnknown indicates an unexpected condition.
	ErrorTypeUnknown
)

func (ErrorType) String

func (es ErrorType) String() string

String returns a string representation of the ErrorType. If the ErrorType is unknown, the ErrorTypeUnknown constant is returned.

func (*ErrorType) UnmarshalText

func (es *ErrorType) UnmarshalText(text []byte) error

UnmarshalText sets the ErrorType receiver to the constant represented by the text argument given. If the text argument does not represent a known ErrorType, it is set to the ErrorTypeUnknown constant, and an UnmarshalTextError is returned.

type HelloMessage

type HelloMessage struct {
	XMLName      xml.Name
	Capabilities []string `xml:"capabilities>capability"`
	SessionID    uint     `xml:"session-id,omitempty"`
}

HelloMessage represents a capabilities exchange message.

func (*HelloMessage) Copy

func (h *HelloMessage) Copy() *HelloMessage

Copy makes a deep copy of this HelloMessage.

type Method

type Method struct {
	XMLName xml.Name
	Attr    []xml.Attr `xml:",attr"`
	Method  []interface{}
}

Method models the structure of a NETCONF method. It is useful for wrapping structs that don't encode the outer rpc tags.

Multiple methods can be encoded into one RPC.

func WrapMethod

func WrapMethod(method ...interface{}) *Method

WrapMethod wraps the given methods' with outer rpc tags, and sets default values for namespace and message id attributes. It returns a pointer to a Method that can be directly marshaled into an RPC by Encoder.

type Reply

type Reply struct {
	XMLName xml.Name     `xml:"rpc-reply"`
	Attr    []xml.Attr   `xml:",attr"`
	Ok      *struct{}    `xml:"ok"`
	Error   []ReplyError `xml:"rpc-error"`
	Data    interface{}  `xml:",any"`
}

Reply models the structure of a NETCONF reply. It is useful for wrapping structs that don't decode the outer rpc-reply tags.

type ReplyError

type ReplyError struct {
	Type     ErrorType     `xml:"error-type"`     // Type is the conceptual layer that the error occurred.
	Tag      ErrorTag      `xml:"error-tag"`      // Tag identifies the error condition.
	Severity ErrorSeverity `xml:"error-severity"` // Severity is the error severity: either error or warning.
	Info     ErrorInfo     `xml:"error-info"`     // Info contains protocol or data-model-specific error content.
	Path     string        `xml:"error-path"`     // Path is the absolute XPath expression identifying the element path to the node.
	Message  string        `xml:"error-message"`  // Message is a human friendly description of the error.
}

ReplyError encapsulates a NETCONF RPC error, and implements the error interface.

func (*ReplyError) Error

func (e *ReplyError) Error() string

Error is the implementation of the error interface.

type ReplyReader

type ReplyReader struct {
	// contains filtered or unexported fields
}

ReplyReader reads exactly one RPC reply from the session, and discards the message separator. If multiple RPCs need to be read from the session, multiple ReplyReaders will be required. The io.EOF error is returned on every read after the NETCONF message separator is encountered. This is how ReplyReader is able to satisfy the strict interpretation of the io.Reader interface.

func NewReplyReader

func NewReplyReader(session io.Reader) *ReplyReader

NewReplyReader assumes the given reader reads from a NETCONF session's stdout, and adapts its behavior to a standard io.Reader, allowing it to work with standard library methods and functions. It is intended to read exactly one RPC reply, however it can be reused after calling the Reset method.

func (*ReplyReader) Read

func (rr *ReplyReader) Read(p []byte) (n int, err error)

Read implements the io.Reader interface by returning io.EOF whenever the standard NETCONF message separator is found in the byte stream.

func (*ReplyReader) Reset

func (rr *ReplyReader) Reset()

Reset clears the internal error field, allowing this reader to be reused.

func (*ReplyReader) WithDeadline

func (rr *ReplyReader) WithDeadline(deadline time.Duration) *DeadlineReader

WithDeadline decorates the ReplyReader with a DeadlineReader. The DeadlineReader sets its deadline before every call to Read.

type Session

type Session struct {
	// contains filtered or unexported fields
}

Session wraps an *ssh.Session providing additional NETCONF functionality. An initialized Session is a io.ReadWriteCloser, with the io.Reader connected to the ssh.Session's stdout, and the io.WriteCloser connected to the ssh.Sessions's stdin.

func (*Session) Close

func (s *Session) Close() error

Close closes all session resources in the following order:

  1. stdin pipe
  2. SSH session
  3. SSH client

Errors are returned with priority matching the same order.

func (*Session) NewDeadlineReader

func (s *Session) NewDeadlineReader(deadline time.Duration) io.Reader

NewDeadlineReader decorates the session's io.Reader with a new DeadlineReader.

The DeadlineReader only adds a deadline when reading from the stream. It does not handle higher level functionality, like a complete implementation of an io.Reader, or discarding NETCONF message separators.

func (*Session) NewDecoder

func (s *Session) NewDecoder() *Decoder

NewDecoder returns a new Decoder object attached to the stdout pipe of the underlying SSH session.

func (*Session) NewEncoder

func (s *Session) NewEncoder() *Encoder

NewEncoder returns a new Encoder object attached to the stdin pipe of the underlying SSH session.

func (*Session) NewReplyReader

func (s *Session) NewReplyReader() *ReplyReader

NewReplyReader returns a ReplyReader that reads exactly one NETCONF RPC Reply from the session's stdout stream. The ReplyReader strictly satisfies io.Reader interface by reading from the stream until the NETCONF message separator "]]>]]>" is reached, and an io.EOF error is returned. The io.EOF error is also returned on all subsequent calls.

The ReplyReader does not close the underlying session. Multiple ReplyReaders are required to read multiple replies from the same session.

func (*Session) NewTimeoutDecoder

func (s *Session) NewTimeoutDecoder(timeout time.Duration) *Decoder

NewTimeoutDecoder returns a new Decoder attached to the stdout pipe of the underlying SSH session. The Decoder's io.TrimReader is wrapped to set a read timeout on the underlying net.Conn before every read.

func (*Session) Read

func (s *Session) Read(p []byte) (n int, err error)

Read is a partial implementation of the io.Reader interface. It reads directly from the session without any modifications. It is not compliant with the standard io.Reader interface because an EOF is only returned if the session is closed.

Most will use ReplyReader or Decoder.

func (*Session) Write

func (s *Session) Write(p []byte) (n int, err error)

Write is the most basic implementation of the io.Writer interface. It writes directly to the stdin stream of the NETCONF session, and does not write a NETCONF message separator "]]>]]>".

Most will use Encoder.

type Uint

type Uint struct {
	// contains filtered or unexported fields
}

Uint is a 64-bit unsigned integer variable that satisfies the expvar.Var interface.

func NewUintCounterContext

func NewUintCounterContext(ctx context.Context) *Uint

NewUintCounterContext allocates the new unsigned integer counter with resources that can only be cancelled by a context.

func (*Uint) Add

func (v *Uint) Add(delta uint64)

Add add the given delta argument to the underlying uint64 value.

func (*Uint) Set

func (v *Uint) Set(value uint64)

Set assigns the given value argument to the underlying uint64.

func (*Uint) String

func (v *Uint) String() string

String converts the underlying uint64 to its base 10 string representation.

func (*Uint) Value

func (v *Uint) Value() uint64

Value returns the current value of the underlying uint64.

type UnmarshalTextError

type UnmarshalTextError struct {
	Type  string // Type is the unknown value's type.
	Value string // Value is the what caused the failure.
}

UnmarshalTextError is returned when UnmarshalText fails to parse the text it's given.

func (*UnmarshalTextError) Error

func (ute *UnmarshalTextError) Error() string

Error is UnmarshalTextError's implementation of the error interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL