From 8bc33fa603af9ccaff0606f4ceb82f46374838de Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Fri, 13 May 2022 16:22:33 +0200 Subject: [PATCH 01/23] changing to netconf client --- alarm/collector.go | 4 +-- bfd/collector.go | 3 +- bgp/collector.go | 9 ++--- connector/connection.go | 28 +++++----------- connector/connection_manager.go | 55 +++++++------------------------ environment/collector.go | 6 ++-- fpc/collector.go | 3 +- go.mod | 2 ++ go.sum | 4 +++ interfacediagnostics/collector.go | 3 +- interfacelabels/dynamic_labels.go | 2 +- interfacequeue/collector.go | 3 +- interfaces/collector.go | 3 +- isis/collector.go | 3 +- l2circuit/collector.go | 3 +- lacp/collector.go | 3 +- ldp/collector.go | 3 +- main.go | 1 - mpls_lsp/collector.go | 3 +- nat2/collector.go | 3 +- ospf/collector.go | 10 +++--- power/collector.go | 3 +- route/collector.go | 3 +- routingengine/collector.go | 3 +- rpc/rpc_client.go | 3 +- rpm/collector.go | 3 +- storage/collector.go | 3 +- system/collector.go | 6 ++-- vpws/collector.go | 3 +- vrrp/collector.go | 3 +- 30 files changed, 86 insertions(+), 98 deletions(-) diff --git a/alarm/collector.go b/alarm/collector.go index 55754d29..a5d82a29 100644 --- a/alarm/collector.go +++ b/alarm/collector.go @@ -74,8 +74,8 @@ func (c *alarmCollector) alarmCounter(client *rpc.Client) (*AlarmCounter, *[]Ala yellow := 0 cmds := []string{ - "show system alarms", - "show chassis alarms", + "", + "", } var alarms []AlarmDetails diff --git a/bfd/collector.go b/bfd/collector.go index 5298568f..dde15b51 100644 --- a/bfd/collector.go +++ b/bfd/collector.go @@ -44,7 +44,8 @@ func (*bfdCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *bfdCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = bfdRpc{} - err := client.RunCommandAndParse("show bfd session extensive", &x) +// err := client.RunCommandAndParse("show bfd session extensive", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } diff --git a/bgp/collector.go b/bgp/collector.go index 9c9b2680..ecfece63 100644 --- a/bgp/collector.go +++ b/bgp/collector.go @@ -77,10 +77,11 @@ func (c *bgpCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *bgpCollector) collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = BGPRPC{} var cmd strings.Builder - cmd.WriteString("show bgp neighbor") - if c.LogicalSystem != "" { - cmd.WriteString(" logical-system " + c.LogicalSystem) - } +// cmd.WriteString("show bgp neighbor") +// if c.LogicalSystem != "" { +// cmd.WriteString(" logical-system " + c.LogicalSystem) +// } + cmd.WriteString("") err := client.RunCommandAndParse(cmd.String(), &x) if err != nil { diff --git a/connector/connection.go b/connector/connection.go index e9d1250f..157cc014 100644 --- a/connector/connection.go +++ b/connector/connection.go @@ -1,19 +1,18 @@ package connector import ( - "bytes" "net" "sync" "github.com/pkg/errors" - "golang.org/x/crypto/ssh" + "github.com/Juniper/go-netconf/netconf" ) // SSHConnection encapsulates the connection to the device type SSHConnection struct { device *Device - client *ssh.Client + session *netconf.Session conn net.Conn mu sync.Mutex done chan struct{} @@ -24,25 +23,16 @@ func (c *SSHConnection) RunCommand(cmd string) ([]byte, error) { c.mu.Lock() defer c.mu.Unlock() - if c.client == nil { + if c.session == nil { return nil, errors.New("not connected") } - session, err := c.client.NewSession() - if err != nil { - return nil, errors.Wrap(err, "could not open session") - } - defer session.Close() - - var b = &bytes.Buffer{} - session.Stdout = b - - err = session.Run(cmd) + reply, err := c.session.Exec(netconf.RawMethod(cmd)) if err != nil { return nil, errors.Wrap(err, "could not run command") } - return b.Bytes(), nil + return []byte(reply.RawReply), nil } func (c *SSHConnection) isConnected() bool { @@ -55,7 +45,7 @@ func (c *SSHConnection) terminate() { c.conn.Close() - c.client = nil + c.session = nil c.conn = nil } @@ -63,13 +53,13 @@ func (c *SSHConnection) close() { c.mu.Lock() defer c.mu.Unlock() - if c.client != nil { - c.client.Close() + if c.session != nil { + c.session.Close() } c.done <- struct{}{} c.conn = nil - c.client = nil + c.session = nil } // Host returns the hostname of the connected device diff --git a/connector/connection_manager.go b/connector/connection_manager.go index 8a44ca56..c15b4f9a 100644 --- a/connector/connection_manager.go +++ b/connector/connection_manager.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "golang.org/x/crypto/ssh" + "github.com/Juniper/go-netconf/netconf" ) const timeoutInSeconds = 5 @@ -25,13 +26,6 @@ func WithReconnectInterval(d time.Duration) Option { } } -// WithKeepAliveInterval sets the keep alive interval (default 10 seconds) -func WithKeepAliveInterval(d time.Duration) Option { - return func(m *SSHConnectionManager) { - m.keepAliveInterval = d - } -} - // WithKeepAliveTimeout sets the timeout after an ssh connection to be determined dead (default 15 seconds) func WithKeepAliveTimeout(d time.Duration) Option { return func(m *SSHConnectionManager) { @@ -39,12 +33,13 @@ func WithKeepAliveTimeout(d time.Duration) Option { } } + // SSHConnectionManager manages SSH connections to different devices type SSHConnectionManager struct { connections map[string]*SSHConnection reconnectInterval time.Duration - keepAliveInterval time.Duration keepAliveTimeout time.Duration + mu sync.Mutex } @@ -53,8 +48,7 @@ func NewConnectionManager(opts ...Option) *SSHConnectionManager { m := &SSHConnectionManager{ connections: make(map[string]*SSHConnection), reconnectInterval: 30 * time.Second, - keepAliveInterval: 10 * time.Second, - keepAliveTimeout: 15 * time.Second, + keepAliveTimeout: 20 * time.Second, } for _, opt := range opts { @@ -81,25 +75,23 @@ func (m *SSHConnectionManager) Connect(device *Device) (*SSHConnection, error) { } func (m *SSHConnectionManager) connect(device *Device) (*SSHConnection, error) { - client, conn, err := m.connectToDevice(device) + session, err := m.connectToDevice(device) if err != nil { return nil, err } c := &SSHConnection{ - conn: conn, - client: client, + session: session, device: device, done: make(chan struct{}), } - go m.keepAlive(c) m.connections[device.Host] = c return c, nil } -func (m *SSHConnectionManager) connectToDevice(device *Device) (*ssh.Client, net.Conn, error) { +func (m *SSHConnectionManager) connectToDevice(device *Device) (*netconf.Session, error) { cfg := &ssh.ClientConfig{ HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: timeoutInSeconds * time.Second, @@ -109,17 +101,12 @@ func (m *SSHConnectionManager) connectToDevice(device *Device) (*ssh.Client, net host := m.tcpAddressForHost(device.Host) - conn, err := net.DialTimeout("tcp", host, cfg.Timeout) - if err != nil { - return nil, nil, errors.Wrap(err, "could not open tcp connection") - } - - c, chans, reqs, err := ssh.NewClientConn(conn, host, cfg) + session,err := netconf.DialSSHTimeout(host,cfg,m.keepAliveTimeout) if err != nil { - return nil, nil, errors.Wrap(err, "could not connect to device") + return nil, errors.Wrap(err, "could not connect to device") } - return ssh.NewClient(c, chans, reqs), conn, nil + return session, nil } func (m *SSHConnectionManager) tcpAddressForHost(host string) string { @@ -149,30 +136,12 @@ func (m *SSHConnectionManager) formatHost(host string) string { return "[" + host + "]" } -func (m *SSHConnectionManager) keepAlive(connection *SSHConnection) { - for { - select { - case <-time.After(m.keepAliveInterval): - log.Debugf("Sending keepalive for ") - connection.conn.SetDeadline(time.Now().Add(m.keepAliveTimeout)) - _, _, err := connection.client.SendRequest("keepalive@golang.org", true, nil) - if err != nil { - log.Infof("Lost connection to %s (%v). Trying to reconnect...", connection.device, err) - connection.terminate() - m.reconnect(connection) - } - case <-connection.done: - return - } - } -} func (m *SSHConnectionManager) reconnect(connection *SSHConnection) { for { - client, conn, err := m.connectToDevice(connection.device) + session, err := m.connectToDevice(connection.device) if err == nil { - connection.client = client - connection.conn = conn + connection.session = session return } diff --git a/environment/collector.go b/environment/collector.go index 68a62e69..33a5e126 100644 --- a/environment/collector.go +++ b/environment/collector.go @@ -78,7 +78,8 @@ func (c *environmentCollector) environmentItems(client *rpc.Client, ch chan<- pr "Present": 5, } - err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { +// err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { return parseXML(b, &x) }) if err != nil { @@ -132,7 +133,8 @@ func (c *environmentCollector) environmentPEMItems(client *rpc.Client, ch chan<- "Empty": 3, } - err := client.RunCommandAndParseWithParser("show chassis environment pem", func(b []byte) error { +// err := client.RunCommandAndParseWithParser("show chassis environment pem", func(b []byte) error { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { return parseXML(b, &x) }) if err != nil { diff --git a/fpc/collector.go b/fpc/collector.go index 2d5ce542..9700246a 100644 --- a/fpc/collector.go +++ b/fpc/collector.go @@ -100,7 +100,8 @@ func (c *fpcCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, // CollectFPC collects metrics from JunOS func (c *fpcCollector) CollectFPCDetail(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { r := RpcReply{} - err := client.RunCommandAndParseWithParser("show chassis fpc detail", func(b []byte) error { +// err := client.RunCommandAndParseWithParser("show chassis fpc detail", func(b []byte) error { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { return parseXML(b, &r) }) diff --git a/go.mod b/go.mod index 44aca83b..a4053d53 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( ) require ( + github.com/Juniper/go-netconf v0.1.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -21,6 +22,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/common v0.26.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect + github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b // indirect golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect google.golang.org/protobuf v1.26.0-rc.1 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect diff --git a/go.sum b/go.sum index d1d86864..15cf3f17 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Juniper/go-netconf v0.1.1 h1:5fx/T7L2Fwq51UnESPOP1CXgGCs7IYxR/pnyC5quu/k= +github.com/Juniper/go-netconf v0.1.1/go.mod h1:2Fy6tQTWnL//D/Ll1hb0RYXN4jndcTyneRn6xj5E1VE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -97,6 +99,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b h1:VfPXB/wCGGt590QhD1bOpv2J/AmC/RJNTg/Q59HKSB0= +github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b/go.mod h1:IZpXDfkJ6tWD3PhBK5YzgQT+xJWh7OsdwiG8hA2MkO4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/interfacediagnostics/collector.go b/interfacediagnostics/collector.go index c5695c54..cda020fd 100644 --- a/interfacediagnostics/collector.go +++ b/interfacediagnostics/collector.go @@ -255,7 +255,8 @@ func (c *interfaceDiagnosticsCollector) Collect(client *rpc.Client, ch chan<- pr func (c *interfaceDiagnosticsCollector) interfaceDiagnostics(client *rpc.Client) ([]*InterfaceDiagnostics, error) { var x = InterfaceDiagnosticsRPC{} - err := client.RunCommandAndParse("show interfaces diagnostics optics", &x) +// err := client.RunCommandAndParse("show interfaces diagnostics optics", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return nil, err } diff --git a/interfacelabels/dynamic_labels.go b/interfacelabels/dynamic_labels.go index 24339da6..4cde48ad 100644 --- a/interfacelabels/dynamic_labels.go +++ b/interfacelabels/dynamic_labels.go @@ -47,7 +47,7 @@ type interfaceLabel struct { // CollectDescriptions collects labels from descriptions func (l *DynamicLabels) CollectDescriptions(device *connector.Device, client *rpc.Client, ifDescReg *regexp.Regexp) error { r := &InterfaceRPC{} - err := client.RunCommandAndParse("show interfaces descriptions", r) + err := client.RunCommandAndParse("", r) if err != nil { return errors.Wrap(err, "could not retrieve interface descriptions for "+device.Host) } diff --git a/interfacequeue/collector.go b/interfacequeue/collector.go index 4435a586..12aa246f 100644 --- a/interfacequeue/collector.go +++ b/interfacequeue/collector.go @@ -101,7 +101,8 @@ func (c *interfaceQueueCollector) Describe(ch chan<- *prometheus.Desc) { func (c *interfaceQueueCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { q := InterfaceQueueRPC{} - err := client.RunCommandAndParse("show interfaces queue", &q) +// err := client.RunCommandAndParse("show interfaces queue", &q) + err := client.RunCommandAndParse("", &q) if err != nil { return err } diff --git a/interfaces/collector.go b/interfaces/collector.go index f19d7236..9b24e3bd 100644 --- a/interfaces/collector.go +++ b/interfaces/collector.go @@ -146,7 +146,8 @@ func (c *interfaceCollector) Collect(client *rpc.Client, ch chan<- prometheus.Me func (c *interfaceCollector) interfaceStats(client *rpc.Client) ([]*InterfaceStats, error) { var x = InterfaceRpc{} - err := client.RunCommandAndParse("show interfaces extensive", &x) +// err := client.RunCommandAndParse("show interfaces extensive", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return nil, err } diff --git a/isis/collector.go b/isis/collector.go index 6714efbf..16b7fc58 100644 --- a/isis/collector.go +++ b/isis/collector.go @@ -56,7 +56,8 @@ func (c *isisCollector) isisAdjancies(client *rpc.Client) (*IsisAdjacencies, err total := 0 var x = IsisRpc{} - err := client.RunCommandAndParse("show isis adjacency", &x) +// err := client.RunCommandAndParse("show isis adjacency", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return nil, err } diff --git a/l2circuit/collector.go b/l2circuit/collector.go index 1e0dd678..fb64d453 100644 --- a/l2circuit/collector.go +++ b/l2circuit/collector.go @@ -78,7 +78,8 @@ func (c *l2circuitCollector) Collect(client *rpc.Client, ch chan<- prometheus.Me func (c *l2circuitCollector) collectL2circuitMetrics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = L2circuitRpc{} - err := client.RunCommandAndParse("show l2circuit connections brief", &x) +// err := client.RunCommandAndParse("show l2circuit connections brief", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } diff --git a/lacp/collector.go b/lacp/collector.go index 165db6c1..6e7b11ea 100644 --- a/lacp/collector.go +++ b/lacp/collector.go @@ -47,7 +47,8 @@ func (*lacpCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *lacpCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = lacpRpc{} - err := client.RunCommandAndParse("show lacp interfaces", &x) +// err := client.RunCommandAndParse("show lacp interfaces", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } diff --git a/ldp/collector.go b/ldp/collector.go index bb8a1e51..c4ab50a0 100644 --- a/ldp/collector.go +++ b/ldp/collector.go @@ -62,7 +62,8 @@ func (c *ldpCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *ldpCollector) collectLDPMetrics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = LDPRpc{} - err := client.RunCommandAndParse("show ldp neighbor", &x) +// err := client.RunCommandAndParse("show ldp neighbor", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } diff --git a/main.go b/main.go index ce19f36b..9c0c749b 100644 --- a/main.go +++ b/main.go @@ -225,7 +225,6 @@ func loadConfigFromFlags() *config.Config { func connectionManager() *connector.SSHConnectionManager { opts := []connector.Option{ connector.WithReconnectInterval(*sshReconnectInterval), - connector.WithKeepAliveInterval(*sshKeepAliveInterval), connector.WithKeepAliveTimeout(*sshKeepAliveTimeout), } diff --git a/mpls_lsp/collector.go b/mpls_lsp/collector.go index 32888326..15109d90 100644 --- a/mpls_lsp/collector.go +++ b/mpls_lsp/collector.go @@ -51,7 +51,8 @@ func (*mpls_lspCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *mpls_lspCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = mpls_lspRpc{} - err := client.RunCommandAndParse("show mpls lsp ingress extensive", &x) //ingress:Display LSPs originating at this router +// err := client.RunCommandAndParse("show mpls lsp ingress extensive", &x) //ingress:Display LSPs originating at this router + err := client.RunCommandAndParse("", &x) //ingress:Display LSPs originating at this router if err != nil { return err } diff --git a/nat2/collector.go b/nat2/collector.go index b223eb45..1c14a930 100644 --- a/nat2/collector.go +++ b/nat2/collector.go @@ -225,7 +225,8 @@ func (c *natCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *natCollector) NatInterfaces(client *rpc.Client) ([]*NatInterface, error) { var x = NatRpc{} - err := client.RunCommandAndParse("show services nat statistics", &x) +// err := client.RunCommandAndParse("show services nat statistics", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return nil, err } diff --git a/ospf/collector.go b/ospf/collector.go index 86ac8ffe..137d3c22 100644 --- a/ospf/collector.go +++ b/ospf/collector.go @@ -64,10 +64,12 @@ func (c *ospfCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *ospfCollector) collectOSPFMetrics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = OspfRpc{} var cmd strings.Builder - cmd.WriteString("show ospf overview") - if c.LogicalSystem != "" { - cmd.WriteString(" logical-system " + c.LogicalSystem) - } +// cmd.WriteString("show ospf overview") +// cmd.WriteString("show ospf overview") +// if c.LogicalSystem != "" { +// cmd.WriteString(" logical-system " + c.LogicalSystem) +// } + cmd.WriteString("") err := client.RunCommandAndParse(cmd.String(), &x) if err != nil { diff --git a/power/collector.go b/power/collector.go index 2ade7145..d7e5e0e8 100644 --- a/power/collector.go +++ b/power/collector.go @@ -88,7 +88,8 @@ func (c *powerCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric } var x = RpcReply{} - err := client.RunCommandAndParseWithParser("show chassis power", func(b []byte) error { +// err := client.RunCommandAndParseWithParser("show chassis power", func(b []byte) error { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { return parseXML(b, &x) }) if err != nil { diff --git a/route/collector.go b/route/collector.go index fa81705a..61c5cf6d 100644 --- a/route/collector.go +++ b/route/collector.go @@ -52,7 +52,8 @@ func (*routeCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *routeCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RouteRpc{} - err := client.RunCommandAndParse("show route summary", &x) +// err := client.RunCommandAndParse("show route summary", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } diff --git a/routingengine/collector.go b/routingengine/collector.go index c7c7df6c..89228770 100644 --- a/routingengine/collector.go +++ b/routingengine/collector.go @@ -168,7 +168,8 @@ func (*routingEngineCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *routingEngineCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpcReply{} - err := client.RunCommandAndParseWithParser("show chassis routing-engine", func(b []byte) error { +// err := client.RunCommandAndParseWithParser("show chassis routing-engine", func(b []byte) error { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { return parseXML(b, &x) }) if err != nil { diff --git a/rpc/rpc_client.go b/rpc/rpc_client.go index c5e55edf..8dd10f6b 100644 --- a/rpc/rpc_client.go +++ b/rpc/rpc_client.go @@ -2,7 +2,6 @@ package rpc import ( "encoding/xml" - "fmt" "log" @@ -41,7 +40,7 @@ func (c *Client) RunCommandAndParseWithParser(cmd string, parser Parser) error { log.Printf("Running command on %s: %s\n", c.conn.Host(), cmd) } - b, err := c.conn.RunCommand(fmt.Sprintf("%s | display xml", cmd)) + b, err := c.conn.RunCommand(cmd) if err != nil { return err } diff --git a/rpm/collector.go b/rpm/collector.go index cfe9f863..db5decea 100644 --- a/rpm/collector.go +++ b/rpm/collector.go @@ -68,7 +68,8 @@ func (c *rpmCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *rpmCollector) collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RPMRPC{} - err := client.RunCommandAndParse("show services rpm probe-results", &x) +// err := client.RunCommandAndParse("show services rpm probe-results", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } diff --git a/storage/collector.go b/storage/collector.go index 26869296..d9791ba2 100644 --- a/storage/collector.go +++ b/storage/collector.go @@ -51,7 +51,8 @@ func (*storageCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *storageCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpcReply{} - err := client.RunCommandAndParseWithParser("show system storage", func(b []byte) error { +// err := client.RunCommandAndParseWithParser("show system storage", func(b []byte) error { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { return parseXML(b, &x) }) if err != nil { diff --git a/system/collector.go b/system/collector.go index 6332492b..1376061e 100644 --- a/system/collector.go +++ b/system/collector.go @@ -162,7 +162,8 @@ func (c *systemCollector) CollectSystem(client *rpc.Client, ch chan<- prometheus r = new(BuffersRPC) - err = client.RunCommandAndParse("show system buffers", r) +// err = client.RunCommandAndParse("show system buffers", r) + err = client.RunCommandAndParse("", r) if err != nil { return err } @@ -277,7 +278,8 @@ func (c *systemCollector) CollectSystem(client *rpc.Client, ch chan<- prometheus // system information r2 = new(SystemInformationRPC) - err = client.RunCommandAndParse("show system information", r2) +// err = client.RunCommandAndParse("show system information", r2) + err = client.RunCommandAndParse("", r2) if err != nil { return err } diff --git a/vpws/collector.go b/vpws/collector.go index 7aa5c18b..4ca023e8 100644 --- a/vpws/collector.go +++ b/vpws/collector.go @@ -55,7 +55,8 @@ func (*vpwsCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *vpwsCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = vpwsRpc{} - err := client.RunCommandAndParse("show evpn vpws-instance", &x) +// err := client.RunCommandAndParse("show evpn vpws-instance", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } diff --git a/vrrp/collector.go b/vrrp/collector.go index 8f1d2bbf..5652d77e 100644 --- a/vrrp/collector.go +++ b/vrrp/collector.go @@ -45,7 +45,8 @@ func (c *vrrpCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, } var x = VrrpRpc{} - err := client.RunCommandAndParse("show vrrp summary", &x) +// err := client.RunCommandAndParse("show vrrp summary", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } From b3b7b77c872c6bcff69620baabb013982e6bbce1 Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Fri, 13 May 2022 17:13:09 +0200 Subject: [PATCH 02/23] more to netconf converted --- environment/collector.go | 2 +- fpc/collector.go | 6 ++++-- interfacediagnostics/collector.go | 3 ++- interfacelabels/dynamic_labels.go | 2 +- ldp/collector.go | 3 ++- rpc/rpc_client.go | 5 +++-- system/collector.go | 3 ++- 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/environment/collector.go b/environment/collector.go index 33a5e126..41ab88e5 100644 --- a/environment/collector.go +++ b/environment/collector.go @@ -89,7 +89,7 @@ func (c *environmentCollector) environmentItems(client *rpc.Client, ch chan<- pr // gather satellite data if client.Satellite { var y = RpcReply{} - err = client.RunCommandAndParseWithParser("show chassis environment satellite", func(b []byte) error { + err = client.RunCommandAndParseWithParser("", func(b []byte) error { if string(b[:]) == "\nerror: syntax error, expecting : satellite\n" { log.Printf("system doesn't seem to have satellite enabled") return nil diff --git a/fpc/collector.go b/fpc/collector.go index 9700246a..b92ed49f 100644 --- a/fpc/collector.go +++ b/fpc/collector.go @@ -122,7 +122,8 @@ func (c *fpcCollector) CollectFPCDetail(client *rpc.Client, ch chan<- prometheus // Collect collects metrics from JunOS func (c *fpcCollector) CollectFPC(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { r := RpcReply{} - err := client.RunCommandAndParseWithParser("show chassis fpc", func(b []byte) error { +// err := client.RunCommandAndParseWithParser("show chassis fpc", func(b []byte) error { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { return parseXML(b, &r) }) if err != nil { @@ -140,7 +141,8 @@ func (c *fpcCollector) CollectFPC(client *rpc.Client, ch chan<- prometheus.Metri func (c *fpcCollector) CollectPIC(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { r := RpcReply{} - err := client.RunCommandAndParseWithParser("show chassis fpc pic-status", func(b []byte) error { +// err := client.RunCommandAndParseWithParser("show chassis fpc pic-status", func(b []byte) error { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { return parseXML(b, &r) }) if err != nil { diff --git a/interfacediagnostics/collector.go b/interfacediagnostics/collector.go index cda020fd..448d77cb 100644 --- a/interfacediagnostics/collector.go +++ b/interfacediagnostics/collector.go @@ -280,7 +280,8 @@ func (c *interfaceDiagnosticsCollector) interfaceDiagnosticsSatellite(client *rp // // workaround: go through all lines of the XML and remove identical, consecutive lines - err := client.RunCommandAndParseWithParser("show interfaces diagnostics optics satellite", func(b []byte) error { +// err := client.RunCommandAndParseWithParser("show interfaces diagnostics optics satellite", func(b []byte) error { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { var ( lines []string = strings.Split(string(b[:]), "\n") lineIndex int diff --git a/interfacelabels/dynamic_labels.go b/interfacelabels/dynamic_labels.go index 4cde48ad..750b4d3f 100644 --- a/interfacelabels/dynamic_labels.go +++ b/interfacelabels/dynamic_labels.go @@ -47,7 +47,7 @@ type interfaceLabel struct { // CollectDescriptions collects labels from descriptions func (l *DynamicLabels) CollectDescriptions(device *connector.Device, client *rpc.Client, ifDescReg *regexp.Regexp) error { r := &InterfaceRPC{} - err := client.RunCommandAndParse("", r) + err := client.RunCommandAndParse("", r) if err != nil { return errors.Wrap(err, "could not retrieve interface descriptions for "+device.Host) } diff --git a/ldp/collector.go b/ldp/collector.go index c4ab50a0..fb61f30e 100644 --- a/ldp/collector.go +++ b/ldp/collector.go @@ -76,7 +76,8 @@ func (c *ldpCollector) collectLDPMetrics(client *rpc.Client, ch chan<- prometheu func (c *ldpCollector) collectLDPSessions(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = LDPSessionRpc{} - err := client.RunCommandAndParse("show ldp session", &x) +// err := client.RunCommandAndParse("show ldp session", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } diff --git a/rpc/rpc_client.go b/rpc/rpc_client.go index 8dd10f6b..56529f09 100644 --- a/rpc/rpc_client.go +++ b/rpc/rpc_client.go @@ -2,7 +2,7 @@ package rpc import ( "encoding/xml" - + "bytes" "log" "github.com/czerwonk/junos_exporter/connector" @@ -30,7 +30,8 @@ func NewClient(ssh *connector.SSHConnection) *Client { // RunCommandAndParse runs a command on JunOS and unmarshals the XML result func (c *Client) RunCommandAndParse(cmd string, obj interface{}) error { return c.RunCommandAndParseWithParser(cmd, func(b []byte) error { - return xml.Unmarshal(b, obj) + //in junos the xml interfaces contains line returns in the values + return xml.Unmarshal(bytes.ReplaceAll(b, []byte("\n"), []byte("")), obj) }) } diff --git a/system/collector.go b/system/collector.go index 1376061e..750180b5 100644 --- a/system/collector.go +++ b/system/collector.go @@ -300,7 +300,8 @@ func (c *systemCollector) CollectSystem(client *rpc.Client, ch chan<- prometheus // system information of satellites r3 = new(SatelliteChassisRPC) - err = client.RunCommandAndParse("show chassis satellite detail", r3) +// err = client.RunCommandAndParse("show chassis satellite detail", r3) + err = client.RunCommandAndParse("", r3) // there are various error messages when satellite is not enabled; thus here we just ignore the error and continue if err == nil { for i = range r3.SatelliteInfo.Satellite { From 1c272b98ac7eeab1e7bcb027c82d2bd0188c23bc Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Sat, 14 May 2022 07:18:41 +0200 Subject: [PATCH 03/23] converting more commands --- accounting/collector.go | 6 ++++-- firewall/collector.go | 3 ++- ipsec/collector.go | 6 ++++-- mac/collector.go | 3 ++- nat/collector.go | 3 ++- nat2/collector.go | 6 ++++-- ospf/collector.go | 9 +++++---- rpki/collector.go | 6 ++++-- security/collector.go | 3 ++- 9 files changed, 29 insertions(+), 16 deletions(-) diff --git a/accounting/collector.go b/accounting/collector.go index a0c167bc..e91b1d25 100644 --- a/accounting/collector.go +++ b/accounting/collector.go @@ -96,7 +96,8 @@ func (c *accountingCollector) Collect(client *rpc.Client, ch chan<- prometheus.M func (c *accountingCollector) accountingFlows(client *rpc.Client) (*AccountingFlow, error) { var x = AccountingFlowRpc{} - err := client.RunCommandAndParse("show services accounting flow inline-jflow", &x) +// err := client.RunCommandAndParse("show services accounting flow inline-jflow", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return nil, err } @@ -119,7 +120,8 @@ func (c *accountingCollector) accountingFlows(client *rpc.Client) (*AccountingFl func (c *accountingCollector) accountingFailures(client *rpc.Client) (*AccountingError, error) { var x = AccountingFlowErrorRpc{} - err := client.RunCommandAndParse("show services accounting errors inline-jflow fpc-slot 0", &x) +// err := client.RunCommandAndParse("show services accounting errors inline-jflow fpc-slot 0", &x) + err := client.RunCommandAndParse("0", &x) if err != nil { return nil, err } diff --git a/firewall/collector.go b/firewall/collector.go index 16bd7980..3b2dc405 100644 --- a/firewall/collector.go +++ b/firewall/collector.go @@ -48,7 +48,8 @@ func (*firewallCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *firewallCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = FirewallRpc{} - err := client.RunCommandAndParse("show firewall filter regex .*", &x) +// err := client.RunCommandAndParse("show firewall filter regex .*", &x) + err := client.RunCommandAndParse(".*", &x) if err != nil { return err } diff --git a/ipsec/collector.go b/ipsec/collector.go index aae8b882..856aeef3 100644 --- a/ipsec/collector.go +++ b/ipsec/collector.go @@ -49,7 +49,8 @@ func (*ipsecCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *ipsecCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpcReply{} - err := client.RunCommandAndParse("show security ipsec security-associations", &x) +// err := client.RunCommandAndParse("show security ipsec security-associations", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } @@ -64,7 +65,8 @@ func (c *ipsecCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric } var conf = ConfigurationSecurityIpsec{} - err = client.RunCommandAndParse("show configuration security ipsec", &conf) +// err = client.RunCommandAndParse("show configuration security ipsec", &conf) + err = client.RunCommandAndParse("", &conf) if err != nil { return err } diff --git a/mac/collector.go b/mac/collector.go index 64d25ea7..05a7b22d 100644 --- a/mac/collector.go +++ b/mac/collector.go @@ -47,7 +47,8 @@ func (*macCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *macCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = MacRpc{} - err := client.RunCommandAndParse("show ethernet-switching table summary", &x) +// err := client.RunCommandAndParse("show ethernet-switching table summary", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } diff --git a/nat/collector.go b/nat/collector.go index 650695a8..7ca3df47 100644 --- a/nat/collector.go +++ b/nat/collector.go @@ -506,7 +506,8 @@ func (c *natCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *natCollector) NatInterfaces(client *rpc.Client) ([]*NatInterface, error) { var x = NatRpc{} - err := client.RunCommandAndParse("show services nat statistics", &x) +// err := client.RunCommandAndParse("show services nat statistics", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return nil, err } diff --git a/nat2/collector.go b/nat2/collector.go index 1c14a930..fb4761d4 100644 --- a/nat2/collector.go +++ b/nat2/collector.go @@ -321,7 +321,8 @@ func (*natCollector) collectForInterface(s *NatInterface, ch chan<- prometheus.M func (c *natCollector) SrcNatPools(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) ([]SrcNatPool, error) { var x = SrcNatPoolRpc{} - err := client.RunCommandAndParse("show services nat source pool all", &x) +// err := client.RunCommandAndParse("show services nat source pool all", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return nil, err } @@ -393,7 +394,8 @@ func (c *natCollector) collectForSrcNatPool(s []SrcNatPool, ch chan<- prometheus func (c *natCollector) ServiceSetsCpuInterfaces(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) ([]*ServiceSetsCpuInterface, error) { var x = ServiceSetsCpuRpc{} - err := client.RunCommandAndParse("show services service-sets cpu-usage", &x) +// err := client.RunCommandAndParse("show services service-sets cpu-usage", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return nil, err } diff --git a/ospf/collector.go b/ospf/collector.go index 137d3c22..926b91f2 100644 --- a/ospf/collector.go +++ b/ospf/collector.go @@ -96,10 +96,11 @@ func (c *ospfCollector) collectOSPFMetrics(client *rpc.Client, ch chan<- prometh func (c *ospfCollector) collectOSPFv3Metrics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = Ospf3Rpc{} var cmd strings.Builder - cmd.WriteString("show ospf3 overview") - if c.LogicalSystem != "" { - cmd.WriteString(" logical-system " + c.LogicalSystem) - } +// cmd.WriteString("show ospf3 overview") + cmd.WriteString("") +// if c.LogicalSystem != "" { +// cmd.WriteString(" logical-system " + c.LogicalSystem) +// } err := client.RunCommandAndParse(cmd.String(), &x) if err != nil { diff --git a/rpki/collector.go b/rpki/collector.go index bbf5cad8..45b8579e 100644 --- a/rpki/collector.go +++ b/rpki/collector.go @@ -79,7 +79,8 @@ func (c *rpkiCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *rpkiCollector) collectSessions(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpkiSessionRpc{} - err := client.RunCommandAndParse("show validation session", &x) +// err := client.RunCommandAndParse("show validation session", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } @@ -129,7 +130,8 @@ func (c *rpkiCollector) collectForSession(s RpkiSession, ch chan<- prometheus.Me func (c *rpkiCollector) collectStatistics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpkiStatisticsRpc{} - err := client.RunCommandAndParse("show validation statistics", &x) +// err := client.RunCommandAndParse("show validation statistics", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } diff --git a/security/collector.go b/security/collector.go index 4c6eb3b5..12f88797 100644 --- a/security/collector.go +++ b/security/collector.go @@ -63,7 +63,8 @@ func (*securityCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *securityCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpcReply{} - err := client.RunCommandAndParse("show security monitoring", &x) +// err := client.RunCommandAndParse("show security monitoring", &x) + err := client.RunCommandAndParse("", &x) if err != nil { return err } From dc46af3ccdabe45948a57c4f134237e6e030e9b8 Mon Sep 17 00:00:00 2001 From: lethalwp <32979360+lethalwp@users.noreply.github.com> Date: Mon, 16 May 2022 11:00:30 +0200 Subject: [PATCH 04/23] correction on session pointer is connected --- connector/connection.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/connection.go b/connector/connection.go index 157cc014..8cd7d303 100644 --- a/connector/connection.go +++ b/connector/connection.go @@ -36,7 +36,7 @@ func (c *SSHConnection) RunCommand(cmd string) ([]byte, error) { } func (c *SSHConnection) isConnected() bool { - return c.conn != nil + return c.session != nil } func (c *SSHConnection) terminate() { From 89911caf9cdcac149d991016fbd0e30ee244c829 Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Tue, 17 May 2022 08:41:12 +0200 Subject: [PATCH 05/23] netconf firewall and ipsec commands fixes --- firewall/collector.go | 2 +- ipsec/collector.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/firewall/collector.go b/firewall/collector.go index 3b2dc405..6205546b 100644 --- a/firewall/collector.go +++ b/firewall/collector.go @@ -49,7 +49,7 @@ func (*firewallCollector) Describe(ch chan<- *prometheus.Desc) { func (c *firewallCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = FirewallRpc{} // err := client.RunCommandAndParse("show firewall filter regex .*", &x) - err := client.RunCommandAndParse(".*", &x) + err := client.RunCommandAndParse(".*", &x) if err != nil { return err } diff --git a/ipsec/collector.go b/ipsec/collector.go index 856aeef3..a56929fc 100644 --- a/ipsec/collector.go +++ b/ipsec/collector.go @@ -66,7 +66,7 @@ func (c *ipsecCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric var conf = ConfigurationSecurityIpsec{} // err = client.RunCommandAndParse("show configuration security ipsec", &conf) - err = client.RunCommandAndParse("", &conf) + err = client.RunCommandAndParse("", &conf) if err != nil { return err } From 60c785bdad92c8edb8444e8f1df78d2e7d7fc346 Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Tue, 17 May 2022 09:03:02 +0200 Subject: [PATCH 06/23] for merge --- go.mod | 4 ++-- go.sum | 20 ++++---------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index fff5ec1e..0208dd40 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/czerwonk/junos_exporter go 1.18 require ( + github.com/Juniper/go-netconf v0.1.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.12.2 github.com/prometheus/client_model v0.2.0 @@ -13,16 +14,15 @@ require ( ) require ( - github.com/Juniper/go-netconf v0.1.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b // indirect github.com/prometheus/common v0.34.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b // indirect golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99 // indirect diff --git a/go.sum b/go.sum index 599c3942..70e9bbd9 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/Juniper/go-netconf v0.1.1 h1:5fx/T7L2Fwq51UnESPOP1CXgGCs7IYxR/pnyC5quu/k= -github.com/Juniper/go-netconf v0.1.1/go.mod h1:2Fy6tQTWnL//D/Ll1hb0RYXN4jndcTyneRn6xj5E1VE= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= @@ -35,7 +33,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= ->>>>>>> 8c2e5687bda8dc255074b050adccf77d7c02a456 +github.com/Juniper/go-netconf v0.1.1 h1:5fx/T7L2Fwq51UnESPOP1CXgGCs7IYxR/pnyC5quu/k= +github.com/Juniper/go-netconf v0.1.1/go.mod h1:2Fy6tQTWnL//D/Ll1hb0RYXN4jndcTyneRn6xj5E1VE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -112,7 +111,6 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -143,10 +141,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -198,23 +194,18 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -<<<<<<< HEAD -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b h1:VfPXB/wCGGt590QhD1bOpv2J/AmC/RJNTg/Q59HKSB0= -github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b/go.mod h1:IZpXDfkJ6tWD3PhBK5YzgQT+xJWh7OsdwiG8hA2MkO4= -======= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b h1:VfPXB/wCGGt590QhD1bOpv2J/AmC/RJNTg/Q59HKSB0= +github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b/go.mod h1:IZpXDfkJ6tWD3PhBK5YzgQT+xJWh7OsdwiG8hA2MkO4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= ->>>>>>> 8c2e5687bda8dc255074b050adccf77d7c02a456 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -342,7 +333,6 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a h1:N2T1jUrTQE9Re6TFF5PhvEHXHCguynGhKjWVsIUt5cY= golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -397,7 +387,6 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -479,7 +468,6 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 300f3984cfb79133051485ec669484bf206bde71 Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Tue, 17 May 2022 10:17:02 +0200 Subject: [PATCH 07/23] rename lacp labels to match other interface output --- lacp/collector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lacp/collector.go b/lacp/collector.go index 00f9b9df..04cdb4c5 100644 --- a/lacp/collector.go +++ b/lacp/collector.go @@ -21,7 +21,7 @@ var ( ) func init() { - l := []string{"target", "name", "member"} + l := []string{"target", "aggregate", "name"} lacpMuxState = prometheus.NewDesc(prefix+"muxstate", "lacp mux state (1: detached, 2: waiting, 3: attached, 4: collecting, 5: distributing, 6: collecting distribuging)", l, nil) } From 012a62c3789d516551d543988ee18b7e1845eee1 Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Wed, 18 May 2022 12:05:22 +0200 Subject: [PATCH 08/23] ipsec fix --- ipsec/rpc.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ipsec/rpc.go b/ipsec/rpc.go index cdbc8b23..2c46a985 100644 --- a/ipsec/rpc.go +++ b/ipsec/rpc.go @@ -55,7 +55,7 @@ type ConfigurationSecurityIpsec struct { Configuration struct { Security struct { Ipsec struct { - Proposal struct { + Proposal []struct { Text string `xml:",chardata"` Name string `xml:"name"` Protocol string `xml:"protocol"` @@ -63,7 +63,7 @@ type ConfigurationSecurityIpsec struct { EncryptionAlgorithm string `xml:"encryption-algorithm"` LifetimeSeconds string `xml:"lifetime-seconds"` } `xml:"proposal"` - Policy struct { + Policy []struct { Name string `xml:"name"` Proposals string `xml:"proposals"` } `xml:"policy"` @@ -78,5 +78,5 @@ type ConfigurationSecurityIpsec struct { } `xml:"vpn"` } `xml:"ipsec"` } `xml:"security"` - } `xml:"configuration"` + } `xml:"data>configuration"` } From a62d0eae928b6baf1ef68d4ddb053f96140344e0 Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Tue, 24 May 2022 12:09:14 +0200 Subject: [PATCH 09/23] go back to the previous ssh paradigm, with a better keepalive --- connector/transport.go | 183 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 connector/transport.go diff --git a/connector/transport.go b/connector/transport.go new file mode 100644 index 00000000..bfaec9ee --- /dev/null +++ b/connector/transport.go @@ -0,0 +1,183 @@ +package connector + +import ( + "bytes" + "encoding/xml" + "fmt" + "io" + "regexp" + "github.com/Juniper/go-netconf/netconf" + +) + +const ( + // msgSeperator is used to separate sent messages via NETCONF + msgSeperator = "]]>]]>" + msgSeperator_v11 = "\n##\n" +) + + +// Transport interface defines what characterisitics make up a NETCONF transport +// layer object. +type Transport interface { + Send([]byte) error + Receive() ([]byte, error) + Close() error + ReceiveHello() (*netconf.HelloMessage, error) + SendHello(*netconf.HelloMessage) error + SetVersion(version string) +} + +type transportBasicIO struct { + io.ReadWriteCloser + //new add + version string +} + +func (t *transportBasicIO) SetVersion(version string) { + t.version = version +} + +// Sends a well formated NETCONF rpc message as a slice of bytes adding on the +// nessisary framining messages. +func (t *transportBasicIO) Send(data []byte) error { + var seperator []byte + var dataInfo []byte + //headlen := 0 + if t.version == "v1.1" { + seperator = append(seperator, []byte(msgSeperator_v11)...) + } else { + seperator = append(seperator, []byte(msgSeperator)...) + } + + if t.version == "v1.1" { + header := fmt.Sprintf("\n#%d\n", len(string(data))) + dataInfo = append(dataInfo, header...) + //t.Write([]byte(header)) + //headlen = len([]byte(header)) + } + dataInfo = append(dataInfo, data...) + dataInfo = append(dataInfo, seperator...) + _, err := t.Write(dataInfo) + + return err +} + +func (t *transportBasicIO) Receive() ([]byte, error) { + var seperator []byte + if t.version == "v1.1" { + seperator = append(seperator, []byte(msgSeperator_v11)...) + } else { + seperator = append(seperator, []byte(msgSeperator)...) + } + return t.WaitForBytes([]byte(seperator)) +} + +func (t *transportBasicIO) SendHello(hello *netconf.HelloMessage) error { + val, err := xml.Marshal(hello) + if err != nil { + return err + } + + header := []byte(xml.Header) + val = append(header, val...) + err = t.Send(val) + return err +} + +func (t *transportBasicIO) ReceiveHello() (*netconf.HelloMessage, error) { + hello := new(netconf.HelloMessage) + + val, err := t.Receive() + if err != nil { + return hello, err + } + + err = xml.Unmarshal(val, hello) + return hello, err +} + +func (t *transportBasicIO) Writeln(b []byte) (int, error) { + t.Write(b) + t.Write([]byte("\n")) + return 0, nil +} + +func (t *transportBasicIO) WaitForFunc(f func([]byte) (int, error)) ([]byte, error) { + var out bytes.Buffer + buf := make([]byte, 8192) + + pos := 0 + for { + n, err := t.Read(buf[pos : pos+(len(buf)/2)]) + if err != nil { + if err != io.EOF { + return nil, err + } + break + } + + if n > 0 { + end, err := f(buf[0 : pos+n]) + if err != nil { + return nil, err + } + + if end > -1 { + out.Write(buf[0:end]) + return out.Bytes(), nil + } + + if pos > 0 { + out.Write(buf[0:pos]) + copy(buf, buf[pos:pos+n]) + } + + pos = n + } + } + + return nil, fmt.Errorf("WaitForFunc failed") +} + +func (t *transportBasicIO) WaitForBytes(b []byte) ([]byte, error) { + return t.WaitForFunc(func(buf []byte) (int, error) { + return bytes.Index(buf, b), nil + }) +} + +func (t *transportBasicIO) WaitForString(s string) (string, error) { + out, err := t.WaitForBytes([]byte(s)) + if out != nil { + return string(out), err + } + return "", err +} + +func (t *transportBasicIO) WaitForRegexp(re *regexp.Regexp) ([]byte, [][]byte, error) { + var matches [][]byte + out, err := t.WaitForFunc(func(buf []byte) (int, error) { + loc := re.FindSubmatchIndex(buf) + if loc != nil { + for i := 2; i < len(loc); i += 2 { + matches = append(matches, buf[loc[i]:loc[i+1]]) + } + return loc[1], nil + } + return -1, nil + }) + return out, matches, err +} + +// ReadWriteCloser represents a combined IO Reader and WriteCloser +type ReadWriteCloser struct { + io.Reader + io.WriteCloser +} + +// NewReadWriteCloser creates a new combined IO Reader and Write Closer from the +// provided objects +func NewReadWriteCloser(r io.Reader, w io.WriteCloser) *ReadWriteCloser { + return &ReadWriteCloser{r, w} +} + From 7683d025fa6c2ed717ecca0f9211641e433ef948 Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Tue, 24 May 2022 12:17:11 +0200 Subject: [PATCH 10/23] go back to the previous ssh paradigm, with a better keepalive --- connector/connection.go | 47 ++++++++++++++++++++++----- connector/connection_manager.go | 57 ++++++++++++++++++++++++++------- 2 files changed, 85 insertions(+), 19 deletions(-) diff --git a/connector/connection.go b/connector/connection.go index 8cd7d303..c34358a3 100644 --- a/connector/connection.go +++ b/connector/connection.go @@ -6,28 +6,59 @@ import ( "github.com/pkg/errors" + "golang.org/x/crypto/ssh" "github.com/Juniper/go-netconf/netconf" ) // SSHConnection encapsulates the connection to the device type SSHConnection struct { device *Device - session *netconf.Session + client *ssh.Client conn net.Conn mu sync.Mutex done chan struct{} } +type TransportSSH struct { + transportBasicIO + sshClient *ssh.Client + sshSession *ssh.Session +} + + // RunCommand runs a command against the device func (c *SSHConnection) RunCommand(cmd string) ([]byte, error) { c.mu.Lock() defer c.mu.Unlock() + var err error - if c.session == nil { + if c.client == nil { return nil, errors.New("not connected") } - reply, err := c.session.Exec(netconf.RawMethod(cmd)) + t := &TransportSSH{} + t.sshSession, err = c.client.NewSession() + if err != nil { + return nil, errors.Wrap(err, "could not open session") + } + defer t.sshSession.Close() + + writer, err := t.sshSession.StdinPipe() + if err != nil { + return nil, errors.Wrap(err, "could not open session stdin") + } + + reader, err := t.sshSession.StdoutPipe() + if err != nil { + return nil, errors.Wrap(err, "could not open session stdout") + } + + t.ReadWriteCloser = netconf.NewReadWriteCloser(reader, writer) + t.sshSession.RequestSubsystem("netconf") + session := netconf.NewSession(t) + + + reply, err := session.Exec(netconf.RawMethod(cmd)) if err != nil { return nil, errors.Wrap(err, "could not run command") } @@ -36,7 +67,7 @@ func (c *SSHConnection) RunCommand(cmd string) ([]byte, error) { } func (c *SSHConnection) isConnected() bool { - return c.session != nil + return c.conn != nil } func (c *SSHConnection) terminate() { @@ -45,7 +76,7 @@ func (c *SSHConnection) terminate() { c.conn.Close() - c.session = nil + c.client = nil c.conn = nil } @@ -53,13 +84,13 @@ func (c *SSHConnection) close() { c.mu.Lock() defer c.mu.Unlock() - if c.session != nil { - c.session.Close() + if c.client != nil { + c.client.Close() } c.done <- struct{}{} c.conn = nil - c.session = nil + c.client = nil } // Host returns the hostname of the connected device diff --git a/connector/connection_manager.go b/connector/connection_manager.go index c15b4f9a..2d09603c 100644 --- a/connector/connection_manager.go +++ b/connector/connection_manager.go @@ -5,12 +5,12 @@ import ( "strings" "sync" "time" + "fmt" log "github.com/sirupsen/logrus" "github.com/pkg/errors" "golang.org/x/crypto/ssh" - "github.com/Juniper/go-netconf/netconf" ) const timeoutInSeconds = 5 @@ -26,6 +26,14 @@ func WithReconnectInterval(d time.Duration) Option { } } +// WithKeepAliveInterval sets the keep alive interval (default 10 seconds) +func WithKeepAliveInterval(d time.Duration) Option { + return func(m *SSHConnectionManager) { + m.keepAliveInterval = d + } +} + + // WithKeepAliveTimeout sets the timeout after an ssh connection to be determined dead (default 15 seconds) func WithKeepAliveTimeout(d time.Duration) Option { return func(m *SSHConnectionManager) { @@ -38,8 +46,8 @@ func WithKeepAliveTimeout(d time.Duration) Option { type SSHConnectionManager struct { connections map[string]*SSHConnection reconnectInterval time.Duration + keepAliveInterval time.Duration keepAliveTimeout time.Duration - mu sync.Mutex } @@ -48,7 +56,8 @@ func NewConnectionManager(opts ...Option) *SSHConnectionManager { m := &SSHConnectionManager{ connections: make(map[string]*SSHConnection), reconnectInterval: 30 * time.Second, - keepAliveTimeout: 20 * time.Second, + keepAliveInterval: 10 * time.Second, + keepAliveTimeout: 15 * time.Second, } for _, opt := range opts { @@ -75,23 +84,26 @@ func (m *SSHConnectionManager) Connect(device *Device) (*SSHConnection, error) { } func (m *SSHConnectionManager) connect(device *Device) (*SSHConnection, error) { - session, err := m.connectToDevice(device) + client, conn, err := m.connectToDevice(device) if err != nil { return nil, err } + fmt.Println(client,conn) c := &SSHConnection{ - session: session, + conn: conn, + client: client, device: device, done: make(chan struct{}), } + go m.keepAlive(c) m.connections[device.Host] = c return c, nil } -func (m *SSHConnectionManager) connectToDevice(device *Device) (*netconf.Session, error) { +func (m *SSHConnectionManager) connectToDevice(device *Device) (*ssh.Client, net.Conn, error) { cfg := &ssh.ClientConfig{ HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: timeoutInSeconds * time.Second, @@ -101,12 +113,17 @@ func (m *SSHConnectionManager) connectToDevice(device *Device) (*netconf.Session host := m.tcpAddressForHost(device.Host) - session,err := netconf.DialSSHTimeout(host,cfg,m.keepAliveTimeout) + conn, err := net.DialTimeout("tcp", host, cfg.Timeout) if err != nil { - return nil, errors.Wrap(err, "could not connect to device") + return nil, nil, errors.Wrap(err, "could not open tcp connection") } - return session, nil + c, chans, reqs, err := ssh.NewClientConn(conn, host, cfg) + if err != nil { + return nil, nil, errors.Wrap(err, "could not connect to device") + } + + return ssh.NewClient(c, chans, reqs), conn, nil } func (m *SSHConnectionManager) tcpAddressForHost(host string) string { @@ -136,12 +153,30 @@ func (m *SSHConnectionManager) formatHost(host string) string { return "[" + host + "]" } +func (m *SSHConnectionManager) keepAlive(connection *SSHConnection) { + for { + select { + case <-time.After(m.keepAliveInterval): + log.Debugf("Sending keepalive for ") + connection.conn.SetDeadline(time.Now().Add(m.keepAliveTimeout)) + _, _, err := connection.client.SendRequest("keepalive@golang.org", true, nil) + if err != nil { + log.Infof("Lost connection to %s (%v). Trying to reconnect...", connection.device, err) + connection.terminate() + m.reconnect(connection) + } + case <-connection.done: + return + } + } +} func (m *SSHConnectionManager) reconnect(connection *SSHConnection) { for { - session, err := m.connectToDevice(connection.device) + client, conn, err := m.connectToDevice(connection.device) if err == nil { - connection.session = session + connection.client = client + connection.conn = conn return } From 96741fc930d7824e506e5087294002eeed2fd6c2 Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Tue, 24 May 2022 12:21:27 +0200 Subject: [PATCH 11/23] cosmeting, realign --- connector/connection_manager.go | 34 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/connector/connection_manager.go b/connector/connection_manager.go index 2d09603c..ae1832fc 100644 --- a/connector/connection_manager.go +++ b/connector/connection_manager.go @@ -5,7 +5,6 @@ import ( "strings" "sync" "time" - "fmt" log "github.com/sirupsen/logrus" @@ -88,10 +87,9 @@ func (m *SSHConnectionManager) connect(device *Device) (*SSHConnection, error) { if err != nil { return nil, err } - fmt.Println(client,conn) c := &SSHConnection{ - conn: conn, + conn: conn, client: client, device: device, done: make(chan struct{}), @@ -154,21 +152,21 @@ func (m *SSHConnectionManager) formatHost(host string) string { } func (m *SSHConnectionManager) keepAlive(connection *SSHConnection) { - for { - select { - case <-time.After(m.keepAliveInterval): - log.Debugf("Sending keepalive for ") - connection.conn.SetDeadline(time.Now().Add(m.keepAliveTimeout)) - _, _, err := connection.client.SendRequest("keepalive@golang.org", true, nil) - if err != nil { - log.Infof("Lost connection to %s (%v). Trying to reconnect...", connection.device, err) - connection.terminate() - m.reconnect(connection) - } - case <-connection.done: - return - } - } + for { + select { + case <-time.After(m.keepAliveInterval): + log.Debugf("Sending keepalive for ") + connection.conn.SetDeadline(time.Now().Add(m.keepAliveTimeout)) + _, _, err := connection.client.SendRequest("keepalive@golang.org", true, nil) + if err != nil { + log.Infof("Lost connection to %s (%v). Trying to reconnect...", connection.device, err) + connection.terminate() + m.reconnect(connection) + } + case <-connection.done: + return + } + } } func (m *SSHConnectionManager) reconnect(connection *SSHConnection) { From a37425661df3ad5d9f49c3facff94dc9d29cfa1c Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Tue, 24 May 2022 12:23:09 +0200 Subject: [PATCH 12/23] cosmeting, realign --- connector/connection_manager.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/connector/connection_manager.go b/connector/connection_manager.go index ae1832fc..88720df9 100644 --- a/connector/connection_manager.go +++ b/connector/connection_manager.go @@ -158,11 +158,11 @@ func (m *SSHConnectionManager) keepAlive(connection *SSHConnection) { log.Debugf("Sending keepalive for ") connection.conn.SetDeadline(time.Now().Add(m.keepAliveTimeout)) _, _, err := connection.client.SendRequest("keepalive@golang.org", true, nil) - if err != nil { - log.Infof("Lost connection to %s (%v). Trying to reconnect...", connection.device, err) - connection.terminate() - m.reconnect(connection) - } + if err != nil { + log.Infof("Lost connection to %s (%v). Trying to reconnect...", connection.device, err) + connection.terminate() + m.reconnect(connection) + } case <-connection.done: return } From 5b9a3dde90137f928fdef7ce1b14cda5c6285364 Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Tue, 24 May 2022 13:18:51 +0200 Subject: [PATCH 13/23] adding netconf as a feature #1 --- accounting/collector.go | 14 ++-- alarm/collector.go | 11 ++- bfd/collector.go | 14 ++-- bgp/collector.go | 25 ++++--- config/config.go | 2 + environment/collector.go | 87 ++++++++++++++++------- firewall/collector.go | 15 ++-- fpc/collector.go | 21 ++++-- interfacediagnostics/collector.go | 113 +++++++++++++++++++++--------- interfacelabels/dynamic_labels.go | 13 +++- interfacequeue/collector.go | 14 ++-- interfaces/collector.go | 14 ++-- ipsec/collector.go | 29 +++++--- ipsec/rpc.go | 61 ++++++++++++++++ isis/collector.go | 15 ++-- junos_collector.go | 4 ++ l2circuit/collector.go | 14 ++-- lacp/collector.go | 14 ++-- ldp/collector.go | 28 +++++--- mac/collector.go | 14 ++-- main.go | 3 + mpls_lsp/collector.go | 14 ++-- nat/collector.go | 50 +++++++++---- nat2/collector.go | 42 +++++++---- ospf/collector.go | 30 +++++--- power/collector.go | 20 ++++-- route/collector.go | 14 ++-- routingengine/collector.go | 20 ++++-- rpc/rpc_client.go | 6 ++ rpki/collector.go | 28 +++++--- rpm/collector.go | 14 ++-- security/collector.go | 14 ++-- storage/collector.go | 20 ++++-- system/collector.go | 35 ++++++--- vpws/collector.go | 14 ++-- vrrp/collector.go | 14 ++-- 36 files changed, 632 insertions(+), 228 deletions(-) diff --git a/accounting/collector.go b/accounting/collector.go index e91b1d25..0f464eef 100644 --- a/accounting/collector.go +++ b/accounting/collector.go @@ -96,10 +96,16 @@ func (c *accountingCollector) Collect(client *rpc.Client, ch chan<- prometheus.M func (c *accountingCollector) accountingFlows(client *rpc.Client) (*AccountingFlow, error) { var x = AccountingFlowRpc{} -// err := client.RunCommandAndParse("show services accounting flow inline-jflow", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services accounting flow inline-jflow", &x) + if err != nil { + return nil, err + } } if x.Error.Message != "" { diff --git a/alarm/collector.go b/alarm/collector.go index a5d82a29..4df4b693 100644 --- a/alarm/collector.go +++ b/alarm/collector.go @@ -74,8 +74,15 @@ func (c *alarmCollector) alarmCounter(client *rpc.Client) (*AlarmCounter, *[]Ala yellow := 0 cmds := []string{ - "", - "", + "show system alarms", + "show chassis alarms", + } + + if client.Netconf { + cmds = []string{ + "", + "", + } } var alarms []AlarmDetails diff --git a/bfd/collector.go b/bfd/collector.go index 7e1e5d1d..f9746155 100644 --- a/bfd/collector.go +++ b/bfd/collector.go @@ -42,10 +42,16 @@ func (*bfdCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *bfdCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = bfdRpc{} -// err := client.RunCommandAndParse("show bfd session extensive", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show bfd session extensive", &x) + if err != nil { + return err + } } for _, bfds := range x.Information.BfdSessions { diff --git a/bgp/collector.go b/bgp/collector.go index ecfece63..dc497634 100644 --- a/bgp/collector.go +++ b/bgp/collector.go @@ -77,17 +77,24 @@ func (c *bgpCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *bgpCollector) collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = BGPRPC{} var cmd strings.Builder -// cmd.WriteString("show bgp neighbor") -// if c.LogicalSystem != "" { -// cmd.WriteString(" logical-system " + c.LogicalSystem) -// } - cmd.WriteString("") - - err := client.RunCommandAndParse(cmd.String(), &x) - if err != nil { - return err + if client.Netconf { + cmd.WriteString("") + err := client.RunCommandAndParse(cmd.String(), &x) + if err != nil { + return err + } + } else { + cmd.WriteString("show bgp neighbor") + if c.LogicalSystem != "" { + cmd.WriteString(" logical-system " + c.LogicalSystem) + } + err := client.RunCommandAndParse(cmd.String(), &x) + if err != nil { + return err + } } + for _, peer := range x.Information.Peers { c.collectForPeer(peer, ch, labelValues) } diff --git a/config/config.go b/config/config.go index 61ea8fa6..f2709759 100644 --- a/config/config.go +++ b/config/config.go @@ -57,6 +57,7 @@ type FeatureConfig struct { RPKI bool `yaml:"rpki,omitempty"` RPM bool `yaml:"rpm,omitempty"` Satellite bool `yaml:"satellite,omitempty"` + Netconf bool `yaml:"netconf,omitempty"` System bool `yaml:"system,omitempty"` Power bool `yaml:"power,omitempty"` MAC bool `yaml:"mac,omitempty"` @@ -127,6 +128,7 @@ func setDefaultValues(c *Config) { f.RPKI = false f.RPM = false f.Satellite = false + f.Netconf = false f.Power = false f.MAC = false f.MPLS_LSP = false diff --git a/environment/collector.go b/environment/collector.go index 41ab88e5..a82957e1 100644 --- a/environment/collector.go +++ b/environment/collector.go @@ -78,31 +78,60 @@ func (c *environmentCollector) environmentItems(client *rpc.Client, ch chan<- pr "Present": 5, } -// err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { - err := client.RunCommandAndParseWithParser("", func(b []byte) error { - return parseXML(b, &x) - }) - if err != nil { - return nil - } + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return nil + } - // gather satellite data - if client.Satellite { - var y = RpcReply{} - err = client.RunCommandAndParseWithParser("", func(b []byte) error { - if string(b[:]) == "\nerror: syntax error, expecting : satellite\n" { - log.Printf("system doesn't seem to have satellite enabled") + // gather satellite data + if client.Satellite { + var y = RpcReply{} + err = client.RunCommandAndParseWithParser("", func(b []byte) error { + if string(b[:]) == "\nerror: syntax error, expecting : satellite\n" { + log.Printf("system doesn't seem to have satellite enabled") + return nil + } + + return parseXML(b, &y) + }) + if err != nil { return nil + } else { + // add satellite details (only if y.MultiRoutingEngineResults.RoutingEngine has elements) + if len(y.MultiRoutingEngineResults.RoutingEngine) > 0 { + x.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items = append(x.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items, y.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items...) + } } - - return parseXML(b, &y) + } + } else { + err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { + return parseXML(b, &x) }) if err != nil { return nil - } else { - // add satellite details (only if y.MultiRoutingEngineResults.RoutingEngine has elements) - if len(y.MultiRoutingEngineResults.RoutingEngine) > 0 { - x.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items = append(x.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items, y.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items...) + } + + // gather satellite data + if client.Satellite { + var y = RpcReply{} + err = client.RunCommandAndParseWithParser("show chassis environment satellite", func(b []byte) error { + if string(b[:]) == "\nerror: syntax error, expecting : satellite\n" { + log.Printf("system doesn't seem to have satellite enabled") + return nil + } + + return parseXML(b, &y) + }) + if err != nil { + return nil + } else { + // add satellite details (only if y.MultiRoutingEngineResults.RoutingEngine has elements) + if len(y.MultiRoutingEngineResults.RoutingEngine) > 0 { + x.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items = append(x.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items, y.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items...) + } } } } @@ -133,12 +162,20 @@ func (c *environmentCollector) environmentPEMItems(client *rpc.Client, ch chan<- "Empty": 3, } -// err := client.RunCommandAndParseWithParser("show chassis environment pem", func(b []byte) error { - err := client.RunCommandAndParseWithParser("", func(b []byte) error { - return parseXML(b, &x) - }) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show chassis environment pem", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } } for _, re := range x.MultiRoutingEngineResults.RoutingEngine { diff --git a/firewall/collector.go b/firewall/collector.go index 6205546b..4654e181 100644 --- a/firewall/collector.go +++ b/firewall/collector.go @@ -48,10 +48,17 @@ func (*firewallCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *firewallCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = FirewallRpc{} -// err := client.RunCommandAndParse("show firewall filter regex .*", &x) - err := client.RunCommandAndParse(".*", &x) - if err != nil { - return err + + if client.Netconf { + err := client.RunCommandAndParse(".*", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show firewall filter regex .*", &x) + if err != nil { + return err + } } for _, t := range x.Information.Filters { diff --git a/fpc/collector.go b/fpc/collector.go index b92ed49f..a4658011 100644 --- a/fpc/collector.go +++ b/fpc/collector.go @@ -100,13 +100,20 @@ func (c *fpcCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, // CollectFPC collects metrics from JunOS func (c *fpcCollector) CollectFPCDetail(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { r := RpcReply{} -// err := client.RunCommandAndParseWithParser("show chassis fpc detail", func(b []byte) error { - err := client.RunCommandAndParseWithParser("", func(b []byte) error { - return parseXML(b, &r) - }) - - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &r) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show chassis fpc detail", func(b []byte) error { + return parseXML(b, &r) + }) + if err != nil { + return err + } } for _, r := range r.MultiRoutingEngineResults.RoutingEngine { diff --git a/interfacediagnostics/collector.go b/interfacediagnostics/collector.go index 448d77cb..03536996 100644 --- a/interfacediagnostics/collector.go +++ b/interfacediagnostics/collector.go @@ -255,10 +255,16 @@ func (c *interfaceDiagnosticsCollector) Collect(client *rpc.Client, ch chan<- pr func (c *interfaceDiagnosticsCollector) interfaceDiagnostics(client *rpc.Client) ([]*InterfaceDiagnostics, error) { var x = InterfaceDiagnosticsRPC{} -// err := client.RunCommandAndParse("show interfaces diagnostics optics", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show interfaces diagnostics optics", &x) + if err != nil { + return nil, err + } } return interfaceDiagnosticsFromRPCResult(x), nil @@ -280,43 +286,82 @@ func (c *interfaceDiagnosticsCollector) interfaceDiagnosticsSatellite(client *rp // // workaround: go through all lines of the XML and remove identical, consecutive lines -// err := client.RunCommandAndParseWithParser("show interfaces diagnostics optics satellite", func(b []byte) error { - err := client.RunCommandAndParseWithParser("", func(b []byte) error { - var ( - lines []string = strings.Split(string(b[:]), "\n") - lineIndex int - tmpByte []byte - ) - - // check if satellite is enabled - if string(b[:]) == "\nerror: syntax error, expecting : satellite\n" { - log.Printf("system doesn't seem to have satellite enabled") - return nil - } - - for lineIndex = range lines { - if lineIndex == 0 { - // add good lines to new byte buffer - tmpByte = append(tmpByte, lines[lineIndex]...) - continue + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + var ( + lines []string = strings.Split(string(b[:]), "\n") + lineIndex int + tmpByte []byte + ) + + // check if satellite is enabled + if string(b[:]) == "\nerror: syntax error, expecting : satellite\n" { + log.Printf("system doesn't seem to have satellite enabled") + return nil } - // check if two consecutive lines are identical (except whitespaces) - if strings.TrimSpace(lines[lineIndex]) == strings.TrimSpace(lines[lineIndex-1]) { - // skip the duplicate line - continue + for lineIndex = range lines { + if lineIndex == 0 { + // add good lines to new byte buffer + tmpByte = append(tmpByte, lines[lineIndex]...) + continue + } + + // check if two consecutive lines are identical (except whitespaces) + if strings.TrimSpace(lines[lineIndex]) == strings.TrimSpace(lines[lineIndex-1]) { + // skip the duplicate line + continue - } else { - // add good lines to new byte buffer - tmpByte = append(tmpByte, lines[lineIndex]...) + } else { + // add good lines to new byte buffer + tmpByte = append(tmpByte, lines[lineIndex]...) + } } + + return xml.Unmarshal(tmpByte, &x) + }) + + if err != nil { + return nil, err } + } else { + err := client.RunCommandAndParseWithParser("show interfaces diagnostics optics satellite", func(b []byte) error { + var ( + lines []string = strings.Split(string(b[:]), "\n") + lineIndex int + tmpByte []byte + ) + + // check if satellite is enabled + if string(b[:]) == "\nerror: syntax error, expecting : satellite\n" { + log.Printf("system doesn't seem to have satellite enabled") + return nil + } + + for lineIndex = range lines { + if lineIndex == 0 { + // add good lines to new byte buffer + tmpByte = append(tmpByte, lines[lineIndex]...) + continue + } - return xml.Unmarshal(tmpByte, &x) - }) + // check if two consecutive lines are identical (except whitespaces) + if strings.TrimSpace(lines[lineIndex]) == strings.TrimSpace(lines[lineIndex-1]) { + // skip the duplicate line + continue - if err != nil { - return nil, err + } else { + // add good lines to new byte buffer + tmpByte = append(tmpByte, lines[lineIndex]...) + } + } + + return xml.Unmarshal(tmpByte, &x) + }) + + if err != nil { + return nil, err + } } return interfaceDiagnosticsFromRPCResult(x), nil diff --git a/interfacelabels/dynamic_labels.go b/interfacelabels/dynamic_labels.go index 750b4d3f..d169e78a 100644 --- a/interfacelabels/dynamic_labels.go +++ b/interfacelabels/dynamic_labels.go @@ -47,9 +47,16 @@ type interfaceLabel struct { // CollectDescriptions collects labels from descriptions func (l *DynamicLabels) CollectDescriptions(device *connector.Device, client *rpc.Client, ifDescReg *regexp.Regexp) error { r := &InterfaceRPC{} - err := client.RunCommandAndParse("", r) - if err != nil { - return errors.Wrap(err, "could not retrieve interface descriptions for "+device.Host) + if client.Netconf { + err := client.RunCommandAndParse("", r) + if err != nil { + return errors.Wrap(err, "could not retrieve interface descriptions for "+device.Host) + } + } else { + err := client.RunCommandAndParse("show interfaces descriptions", r) + if err != nil { + return errors.Wrap(err, "could not retrieve interface descriptions for "+device.Host) + } } l.parseDescriptions(device, r.Information.Interfaces, ifDescReg) diff --git a/interfacequeue/collector.go b/interfacequeue/collector.go index 12aa246f..3ff9775b 100644 --- a/interfacequeue/collector.go +++ b/interfacequeue/collector.go @@ -101,10 +101,16 @@ func (c *interfaceQueueCollector) Describe(ch chan<- *prometheus.Desc) { func (c *interfaceQueueCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { q := InterfaceQueueRPC{} -// err := client.RunCommandAndParse("show interfaces queue", &q) - err := client.RunCommandAndParse("", &q) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &q) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show interfaces queue", &q) + if err != nil { + return err + } } for _, iface := range q.InterfaceInformation.Interfaces { diff --git a/interfaces/collector.go b/interfaces/collector.go index addd59c2..98d7bf7c 100644 --- a/interfaces/collector.go +++ b/interfaces/collector.go @@ -146,10 +146,16 @@ func (c *interfaceCollector) Collect(client *rpc.Client, ch chan<- prometheus.Me func (c *interfaceCollector) interfaceStats(client *rpc.Client) ([]*InterfaceStats, error) { var x = InterfaceRpc{} -// err := client.RunCommandAndParse("show interfaces extensive", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show interfaces extensive", &x) + if err != nil { + return nil, err + } } stats := make([]*InterfaceStats, 0) diff --git a/ipsec/collector.go b/ipsec/collector.go index a56929fc..8ac97720 100644 --- a/ipsec/collector.go +++ b/ipsec/collector.go @@ -49,10 +49,16 @@ func (*ipsecCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *ipsecCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpcReply{} -// err := client.RunCommandAndParse("show security ipsec security-associations", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show security ipsec security-associations", &x) + if err != nil { + return err + } } for _, re := range x.MultiRoutingEngineResults.RoutingEngine { @@ -65,10 +71,17 @@ func (c *ipsecCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric } var conf = ConfigurationSecurityIpsec{} -// err = client.RunCommandAndParse("show configuration security ipsec", &conf) - err = client.RunCommandAndParse("", &conf) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &conf) + if err != nil { + return err + } + } else { + conf = ConfigurationSecurityIpsec{} + err := client.RunCommandAndParse("show configuration security ipsec", &conf) + if err != nil { + return err + } } cls := append(labelValues, "N/A", "configured tunnels", "") diff --git a/ipsec/rpc.go b/ipsec/rpc.go index 2c46a985..651c0556 100644 --- a/ipsec/rpc.go +++ b/ipsec/rpc.go @@ -52,6 +52,67 @@ type RpcReplyNoRE struct { // ConfigurationSecurityIpsec is used for xml unmarshalling // In order to get the number of configured VPNs type ConfigurationSecurityIpsec struct { + Configuration struct { + Security struct { + Ipsec struct { + Proposal []struct { + Text string `xml:",chardata"` + Name string `xml:"name"` + Protocol string `xml:"protocol"` + AuthenticationAlgorithm string `xml:"authentication-algorithm"` + EncryptionAlgorithm string `xml:"encryption-algorithm"` + LifetimeSeconds string `xml:"lifetime-seconds"` + } `xml:"proposal"` + Policy []struct { + Name string `xml:"name"` + Proposals string `xml:"proposals"` + } `xml:"policy"` + Vpn []struct { + Name string `xml:"name"` + BindInterface string `xml:"bind-interface"` + Ike struct { + Gateway string `xml:"gateway"` + IpsecPolicy string `xml:"ipsec-policy"` + } `xml:"ike"` + EstablishTunnels string `xml:"establish-tunnels"` + } `xml:"vpn"` + } `xml:"ipsec"` + } `xml:"security"` + } `xml:"configuration"` +} + +type ConfigurationSecurityIpsecSSH struct { + Configuration struct { + Security struct { + Ipsec struct { + Proposal []struct { + Text string `xml:",chardata"` + Name string `xml:"name"` + Protocol string `xml:"protocol"` + AuthenticationAlgorithm string `xml:"authentication-algorithm"` + EncryptionAlgorithm string `xml:"encryption-algorithm"` + LifetimeSeconds string `xml:"lifetime-seconds"` + } `xml:"proposal"` + Policy []struct { + Name string `xml:"name"` + Proposals string `xml:"proposals"` + } `xml:"policy"` + Vpn []struct { + Name string `xml:"name"` + BindInterface string `xml:"bind-interface"` + Ike struct { + Gateway string `xml:"gateway"` + IpsecPolicy string `xml:"ipsec-policy"` + } `xml:"ike"` + EstablishTunnels string `xml:"establish-tunnels"` + } `xml:"vpn"` + } `xml:"ipsec"` + } `xml:"security"` + } `xml:"configuration"` +} + + +type ConfigurationSecurityIpsecNetconf struct { Configuration struct { Security struct { Ipsec struct { diff --git a/isis/collector.go b/isis/collector.go index 16b7fc58..d3207452 100644 --- a/isis/collector.go +++ b/isis/collector.go @@ -56,10 +56,17 @@ func (c *isisCollector) isisAdjancies(client *rpc.Client) (*IsisAdjacencies, err total := 0 var x = IsisRpc{} -// err := client.RunCommandAndParse("show isis adjacency", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return nil, err + + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show isis adjacency", &x) + if err != nil { + return nil, err + } } for _, adjacency := range x.Information.Adjacencies { diff --git a/junos_collector.go b/junos_collector.go index 36774adc..0cc14e95 100644 --- a/junos_collector.go +++ b/junos_collector.go @@ -96,6 +96,10 @@ func clientForDevice(device *connector.Device, connManager *connector.SSHConnect c.EnableSatellite() } + if cfg.Features.Netconf { + c.EnableNetconf() + } + return c, nil } diff --git a/l2circuit/collector.go b/l2circuit/collector.go index fb64d453..2331922b 100644 --- a/l2circuit/collector.go +++ b/l2circuit/collector.go @@ -78,10 +78,16 @@ func (c *l2circuitCollector) Collect(client *rpc.Client, ch chan<- prometheus.Me func (c *l2circuitCollector) collectL2circuitMetrics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = L2circuitRpc{} -// err := client.RunCommandAndParse("show l2circuit connections brief", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show l2circuit connections brief", &x) + if err != nil { + return err + } } neighbors := x.Information.Neighbors diff --git a/lacp/collector.go b/lacp/collector.go index 04cdb4c5..431e8c68 100644 --- a/lacp/collector.go +++ b/lacp/collector.go @@ -46,10 +46,16 @@ func (*lacpCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *lacpCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = lacpRpc{} -// err := client.RunCommandAndParse("show lacp interfaces", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show lacp interfaces", &x) + if err != nil { + return err + } } for _, iface := range x.Information.LacpInterfaces { diff --git a/ldp/collector.go b/ldp/collector.go index fb61f30e..79901011 100644 --- a/ldp/collector.go +++ b/ldp/collector.go @@ -62,10 +62,16 @@ func (c *ldpCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *ldpCollector) collectLDPMetrics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = LDPRpc{} -// err := client.RunCommandAndParse("show ldp neighbor", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show ldp neighbor", &x) + if err != nil { + return err + } } neighbors := x.Information.Neighbors @@ -76,10 +82,16 @@ func (c *ldpCollector) collectLDPMetrics(client *rpc.Client, ch chan<- prometheu func (c *ldpCollector) collectLDPSessions(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = LDPSessionRpc{} -// err := client.RunCommandAndParse("show ldp session", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show ldp session", &x) + if err != nil { + return err + } } sessions := x.Information.Sessions diff --git a/mac/collector.go b/mac/collector.go index 0f312ceb..956617a4 100644 --- a/mac/collector.go +++ b/mac/collector.go @@ -47,10 +47,16 @@ func (*macCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *macCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = MacRpc{} -// err := client.RunCommandAndParse("show ethernet-switching table summary", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show ethernet-switching table summary", &x) + if err != nil { + return err + } } entry := x.Information.Table.Entry diff --git a/main.go b/main.go index e5d2cd01..40342b00 100644 --- a/main.go +++ b/main.go @@ -58,6 +58,7 @@ var ( interfaceQueuesEnabled = flag.Bool("queues.enabled", false, "Scrape interface queue metrics") rpkiEnabled = flag.Bool("rpki.enabled", false, "Scrape rpki metrics") satelliteEnabled = flag.Bool("satellite.enabled", false, "Scrape metrics from satellite devices") + netconfEnabled = flag.Bool("netconf.enabled", false, "enable netconf rpc query style") systemEnabled = flag.Bool("system.enabled", false, "Scrape system metrics") macEnabled = flag.Bool("mac.enabled", false, "Scrape MAC address table metrics") alarmFilter = flag.String("alarms.filter", "", "Regex to filter for alerts to ignore") @@ -215,6 +216,7 @@ func loadConfigFromFlags() *config.Config { f.RPKI = *rpkiEnabled f.Storage = *storageEnabled f.Satellite = *satelliteEnabled + f.Netconf = *netconfEnabled f.System = *systemEnabled f.Power = *powerEnabled f.MAC = *macEnabled @@ -225,6 +227,7 @@ func loadConfigFromFlags() *config.Config { func connectionManager() *connector.SSHConnectionManager { opts := []connector.Option{ connector.WithReconnectInterval(*sshReconnectInterval), + connector.WithKeepAliveInterval(*sshKeepAliveInterval), connector.WithKeepAliveTimeout(*sshKeepAliveTimeout), } diff --git a/mpls_lsp/collector.go b/mpls_lsp/collector.go index f78b8d7a..c3b92628 100644 --- a/mpls_lsp/collector.go +++ b/mpls_lsp/collector.go @@ -49,10 +49,16 @@ func (*mpls_lspCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *mpls_lspCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = mpls_lspRpc{} -// err := client.RunCommandAndParse("show mpls lsp ingress extensive", &x) //ingress:Display LSPs originating at this router - err := client.RunCommandAndParse("", &x) //ingress:Display LSPs originating at this router - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) //ingress:Display LSPs originating at this router + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show mpls lsp ingress extensive", &x) //ingress:Display LSPs originating at this router + if err != nil { + return err + } } for _, lsp := range x.Information.Sessions { diff --git a/nat/collector.go b/nat/collector.go index 7ca3df47..073357a6 100644 --- a/nat/collector.go +++ b/nat/collector.go @@ -1,6 +1,7 @@ package nat import ( + "fmt" "github.com/czerwonk/junos_exporter/collector" "github.com/czerwonk/junos_exporter/rpc" "github.com/prometheus/client_golang/prometheus" @@ -506,10 +507,16 @@ func (c *natCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *natCollector) NatInterfaces(client *rpc.Client) ([]*NatInterface, error) { var x = NatRpc{} -// err := client.RunCommandAndParse("show services nat statistics", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services nat statistics", &x) + if err != nil { + return nil, err + } } interfaces := make([]*NatInterface, 0) @@ -917,9 +924,14 @@ func (*natCollector) collectForInterface(s *NatInterface, ch chan<- prometheus.M func (c *natCollector) NatPoolInterfaces(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) ([]*NatPoolInterface, error) { var x = NatPoolRpc{} - err := client.RunCommandAndParse("show services nat pool", &x) - if err != nil { - return nil, err + if client.Netconf { + //TODO + return nil, fmt.Errorf("not implemented") + } else { + err := client.RunCommandAndParse("show services nat pool", &x) + if err != nil { + return nil, err + } } interfaces := make([]*NatPoolInterface, 0) @@ -951,9 +963,14 @@ func (c *natCollector) collectForPoolInterface(s *NatPoolInterface, ch chan<- pr func (c *natCollector) NatPoolDetailInterfaces(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) ([]*NatPoolDetailInterface, error) { var x = NatPoolDetailRpc{} - err := client.RunCommandAndParse("show services nat pool detail", &x) - if err != nil { - return nil, err + if client.Netconf { + //TODO + return nil, fmt.Errorf("not implemented") + } else { + err := client.RunCommandAndParse("show services nat pool detail", &x) + if err != nil { + return nil, err + } } interfacesdetail := make([]*NatPoolDetailInterface, 0) @@ -993,9 +1010,16 @@ func (c *natCollector) collectForPoolDetailInterface(s *NatPoolDetailInterface, func (c *natCollector) ServiceSetsCpuInterfaces(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) ([]*ServiceSetsCpuInterface, error) { var x = ServiceSetsCpuRpc{} - err := client.RunCommandAndParse("show services service-sets cpu-usage", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services service-sets cpu-usage", &x) + if err != nil { + return nil, err + } } interfacesdetail := make([]*ServiceSetsCpuInterface, 0) diff --git a/nat2/collector.go b/nat2/collector.go index fb4761d4..29fb4432 100644 --- a/nat2/collector.go +++ b/nat2/collector.go @@ -225,10 +225,16 @@ func (c *natCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *natCollector) NatInterfaces(client *rpc.Client) ([]*NatInterface, error) { var x = NatRpc{} -// err := client.RunCommandAndParse("show services nat statistics", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services nat statistics", &x) + if err != nil { + return nil, err + } } interfaces := make([]*NatInterface, 0) @@ -321,10 +327,16 @@ func (*natCollector) collectForInterface(s *NatInterface, ch chan<- prometheus.M func (c *natCollector) SrcNatPools(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) ([]SrcNatPool, error) { var x = SrcNatPoolRpc{} -// err := client.RunCommandAndParse("show services nat source pool all", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services nat source pool all", &x) + if err != nil { + return nil, err + } } return x.Information.Pools[:], nil @@ -394,10 +406,16 @@ func (c *natCollector) collectForSrcNatPool(s []SrcNatPool, ch chan<- prometheus func (c *natCollector) ServiceSetsCpuInterfaces(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) ([]*ServiceSetsCpuInterface, error) { var x = ServiceSetsCpuRpc{} -// err := client.RunCommandAndParse("show services service-sets cpu-usage", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services service-sets cpu-usage", &x) + if err != nil { + return nil, err + } } interfacesdetail := make([]*ServiceSetsCpuInterface, 0) diff --git a/ospf/collector.go b/ospf/collector.go index 926b91f2..31c4ce15 100644 --- a/ospf/collector.go +++ b/ospf/collector.go @@ -64,12 +64,16 @@ func (c *ospfCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *ospfCollector) collectOSPFMetrics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = OspfRpc{} var cmd strings.Builder -// cmd.WriteString("show ospf overview") -// cmd.WriteString("show ospf overview") -// if c.LogicalSystem != "" { -// cmd.WriteString(" logical-system " + c.LogicalSystem) -// } - cmd.WriteString("") + + if client.Netconf { + cmd.WriteString("") + } else { + cmd.WriteString("show ospf overview") + cmd.WriteString("show ospf overview") + if c.LogicalSystem != "" { + cmd.WriteString(" logical-system " + c.LogicalSystem) + } + } err := client.RunCommandAndParse(cmd.String(), &x) if err != nil { @@ -96,11 +100,15 @@ func (c *ospfCollector) collectOSPFMetrics(client *rpc.Client, ch chan<- prometh func (c *ospfCollector) collectOSPFv3Metrics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = Ospf3Rpc{} var cmd strings.Builder -// cmd.WriteString("show ospf3 overview") - cmd.WriteString("") -// if c.LogicalSystem != "" { -// cmd.WriteString(" logical-system " + c.LogicalSystem) -// } + + if client.Netconf { + cmd.WriteString("") + } else { + cmd.WriteString("show ospf3 overview") + if c.LogicalSystem != "" { + cmd.WriteString(" logical-system " + c.LogicalSystem) + } + } err := client.RunCommandAndParse(cmd.String(), &x) if err != nil { diff --git a/power/collector.go b/power/collector.go index d7e5e0e8..44af0fc6 100644 --- a/power/collector.go +++ b/power/collector.go @@ -88,12 +88,20 @@ func (c *powerCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric } var x = RpcReply{} -// err := client.RunCommandAndParseWithParser("show chassis power", func(b []byte) error { - err := client.RunCommandAndParseWithParser("", func(b []byte) error { - return parseXML(b, &x) - }) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show chassis power", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } } for _, re := range x.MultiRoutingEngineResults.RoutingEngine { diff --git a/route/collector.go b/route/collector.go index 61c5cf6d..d3a3742e 100644 --- a/route/collector.go +++ b/route/collector.go @@ -52,10 +52,16 @@ func (*routeCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *routeCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RouteRpc{} -// err := client.RunCommandAndParse("show route summary", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show route summary", &x) + if err != nil { + return err + } } for _, t := range x.Information.Tables { diff --git a/routingengine/collector.go b/routingengine/collector.go index 89228770..5c392efc 100644 --- a/routingengine/collector.go +++ b/routingengine/collector.go @@ -168,12 +168,20 @@ func (*routingEngineCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *routingEngineCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpcReply{} -// err := client.RunCommandAndParseWithParser("show chassis routing-engine", func(b []byte) error { - err := client.RunCommandAndParseWithParser("", func(b []byte) error { - return parseXML(b, &x) - }) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show chassis routing-engine", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } } for _, re := range x.MultiRoutingEngineResults.RoutingEngine { diff --git a/rpc/rpc_client.go b/rpc/rpc_client.go index 56529f09..947b925e 100644 --- a/rpc/rpc_client.go +++ b/rpc/rpc_client.go @@ -18,6 +18,7 @@ type Client struct { conn *connector.SSHConnection debug bool Satellite bool + Netconf bool } // NewClient creates a new client to connect to @@ -73,3 +74,8 @@ func (c *Client) DisableDebug() { func (c *Client) EnableSatellite() { c.Satellite = true } + +// EnableNetconf enables netconf RPCs instead of SSH-CLI +func (c *Client) EnableNetconf() { + c.Netconf = true +} diff --git a/rpki/collector.go b/rpki/collector.go index 45b8579e..35d48976 100644 --- a/rpki/collector.go +++ b/rpki/collector.go @@ -79,10 +79,16 @@ func (c *rpkiCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *rpkiCollector) collectSessions(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpkiSessionRpc{} -// err := client.RunCommandAndParse("show validation session", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show validation session", &x) + if err != nil { + return err + } } for _, session := range x.Information.RpkiSessions { @@ -130,10 +136,16 @@ func (c *rpkiCollector) collectForSession(s RpkiSession, ch chan<- prometheus.Me func (c *rpkiCollector) collectStatistics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpkiStatisticsRpc{} -// err := client.RunCommandAndParse("show validation statistics", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show validation statistics", &x) + if err != nil { + return err + } } ch <- prometheus.MustNewConstMetric(memoryUtilizationDesc, prometheus.GaugeValue, float64(x.Information.Statistics.MemoryUtilization), labelValues...) diff --git a/rpm/collector.go b/rpm/collector.go index db5decea..67208724 100644 --- a/rpm/collector.go +++ b/rpm/collector.go @@ -68,10 +68,16 @@ func (c *rpmCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *rpmCollector) collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RPMRPC{} -// err := client.RunCommandAndParse("show services rpm probe-results", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show services rpm probe-results", &x) + if err != nil { + return err + } } for _, probe := range x.Results.Probes { diff --git a/security/collector.go b/security/collector.go index 12f88797..4775fdc5 100644 --- a/security/collector.go +++ b/security/collector.go @@ -63,10 +63,16 @@ func (*securityCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *securityCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpcReply{} -// err := client.RunCommandAndParse("show security monitoring", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show security monitoring", &x) + if err != nil { + return err + } } for _, re := range x.MultiRoutingEngineResults.RoutingEngine { diff --git a/storage/collector.go b/storage/collector.go index d9791ba2..7dadcb7d 100644 --- a/storage/collector.go +++ b/storage/collector.go @@ -51,12 +51,20 @@ func (*storageCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *storageCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpcReply{} -// err := client.RunCommandAndParseWithParser("show system storage", func(b []byte) error { - err := client.RunCommandAndParseWithParser("", func(b []byte) error { - return parseXML(b, &x) - }) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show system storage", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } } for _, re := range x.MultiRoutingEngineResults.RoutingEngine { diff --git a/system/collector.go b/system/collector.go index 750180b5..fe8c0afd 100644 --- a/system/collector.go +++ b/system/collector.go @@ -162,10 +162,16 @@ func (c *systemCollector) CollectSystem(client *rpc.Client, ch chan<- prometheus r = new(BuffersRPC) -// err = client.RunCommandAndParse("show system buffers", r) - err = client.RunCommandAndParse("", r) - if err != nil { - return err + if client.Netconf { + err = client.RunCommandAndParse("", r) + if err != nil { + return err + } + } else { + err = client.RunCommandAndParse("show system buffers", r) + if err != nil { + return err + } } if r.Output != "" { @@ -278,10 +284,16 @@ func (c *systemCollector) CollectSystem(client *rpc.Client, ch chan<- prometheus // system information r2 = new(SystemInformationRPC) -// err = client.RunCommandAndParse("show system information", r2) - err = client.RunCommandAndParse("", r2) - if err != nil { - return err + if client.Netconf { + err = client.RunCommandAndParse("", r2) + if err != nil { + return err + } + } else { + err = client.RunCommandAndParse("show system information", r2) + if err != nil { + return err + } } // create LabelSet (target, "model", "os", "os_version", "serial", "hostname", "alias", "slot_id", "state") @@ -300,8 +312,11 @@ func (c *systemCollector) CollectSystem(client *rpc.Client, ch chan<- prometheus // system information of satellites r3 = new(SatelliteChassisRPC) -// err = client.RunCommandAndParse("show chassis satellite detail", r3) - err = client.RunCommandAndParse("", r3) + if client.Netconf { + err = client.RunCommandAndParse("", r3) + } else { + err = client.RunCommandAndParse("show chassis satellite detail", r3) + } // there are various error messages when satellite is not enabled; thus here we just ignore the error and continue if err == nil { for i = range r3.SatelliteInfo.Satellite { diff --git a/vpws/collector.go b/vpws/collector.go index 91ba7c44..815cf35e 100644 --- a/vpws/collector.go +++ b/vpws/collector.go @@ -53,10 +53,16 @@ func (*vpwsCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *vpwsCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = vpwsRpc{} -// err := client.RunCommandAndParse("show evpn vpws-instance", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show evpn vpws-instance", &x) + if err != nil { + return err + } } for _, vInst := range x.Information.VpwsInstances { diff --git a/vrrp/collector.go b/vrrp/collector.go index 941df35d..5290449f 100644 --- a/vrrp/collector.go +++ b/vrrp/collector.go @@ -44,10 +44,16 @@ func (c *vrrpCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, } var x = VrrpRpc{} -// err := client.RunCommandAndParse("show vrrp summary", &x) - err := client.RunCommandAndParse("", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show vrrp summary", &x) + if err != nil { + return err + } } for _, iface := range x.Information.Interfaces { From b7e41b16be7ac4fedd237fb1058daec7a6998564 Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Tue, 24 May 2022 13:41:28 +0200 Subject: [PATCH 14/23] adding netconf as a feature #2 --- accounting/collector.go | 14 ++++++++++---- bgp/collector.go | 13 +++++-------- fpc/collector.go | 40 ++++++++++++++++++++++++++++------------ 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/accounting/collector.go b/accounting/collector.go index 0f464eef..b6cbcd78 100644 --- a/accounting/collector.go +++ b/accounting/collector.go @@ -126,10 +126,16 @@ func (c *accountingCollector) accountingFlows(client *rpc.Client) (*AccountingFl func (c *accountingCollector) accountingFailures(client *rpc.Client) (*AccountingError, error) { var x = AccountingFlowErrorRpc{} -// err := client.RunCommandAndParse("show services accounting errors inline-jflow fpc-slot 0", &x) - err := client.RunCommandAndParse("0", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("0", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services accounting errors inline-jflow fpc-slot 0", &x) + if err != nil { + return nil, err + } } return &AccountingError{ diff --git a/bgp/collector.go b/bgp/collector.go index dc497634..9a14940e 100644 --- a/bgp/collector.go +++ b/bgp/collector.go @@ -79,19 +79,16 @@ func (c *bgpCollector) collect(client *rpc.Client, ch chan<- prometheus.Metric, var cmd strings.Builder if client.Netconf { cmd.WriteString("") - err := client.RunCommandAndParse(cmd.String(), &x) - if err != nil { - return err - } } else { cmd.WriteString("show bgp neighbor") if c.LogicalSystem != "" { cmd.WriteString(" logical-system " + c.LogicalSystem) } - err := client.RunCommandAndParse(cmd.String(), &x) - if err != nil { - return err - } + } + + err := client.RunCommandAndParse(cmd.String(), &x) + if err != nil { + return err } diff --git a/fpc/collector.go b/fpc/collector.go index a4658011..a272d427 100644 --- a/fpc/collector.go +++ b/fpc/collector.go @@ -129,12 +129,20 @@ func (c *fpcCollector) CollectFPCDetail(client *rpc.Client, ch chan<- prometheus // Collect collects metrics from JunOS func (c *fpcCollector) CollectFPC(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { r := RpcReply{} -// err := client.RunCommandAndParseWithParser("show chassis fpc", func(b []byte) error { - err := client.RunCommandAndParseWithParser("", func(b []byte) error { - return parseXML(b, &r) - }) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &r) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show chassis fpc", func(b []byte) error { + return parseXML(b, &r) + }) + if err != nil { + return err + } } for _, r := range r.MultiRoutingEngineResults.RoutingEngine { @@ -148,12 +156,20 @@ func (c *fpcCollector) CollectFPC(client *rpc.Client, ch chan<- prometheus.Metri func (c *fpcCollector) CollectPIC(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { r := RpcReply{} -// err := client.RunCommandAndParseWithParser("show chassis fpc pic-status", func(b []byte) error { - err := client.RunCommandAndParseWithParser("", func(b []byte) error { - return parseXML(b, &r) - }) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &r) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show chassis fpc pic-status", func(b []byte) error { + return parseXML(b, &r) + }) + if err != nil { + return err + } } for _, r := range r.MultiRoutingEngineResults.RoutingEngine { labels := append(labelValues, r.Name) From 59d315da35f27cabfcf3207b728a4b7ad004582f Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Tue, 24 May 2022 16:17:44 +0200 Subject: [PATCH 15/23] adding netconf as a feature --- connector/connection.go | 36 +++++++++++++++++++++++++++++++ connector/connection_manager.go | 7 +++--- environment/collector.go | 3 ++- interfacediagnostics/collector.go | 9 ++------ junos_collector.go | 2 +- ospf/collector.go | 1 - rpc/rpc_client.go | 25 ++++++++++++++++----- storage/collector.go | 24 ++++++++++++++++++++- 8 files changed, 88 insertions(+), 19 deletions(-) diff --git a/connector/connection.go b/connector/connection.go index c34358a3..07c32656 100644 --- a/connector/connection.go +++ b/connector/connection.go @@ -1,6 +1,7 @@ package connector import ( + "bytes" "net" "sync" @@ -17,6 +18,7 @@ type SSHConnection struct { conn net.Conn mu sync.Mutex done chan struct{} + netconf bool } type TransportSSH struct { @@ -28,6 +30,40 @@ type TransportSSH struct { // RunCommand runs a command against the device func (c *SSHConnection) RunCommand(cmd string) ([]byte, error) { + if c.netconf { + return c.RunCommandNETCONF(cmd) + } else { + return c.RunCommandSSH(cmd) + } +} + +func (c *SSHConnection) RunCommandSSH(cmd string) ([]byte, error) { + c.mu.Lock() + defer c.mu.Unlock() + + if c.client == nil { + return nil, errors.New("not connected") + } + + session, err := c.client.NewSession() + if err != nil { + return nil, errors.Wrap(err, "could not open session") + } + defer session.Close() + + var b = &bytes.Buffer{} + session.Stdout = b + + err = session.Run(cmd) + if err != nil { + return nil, errors.Wrap(err, "could not run command") + } + + return b.Bytes(), nil +} + + +func (c *SSHConnection) RunCommandNETCONF(cmd string) ([]byte, error) { c.mu.Lock() defer c.mu.Unlock() var err error diff --git a/connector/connection_manager.go b/connector/connection_manager.go index 88720df9..e39451f0 100644 --- a/connector/connection_manager.go +++ b/connector/connection_manager.go @@ -67,7 +67,7 @@ func NewConnectionManager(opts ...Option) *SSHConnectionManager { } // Connect connects to a device or returns an long living connection -func (m *SSHConnectionManager) Connect(device *Device) (*SSHConnection, error) { +func (m *SSHConnectionManager) Connect(device *Device, netconf bool) (*SSHConnection, error) { m.mu.Lock() defer m.mu.Unlock() @@ -79,10 +79,10 @@ func (m *SSHConnectionManager) Connect(device *Device) (*SSHConnection, error) { return connection, nil } - return m.connect(device) + return m.connect(device, netconf) } -func (m *SSHConnectionManager) connect(device *Device) (*SSHConnection, error) { +func (m *SSHConnectionManager) connect(device *Device, netconf bool) (*SSHConnection, error) { client, conn, err := m.connectToDevice(device) if err != nil { return nil, err @@ -93,6 +93,7 @@ func (m *SSHConnectionManager) connect(device *Device) (*SSHConnection, error) { client: client, device: device, done: make(chan struct{}), + netconf: netconf, } go m.keepAlive(c) diff --git a/environment/collector.go b/environment/collector.go index a82957e1..ecab65ca 100644 --- a/environment/collector.go +++ b/environment/collector.go @@ -98,7 +98,8 @@ func (c *environmentCollector) environmentItems(client *rpc.Client, ch chan<- pr return parseXML(b, &y) }) if err != nil { - return nil + // probably no satellite, but let's continue the task + //return nil } else { // add satellite details (only if y.MultiRoutingEngineResults.RoutingEngine has elements) if len(y.MultiRoutingEngineResults.RoutingEngine) > 0 { diff --git a/interfacediagnostics/collector.go b/interfacediagnostics/collector.go index 03536996..1ad8b1e5 100644 --- a/interfacediagnostics/collector.go +++ b/interfacediagnostics/collector.go @@ -294,12 +294,6 @@ func (c *interfaceDiagnosticsCollector) interfaceDiagnosticsSatellite(client *rp tmpByte []byte ) - // check if satellite is enabled - if string(b[:]) == "\nerror: syntax error, expecting : satellite\n" { - log.Printf("system doesn't seem to have satellite enabled") - return nil - } - for lineIndex = range lines { if lineIndex == 0 { // add good lines to new byte buffer @@ -322,7 +316,8 @@ func (c *interfaceDiagnosticsCollector) interfaceDiagnosticsSatellite(client *rp }) if err != nil { - return nil, err + log.Printf("system doesn't seem to have satellite enabled") + return nil,nil } } else { err := client.RunCommandAndParseWithParser("show interfaces diagnostics optics satellite", func(b []byte) error { diff --git a/junos_collector.go b/junos_collector.go index 0cc14e95..b8c6f390 100644 --- a/junos_collector.go +++ b/junos_collector.go @@ -81,7 +81,7 @@ func newJunosCollector(devices []*connector.Device, connectionManager *connector } func clientForDevice(device *connector.Device, connManager *connector.SSHConnectionManager) (*rpc.Client, error) { - conn, err := connManager.Connect(device) + conn, err := connManager.Connect(device, cfg.Features.Netconf) if err != nil { return nil, err } diff --git a/ospf/collector.go b/ospf/collector.go index 31c4ce15..29e5d3d4 100644 --- a/ospf/collector.go +++ b/ospf/collector.go @@ -68,7 +68,6 @@ func (c *ospfCollector) collectOSPFMetrics(client *rpc.Client, ch chan<- prometh if client.Netconf { cmd.WriteString("") } else { - cmd.WriteString("show ospf overview") cmd.WriteString("show ospf overview") if c.LogicalSystem != "" { cmd.WriteString(" logical-system " + c.LogicalSystem) diff --git a/rpc/rpc_client.go b/rpc/rpc_client.go index 947b925e..0c07dc4f 100644 --- a/rpc/rpc_client.go +++ b/rpc/rpc_client.go @@ -2,6 +2,7 @@ package rpc import ( "encoding/xml" + "fmt" "bytes" "log" @@ -30,10 +31,16 @@ func NewClient(ssh *connector.SSHConnection) *Client { // RunCommandAndParse runs a command on JunOS and unmarshals the XML result func (c *Client) RunCommandAndParse(cmd string, obj interface{}) error { - return c.RunCommandAndParseWithParser(cmd, func(b []byte) error { - //in junos the xml interfaces contains line returns in the values - return xml.Unmarshal(bytes.ReplaceAll(b, []byte("\n"), []byte("")), obj) - }) + if c.Netconf { + return c.RunCommandAndParseWithParser(cmd, func(b []byte) error { + //in junos the xml interfaces contains line returns in the values + return xml.Unmarshal(bytes.ReplaceAll(b, []byte("\n"), []byte("")), obj) + }) + } else { + return c.RunCommandAndParseWithParser(cmd, func(b []byte) error { + return xml.Unmarshal(b, obj) + }) + } } // RunCommandAndParseWithParser runs a command on JunOS and uses the given parser to handle the result @@ -42,7 +49,15 @@ func (c *Client) RunCommandAndParseWithParser(cmd string, parser Parser) error { log.Printf("Running command on %s: %s\n", c.conn.Host(), cmd) } - b, err := c.conn.RunCommand(cmd) + var err error + var b []byte + + if c.Netconf { + b, err = c.conn.RunCommand(cmd) + } else { + b, err = c.conn.RunCommand(fmt.Sprintf("%s | display xml", cmd)) + } + if err != nil { return err } diff --git a/storage/collector.go b/storage/collector.go index 7dadcb7d..8c803d92 100644 --- a/storage/collector.go +++ b/storage/collector.go @@ -4,6 +4,7 @@ import ( "encoding/xml" "strconv" "strings" + "bytes" "github.com/czerwonk/junos_exporter/collector" "github.com/czerwonk/junos_exporter/rpc" @@ -53,7 +54,7 @@ func (c *storageCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metr var x = RpcReply{} if client.Netconf { err := client.RunCommandAndParseWithParser("", func(b []byte) error { - return parseXML(b, &x) + return parseXMLnetconf(b, &x) }) if err != nil { return err @@ -106,3 +107,24 @@ func parseXML(b []byte, res *RpcReply) error { } return nil } + +func parseXMLnetconf(b []byte, res *RpcReply) error { + if strings.Contains(string(b), "multi-routing-engine-results") { + return xml.Unmarshal(b, res) + } + + fi := RpcReplyNoRE{} + + err := xml.Unmarshal(bytes.ReplaceAll(b, []byte("\n"), []byte("")), &fi) + if err != nil { + return err + } + + res.MultiRoutingEngineResults.RoutingEngine = []RoutingEngine{ + { + Name: "N/A", + StorageInformation: fi.StorageInformation, + }, + } + return nil +} From d00882cd692ffc6f35d15550606df00362e61b8c Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Tue, 24 May 2022 16:20:49 +0200 Subject: [PATCH 16/23] ipsec compatibility --- ipsec/rpc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipsec/rpc.go b/ipsec/rpc.go index 651c0556..b63c3f5c 100644 --- a/ipsec/rpc.go +++ b/ipsec/rpc.go @@ -139,5 +139,5 @@ type ConfigurationSecurityIpsecNetconf struct { } `xml:"vpn"` } `xml:"ipsec"` } `xml:"security"` - } `xml:"data>configuration"` + } `xml:"data,omitempty>configuration"` } From f97959e90436d9cc3e0af4fec41dd16a210f23f8 Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Tue, 24 May 2022 16:32:26 +0200 Subject: [PATCH 17/23] ipsec compatibility --- ipsec/collector.go | 10 ++++++---- ipsec/rpc.go | 35 ++--------------------------------- 2 files changed, 8 insertions(+), 37 deletions(-) diff --git a/ipsec/collector.go b/ipsec/collector.go index 8ac97720..95600870 100644 --- a/ipsec/collector.go +++ b/ipsec/collector.go @@ -70,22 +70,24 @@ func (c *ipsecCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric } } - var conf = ConfigurationSecurityIpsec{} if client.Netconf { + var conf = ConfigurationSecurityIpsecnetconf{} err := client.RunCommandAndParse("", &conf) if err != nil { return err } + cls := append(labelValues, "N/A", "configured tunnels", "") + ch <- prometheus.MustNewConstMetric(configuredTunnels, prometheus.GaugeValue, float64(len(conf.Configuration.Security.Ipsec.Vpn)), cls...) } else { - conf = ConfigurationSecurityIpsec{} + var conf = ConfigurationSecurityIpsec{} err := client.RunCommandAndParse("show configuration security ipsec", &conf) if err != nil { return err } + cls := append(labelValues, "N/A", "configured tunnels", "") + ch <- prometheus.MustNewConstMetric(configuredTunnels, prometheus.GaugeValue, float64(len(conf.Configuration.Security.Ipsec.Vpn)), cls...) } - cls := append(labelValues, "N/A", "configured tunnels", "") - ch <- prometheus.MustNewConstMetric(configuredTunnels, prometheus.GaugeValue, float64(len(conf.Configuration.Security.Ipsec.Vpn)), cls...) return nil } diff --git a/ipsec/rpc.go b/ipsec/rpc.go index b63c3f5c..0f26cc97 100644 --- a/ipsec/rpc.go +++ b/ipsec/rpc.go @@ -81,7 +81,7 @@ type ConfigurationSecurityIpsec struct { } `xml:"configuration"` } -type ConfigurationSecurityIpsecSSH struct { +type ConfigurationSecurityIpsecnetconf struct { Configuration struct { Security struct { Ipsec struct { @@ -108,36 +108,5 @@ type ConfigurationSecurityIpsecSSH struct { } `xml:"vpn"` } `xml:"ipsec"` } `xml:"security"` - } `xml:"configuration"` -} - - -type ConfigurationSecurityIpsecNetconf struct { - Configuration struct { - Security struct { - Ipsec struct { - Proposal []struct { - Text string `xml:",chardata"` - Name string `xml:"name"` - Protocol string `xml:"protocol"` - AuthenticationAlgorithm string `xml:"authentication-algorithm"` - EncryptionAlgorithm string `xml:"encryption-algorithm"` - LifetimeSeconds string `xml:"lifetime-seconds"` - } `xml:"proposal"` - Policy []struct { - Name string `xml:"name"` - Proposals string `xml:"proposals"` - } `xml:"policy"` - Vpn []struct { - Name string `xml:"name"` - BindInterface string `xml:"bind-interface"` - Ike struct { - Gateway string `xml:"gateway"` - IpsecPolicy string `xml:"ipsec-policy"` - } `xml:"ike"` - EstablishTunnels string `xml:"establish-tunnels"` - } `xml:"vpn"` - } `xml:"ipsec"` - } `xml:"security"` - } `xml:"data,omitempty>configuration"` + } `xml:"data>configuration"` } From f0bf3692f5c7b9718a3ff31a806be8e15b6813c7 Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Wed, 25 May 2022 10:24:14 +0200 Subject: [PATCH 18/23] keep the netconf session active - performance --- connector/connection.go | 49 ++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/connector/connection.go b/connector/connection.go index 07c32656..89155d50 100644 --- a/connector/connection.go +++ b/connector/connection.go @@ -4,7 +4,8 @@ import ( "bytes" "net" "sync" - + "fmt" + "io" "github.com/pkg/errors" "golang.org/x/crypto/ssh" @@ -19,6 +20,7 @@ type SSHConnection struct { mu sync.Mutex done chan struct{} netconf bool + netconfsession *netconf.Session } type TransportSSH struct { @@ -73,29 +75,36 @@ func (c *SSHConnection) RunCommandNETCONF(cmd string) ([]byte, error) { } t := &TransportSSH{} - t.sshSession, err = c.client.NewSession() - if err != nil { - return nil, errors.Wrap(err, "could not open session") - } - defer t.sshSession.Close() - - writer, err := t.sshSession.StdinPipe() - if err != nil { - return nil, errors.Wrap(err, "could not open session stdin") + if c.netconfsession == nil { + t.sshSession, err = c.client.NewSession() + if err != nil { + return nil, errors.Wrap(err, "could not open session") + } + + writer, err := t.sshSession.StdinPipe() + if err != nil { + return nil, errors.Wrap(err, "could not open session stdin") + } + + reader, err := t.sshSession.StdoutPipe() + if err != nil { + return nil, errors.Wrap(err, "could not open session stdout") + } + + t.ReadWriteCloser = netconf.NewReadWriteCloser(reader, writer) + t.sshSession.RequestSubsystem("netconf") + c.netconfsession = netconf.NewSession(t) } - reader, err := t.sshSession.StdoutPipe() - if err != nil { - return nil, errors.Wrap(err, "could not open session stdout") - } - - t.ReadWriteCloser = netconf.NewReadWriteCloser(reader, writer) - t.sshSession.RequestSubsystem("netconf") - session := netconf.NewSession(t) - + reply, err := c.netconfsession.Exec(netconf.RawMethod(cmd)) - reply, err := session.Exec(netconf.RawMethod(cmd)) if err != nil { + if err == io.EOF { + //probably lost the session, closing to force a reopen + fmt.Println("Error - Closing") + c.netconfsession.Close() + c.netconfsession = nil + } return nil, errors.Wrap(err, "could not run command") } From 0b4625ba4ea194cc913669112250b27e6e05b9ca Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Fri, 10 Jun 2022 10:05:03 +0200 Subject: [PATCH 19/23] adding virtual-chassis check --- README.md | 5 ++++ collectors.go | 2 ++ config/config.go | 2 ++ virtualchassis/collector.go | 58 +++++++++++++++++++++++++++++++++++++ virtualchassis/rpc.go | 22 ++++++++++++++ 5 files changed, 89 insertions(+) create mode 100644 virtualchassis/collector.go create mode 100644 virtualchassis/rpc.go diff --git a/README.md b/README.md index 39839201..e609995f 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,11 @@ States map to human readable names like this: 4 = "Ex-Incr" 5 = "Ex-Full" ``` +* VirtualChassis (status of members) +``` +0 = "NotPrsnt" +1 = "NotPrsnt" +``` * VRRP (state per interface) States map to human readable names like this: ``` diff --git a/collectors.go b/collectors.go index 9429bb19..c92ad26d 100644 --- a/collectors.go +++ b/collectors.go @@ -33,6 +33,7 @@ import ( "github.com/czerwonk/junos_exporter/security" "github.com/czerwonk/junos_exporter/storage" "github.com/czerwonk/junos_exporter/system" + "github.com/czerwonk/junos_exporter/virtualchassis" "github.com/czerwonk/junos_exporter/vrrp" "github.com/czerwonk/junos_exporter/vpws" ) @@ -105,6 +106,7 @@ func (c *collectors) initCollectorsForDevices(device *connector.Device) { c.addCollectorIfEnabledForDevice(device, "system", f.System, system.NewCollector) c.addCollectorIfEnabledForDevice(device, "power", f.Power, power.NewCollector) c.addCollectorIfEnabledForDevice(device, "mac", f.MAC, mac.NewCollector) + c.addCollectorIfEnabledForDevice(device, "virtualchassis", f.VirtualChassis, virtualchassis.NewCollector) c.addCollectorIfEnabledForDevice(device, "vrrp", f.VRRP, vrrp.NewCollector) c.addCollectorIfEnabledForDevice(device, "vpws", f.VPWS, vpws.NewCollector) c.addCollectorIfEnabledForDevice(device, "mpls_lsp", f.MPLS_LSP, mpls_lsp.NewCollector) diff --git a/config/config.go b/config/config.go index 61ea8fa6..912bf629 100644 --- a/config/config.go +++ b/config/config.go @@ -61,6 +61,7 @@ type FeatureConfig struct { Power bool `yaml:"power,omitempty"` MAC bool `yaml:"mac,omitempty"` MPLS_LSP bool `yaml:"mpls_lsp,omitempty"` + VirtualChassis bool `yaml:"virtualchassis,omitempty"` VPWS bool `yaml:"vpws,omitempty"` VRRP bool `yaml:"vrrp,omitempty"` } @@ -130,6 +131,7 @@ func setDefaultValues(c *Config) { f.Power = false f.MAC = false f.MPLS_LSP = false + f.VirtualChassis = false f.VPWS = false f.VRRP = false f.BFD = false diff --git a/virtualchassis/collector.go b/virtualchassis/collector.go new file mode 100644 index 00000000..ddfe746d --- /dev/null +++ b/virtualchassis/collector.go @@ -0,0 +1,58 @@ +package virtualchassis + +import ( + "github.com/czerwonk/junos_exporter/collector" + "github.com/czerwonk/junos_exporter/rpc" + "github.com/prometheus/client_golang/prometheus" +) + +const prefix = "junos_virtualchassis_" + +var ( + virtualchassismemberstatus *prometheus.Desc +) + +func init() { + l := []string{"target", "status", "serial", "model", "id", "fpcslot", "role"} + virtualchassismemberstatus = prometheus.NewDesc(prefix+"member_status", "virtualchassis member-status (1: Prsnt, 0: NotPrsnt)", l, nil) +} + +type virtualchassisCollector struct { +} + +// Name returns the name of the collector +func (*virtualchassisCollector) Name() string { + return "virtualchassis" +} + +// NewCollector creates a new collector +func NewCollector() collector.RPCCollector { + return &virtualchassisCollector{} +} + +// Describe describes the metrics +func (*virtualchassisCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- virtualchassismemberstatus +} + +// Collect collects metrics from JunOS +func (c *virtualchassisCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { + statusValues := map[string]int{ + "NotPrsnt": 0, + "Prsnt": 1, + } + + var x = virtualChassisRpc{} + err := client.RunCommandAndParse("show virtual-chassis", &x) + if err != nil { + return err + } + + for _, m := range x.VirtualChassisInformation.MemberList.Member { + l := labelValues + l = append(l, m.Status, m.SerialNumber, m.Model, m.Id, m.FpcSlot, m.Role ) + ch <- prometheus.MustNewConstMetric(virtualchassismemberstatus, prometheus.GaugeValue, float64(statusValues[m.Status]), l...) + } + + return nil +} diff --git a/virtualchassis/rpc.go b/virtualchassis/rpc.go new file mode 100644 index 00000000..f0a5660f --- /dev/null +++ b/virtualchassis/rpc.go @@ -0,0 +1,22 @@ +package virtualchassis + +type virtualChassisRpc struct { + VirtualChassisInformation struct { + VirtualChassisIdInformation struct { + VirtualChassisId string `xml:"virtual-chassis-id"` + VirtualChassisMode string `xml:"virtual-chassis-mode"` + } `xml:"virtual-chassis-id-information"` + MemberList struct { + Member []vcmembers `xml:"member"` + } `xml:"member-list"` + } `xml:"virtual-chassis-information"` +} + +type vcmembers struct { + Status string `xml:"member-status"` + Id string `xml:"member-id"` + FpcSlot string `xml:"fpc-slot"` + SerialNumber string `xml:"member-serial-number"` + Model string `xml:"member-model"` + Role string `xml:"member-role"` +} From 9264ebe07cbcf97fa2e7120d7672b14845c20536 Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Fri, 10 Jun 2022 10:05:03 +0200 Subject: [PATCH 20/23] adding virtual-chassis check --- README.md | 5 ++++ collectors.go | 2 ++ config/config.go | 2 ++ virtualchassis/collector.go | 58 +++++++++++++++++++++++++++++++++++++ virtualchassis/rpc.go | 22 ++++++++++++++ 5 files changed, 89 insertions(+) create mode 100644 virtualchassis/collector.go create mode 100644 virtualchassis/rpc.go diff --git a/README.md b/README.md index 39839201..e609995f 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,11 @@ States map to human readable names like this: 4 = "Ex-Incr" 5 = "Ex-Full" ``` +* VirtualChassis (status of members) +``` +0 = "NotPrsnt" +1 = "NotPrsnt" +``` * VRRP (state per interface) States map to human readable names like this: ``` diff --git a/collectors.go b/collectors.go index 9429bb19..c92ad26d 100644 --- a/collectors.go +++ b/collectors.go @@ -33,6 +33,7 @@ import ( "github.com/czerwonk/junos_exporter/security" "github.com/czerwonk/junos_exporter/storage" "github.com/czerwonk/junos_exporter/system" + "github.com/czerwonk/junos_exporter/virtualchassis" "github.com/czerwonk/junos_exporter/vrrp" "github.com/czerwonk/junos_exporter/vpws" ) @@ -105,6 +106,7 @@ func (c *collectors) initCollectorsForDevices(device *connector.Device) { c.addCollectorIfEnabledForDevice(device, "system", f.System, system.NewCollector) c.addCollectorIfEnabledForDevice(device, "power", f.Power, power.NewCollector) c.addCollectorIfEnabledForDevice(device, "mac", f.MAC, mac.NewCollector) + c.addCollectorIfEnabledForDevice(device, "virtualchassis", f.VirtualChassis, virtualchassis.NewCollector) c.addCollectorIfEnabledForDevice(device, "vrrp", f.VRRP, vrrp.NewCollector) c.addCollectorIfEnabledForDevice(device, "vpws", f.VPWS, vpws.NewCollector) c.addCollectorIfEnabledForDevice(device, "mpls_lsp", f.MPLS_LSP, mpls_lsp.NewCollector) diff --git a/config/config.go b/config/config.go index f2709759..acff763d 100644 --- a/config/config.go +++ b/config/config.go @@ -62,6 +62,7 @@ type FeatureConfig struct { Power bool `yaml:"power,omitempty"` MAC bool `yaml:"mac,omitempty"` MPLS_LSP bool `yaml:"mpls_lsp,omitempty"` + VirtualChassis bool `yaml:"virtualchassis,omitempty"` VPWS bool `yaml:"vpws,omitempty"` VRRP bool `yaml:"vrrp,omitempty"` } @@ -132,6 +133,7 @@ func setDefaultValues(c *Config) { f.Power = false f.MAC = false f.MPLS_LSP = false + f.VirtualChassis = false f.VPWS = false f.VRRP = false f.BFD = false diff --git a/virtualchassis/collector.go b/virtualchassis/collector.go new file mode 100644 index 00000000..ddfe746d --- /dev/null +++ b/virtualchassis/collector.go @@ -0,0 +1,58 @@ +package virtualchassis + +import ( + "github.com/czerwonk/junos_exporter/collector" + "github.com/czerwonk/junos_exporter/rpc" + "github.com/prometheus/client_golang/prometheus" +) + +const prefix = "junos_virtualchassis_" + +var ( + virtualchassismemberstatus *prometheus.Desc +) + +func init() { + l := []string{"target", "status", "serial", "model", "id", "fpcslot", "role"} + virtualchassismemberstatus = prometheus.NewDesc(prefix+"member_status", "virtualchassis member-status (1: Prsnt, 0: NotPrsnt)", l, nil) +} + +type virtualchassisCollector struct { +} + +// Name returns the name of the collector +func (*virtualchassisCollector) Name() string { + return "virtualchassis" +} + +// NewCollector creates a new collector +func NewCollector() collector.RPCCollector { + return &virtualchassisCollector{} +} + +// Describe describes the metrics +func (*virtualchassisCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- virtualchassismemberstatus +} + +// Collect collects metrics from JunOS +func (c *virtualchassisCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { + statusValues := map[string]int{ + "NotPrsnt": 0, + "Prsnt": 1, + } + + var x = virtualChassisRpc{} + err := client.RunCommandAndParse("show virtual-chassis", &x) + if err != nil { + return err + } + + for _, m := range x.VirtualChassisInformation.MemberList.Member { + l := labelValues + l = append(l, m.Status, m.SerialNumber, m.Model, m.Id, m.FpcSlot, m.Role ) + ch <- prometheus.MustNewConstMetric(virtualchassismemberstatus, prometheus.GaugeValue, float64(statusValues[m.Status]), l...) + } + + return nil +} diff --git a/virtualchassis/rpc.go b/virtualchassis/rpc.go new file mode 100644 index 00000000..f0a5660f --- /dev/null +++ b/virtualchassis/rpc.go @@ -0,0 +1,22 @@ +package virtualchassis + +type virtualChassisRpc struct { + VirtualChassisInformation struct { + VirtualChassisIdInformation struct { + VirtualChassisId string `xml:"virtual-chassis-id"` + VirtualChassisMode string `xml:"virtual-chassis-mode"` + } `xml:"virtual-chassis-id-information"` + MemberList struct { + Member []vcmembers `xml:"member"` + } `xml:"member-list"` + } `xml:"virtual-chassis-information"` +} + +type vcmembers struct { + Status string `xml:"member-status"` + Id string `xml:"member-id"` + FpcSlot string `xml:"fpc-slot"` + SerialNumber string `xml:"member-serial-number"` + Model string `xml:"member-model"` + Role string `xml:"member-role"` +} From 53635f38dc16525d6139703f79b88401e30068eb Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Fri, 10 Jun 2022 10:13:29 +0200 Subject: [PATCH 21/23] adding rpc to virtualchassis --- virtualchassis/collector.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/virtualchassis/collector.go b/virtualchassis/collector.go index ddfe746d..52d4731d 100644 --- a/virtualchassis/collector.go +++ b/virtualchassis/collector.go @@ -43,9 +43,16 @@ func (c *virtualchassisCollector) Collect(client *rpc.Client, ch chan<- promethe } var x = virtualChassisRpc{} - err := client.RunCommandAndParse("show virtual-chassis", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil + } + } else { + err := client.RunCommandAndParse("show virtual-chassis", &x) + if err != nil { + return err + } } for _, m := range x.VirtualChassisInformation.MemberList.Member { From d87da7c0d84ffbc7421c9921dc43d61b43739d9d Mon Sep 17 00:00:00 2001 From: Jean-Yves Simon Date: Mon, 11 Jul 2022 12:51:04 +0200 Subject: [PATCH 22/23] ajout interface locale vpws remote --- vpws/collector.go | 6 ++++++ vpws/rpc.go | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/vpws/collector.go b/vpws/collector.go index b9db370f..f5b34039 100644 --- a/vpws/collector.go +++ b/vpws/collector.go @@ -68,6 +68,12 @@ func (c *vpwsCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, ch <- prometheus.MustNewConstMetric(vpwsSid, prometheus.GaugeValue, float64(vpwsSidMap[vSid.Status]), l...) } + + if vIf.RemoteStatus.LocalInterfaceName != "" { + l := append(labelValues, vInst.Name, vInst.RD, vIf.Name, "remote", vIf.RemoteStatus.Sid, vIf.RemoteStatus.LocalInterfaceName, "", "local", "") + ch <- prometheus.MustNewConstMetric(vpwsSid, prometheus.GaugeValue, float64(vpwsStatusMap[vIf.RemoteStatus.LocalInterfaceStatus]), l...) + } + for _, vSid := range vIf.RemoteStatus.SidPeInfo { l := append(labelValues, vInst.Name, vInst.RD, vIf.Name, "remote", vIf.RemoteStatus.Sid, vSid.IP, vSid.Esi, vSid.Mode, vSid.Role) ch <- prometheus.MustNewConstMetric(vpwsSid, prometheus.GaugeValue, float64(vpwsSidMap[vSid.Status]), l...) diff --git a/vpws/rpc.go b/vpws/rpc.go index 959eb619..762daad4 100644 --- a/vpws/rpc.go +++ b/vpws/rpc.go @@ -28,8 +28,10 @@ type vpwsInterface struct { } `xml:"evpn-vpws-service-id-local-status-table>evpn-vpws-sid-local"` RemoteStatus struct { - Sid string `xml:"evpn-vpws-sid-remote-value"` - SidPeInfo []vpwsSidPeInfo `xml:"evpn-vpws-sid-pe-status-table>evpn-vpws-sid-pe-info"` + Sid string `xml:"evpn-vpws-sid-remote-value"` + LocalInterfaceName string `xml:"evpn-vpws-sid-local-interface-name"` + LocalInterfaceStatus string `xml:"evpn-vpws-sid-local-interface-status"` + SidPeInfo []vpwsSidPeInfo `xml:"evpn-vpws-sid-pe-status-table>evpn-vpws-sid-pe-info"` } `xml:"evpn-vpws-service-id-remote-status-table>evpn-vpws-sid-remote"` } From b97d41e5866c64e9493f95219aca96d682481ee0 Mon Sep 17 00:00:00 2001 From: JYS Date: Mon, 11 Jul 2022 13:00:05 +0200 Subject: [PATCH 23/23] typo --- virtualchassis/collector.go | 1 + 1 file changed, 1 insertion(+) diff --git a/virtualchassis/collector.go b/virtualchassis/collector.go index f6facb9f..52d4731d 100644 --- a/virtualchassis/collector.go +++ b/virtualchassis/collector.go @@ -53,6 +53,7 @@ func (c *virtualchassisCollector) Collect(client *rpc.Client, ch chan<- promethe if err != nil { return err } + } for _, m := range x.VirtualChassisInformation.MemberList.Member { l := labelValues