Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Netconf rpc - add as feature #190

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8bc33fa
changing to netconf client
lethalwp May 13, 2022
b3b7b77
more to netconf converted
lethalwp May 13, 2022
1c272b9
converting more commands
lethalwp May 14, 2022
a224c52
Merge branch 'add-basic-mpls-lsp' into netconf-rpc
lethalwp May 14, 2022
dc46af3
correction on session pointer is connected
lethalwp May 16, 2022
89911ca
netconf firewall and ipsec commands fixes
lethalwp May 17, 2022
980f92a
Merge branch 'netconf-rpc' of github.com:lethalwp/junos_exporter into…
lethalwp May 17, 2022
3d8452b
merging upstream
lethalwp May 17, 2022
24a38fe
Merge branch 'czerwonk-master' into netconf-rpc
lethalwp May 17, 2022
60c785b
for merge
lethalwp May 17, 2022
300f398
rename lacp labels to match other interface output
lethalwp May 17, 2022
012a62c
ipsec fix
lethalwp May 18, 2022
30afee9
Merge branch 'czerwonk:master' into netconf-rpc
lethalwp May 23, 2022
a62d0ea
go back to the previous ssh paradigm, with a better keepalive
lethalwp May 24, 2022
7683d02
go back to the previous ssh paradigm, with a better keepalive
lethalwp May 24, 2022
96741fc
cosmeting, realign
lethalwp May 24, 2022
a374256
cosmeting, realign
lethalwp May 24, 2022
5b9a3dd
adding netconf as a feature #1
lethalwp May 24, 2022
b7e41b1
adding netconf as a feature #2
lethalwp May 24, 2022
59d315d
adding netconf as a feature
lethalwp May 24, 2022
d00882c
ipsec compatibility
lethalwp May 24, 2022
f97959e
ipsec compatibility
lethalwp May 24, 2022
f0bf369
keep the netconf session active - performance
lethalwp May 25, 2022
0b4625b
adding virtual-chassis check
lethalwp Jun 10, 2022
9264ebe
adding virtual-chassis check
lethalwp Jun 10, 2022
53635f3
adding rpc to virtualchassis
lethalwp Jun 10, 2022
d87da7c
ajout interface locale vpws remote
lethalwp Jul 11, 2022
0875b93
resynch with master
lethalwp Jul 11, 2022
b97d41e
typo
lethalwp Jul 11, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
```
Expand Down
26 changes: 20 additions & 6 deletions accounting/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +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)
if err != nil {
return nil, err
if client.Netconf {
err := client.RunCommandAndParse("<get-service-accounting-error-inline-jflow-information></get-service-accounting-error-inline-jflow-information>", &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 != "" {
Expand All @@ -119,9 +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)
if err != nil {
return nil, err
if client.Netconf {
err := client.RunCommandAndParse("<get-service-accounting-error-inline-jflow-information><inline-jflow-error-information>0</inline-jflow-error-information></get-service-accounting-error-inline-jflow-information>", &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{
Expand Down
7 changes: 7 additions & 0 deletions alarm/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ func (c *alarmCollector) alarmCounter(client *rpc.Client) (*AlarmCounter, *[]Ala
"show chassis alarms",
}

if client.Netconf {
cmds = []string{
"<get-system-alarm-information/>",
"<get-alarm-information/>",
}
}

var alarms []AlarmDetails

messages := make(map[string]interface{})
Expand Down
15 changes: 11 additions & 4 deletions bfd/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,17 @@ 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)
if err != nil {
return err
var x = bfdRpc{}
if client.Netconf {
err := client.RunCommandAndParse("<get-bfd-session-information><extensive/></get-bfd-session-information>", &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 {
Expand Down
11 changes: 8 additions & 3 deletions bgp/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,21 @@ 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)
if client.Netconf {
cmd.WriteString("<get-bgp-neighbor-information/>")
} 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)
}
Expand Down
2 changes: 2 additions & 0 deletions collectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@ 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"`
MPLS_LSP bool `yaml:"mpls_lsp,omitempty"`
VirtualChassis bool `yaml:"virtualchassis,omitempty"`
VPWS bool `yaml:"vpws,omitempty"`
VRRP bool `yaml:"vrrp,omitempty"`
}
Expand Down Expand Up @@ -127,9 +129,11 @@ 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
f.VirtualChassis = false
f.VPWS = false
f.VRRP = false
f.BFD = false
Expand Down
68 changes: 67 additions & 1 deletion connector/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import (
"bytes"
"net"
"sync"

"fmt"
"io"
"github.com/pkg/errors"

"golang.org/x/crypto/ssh"
"github.com/Juniper/go-netconf/netconf"
)

// SSHConnection encapsulates the connection to the device
Expand All @@ -17,10 +19,27 @@ type SSHConnection struct {
conn net.Conn
mu sync.Mutex
done chan struct{}
netconf bool
netconfsession *netconf.Session
}

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) {
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()

Expand All @@ -45,6 +64,53 @@ func (c *SSHConnection) RunCommand(cmd string) ([]byte, error) {
return b.Bytes(), nil
}


func (c *SSHConnection) RunCommandNETCONF(cmd string) ([]byte, error) {
c.mu.Lock()
defer c.mu.Unlock()
var err error

if c.client == nil {
return nil, errors.New("not connected")
}

t := &TransportSSH{}
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)
}

reply, err := c.netconfsession.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")
}

return []byte(reply.RawReply), nil
}

func (c *SSHConnection) isConnected() bool {
return c.conn != nil
}
Expand Down
9 changes: 6 additions & 3 deletions connector/connection_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ func WithKeepAliveInterval(d time.Duration) Option {
}
}


// 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) {
m.keepAliveTimeout = d
}
}


// SSHConnectionManager manages SSH connections to different devices
type SSHConnectionManager struct {
connections map[string]*SSHConnection
Expand All @@ -65,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()

Expand All @@ -77,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
Expand All @@ -91,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)

Expand Down
Loading