Skip to content

Commit

Permalink
Merge pull request czerwonk#2 from czerwonk/rip_snmp
Browse files Browse the repository at this point in the history
R.I.P. SNMP
  • Loading branch information
czerwonk authored Jan 6, 2018
2 parents bc59255 + fdc773d commit 8d7042b
Show file tree
Hide file tree
Showing 46 changed files with 565 additions and 6,854 deletions.
7 changes: 1 addition & 6 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,3 @@
[[constraint]]
branch = "master"
name = "github.com/prometheus/common"

[[constraint]]
name = "github.com/soniah/gosnmp"
revision = "55acec044875c95a3fb3fbc37160cb766501939f"
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
[![Docker Build Statu](https://img.shields.io/docker/build/czerwonk/junos_exporter.svg)](https://hub.docker.com/r/czerwonk/junos_exporter/builds)
[![Go Report Card](https://goreportcard.com/badge/github.com/czerwonk/junos_exporter)](https://goreportcard.com/report/github.com/czerwonk/junos_exporter)

Exporter for metrics from devices running JunOS (via SNMP) https://prometheus.io/
Exporter for metrics from devices running JunOS (via SSH) https://prometheus.io/

## Remarks
this is an early version

This project is an alternative approach for collecting metrics from Juniper devices.
The set of metrics is minimal to increase performance.
We (a few friends from the Freifunk communiy and myself) used the generic snmp_exporter before.
Expand All @@ -16,15 +14,29 @@ We wanted to have an KIS and vendor specific exporter instead.
This approach should allow us to scrape our metrics in a very time efficient way.
For this reason this project was started.

## Important notice for users of version < 0.5
In version 0.5 SNMP was replaced by SSH. This is was a breaking change (metric names were kept).
All SNMP related parameters were removed at this point.
Please have a look on the new SSH related parameters and update your service units accordingly.

## Install
```
go get -u github.com/czerwonk/junos_exporter
```

## Usage
In this example we want to scrape 3 hosts:
* Host 1 (DNS: host1.example.com, Port: 22)
* Host 2 (DNS: host2.example.com, Port: 2233)
* Host 3 (IP: 172.16.0.1, Port: 22)

```
./junos_exporter -ssh.targets="host1.example.com,host2.example.com:2233,172.16.0.1" -ssh.keyfile=junos_exporter
```

## Third Party Components
This software uses components of the following projects
* Prometheus Go client library (https://github.com/prometheus/client_golang)
* gosnmp (https://github.com/soniah/gosnmp)

## License
(c) Daniel Czerwonk, 2017. Licensed under [MIT](LICENSE) license.
Expand Down
36 changes: 36 additions & 0 deletions alarm/alarm_collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package alarm

import "github.com/prometheus/client_golang/prometheus"

const prefix = "junos_alarms_"

var (
alarmsYellowCount *prometheus.Desc
alarmsRedCount *prometheus.Desc
)

func init() {
l := []string{"target"}
alarmsYellowCount = prometheus.NewDesc(prefix+"yellow_count", "Number of yollow alarms (not silenced)", l, nil)
alarmsRedCount = prometheus.NewDesc(prefix+"red_count", "Number of red alarms (not silenced)", l, nil)
}

type AlarmCollector struct {
}

func (*AlarmCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- alarmsYellowCount
ch <- alarmsRedCount
}

func (c *AlarmCollector) Collect(datasource AlarmDatasource, ch chan<- prometheus.Metric, labelValues []string) error {
counter, err := datasource.AlarmCounter()
if err != nil {
return err
}

ch <- prometheus.MustNewConstMetric(alarmsYellowCount, prometheus.GaugeValue, counter.YellowCount, labelValues...)
ch <- prometheus.MustNewConstMetric(alarmsRedCount, prometheus.GaugeValue, counter.RedCount, labelValues...)

return nil
}
6 changes: 6 additions & 0 deletions alarm/alarm_counter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package alarm

type AlarmCounter struct {
YellowCount float64
RedCount float64
}
5 changes: 5 additions & 0 deletions alarm/datasource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package alarm

type AlarmDatasource interface {
AlarmCounter() (*AlarmCounter, error)
}
73 changes: 73 additions & 0 deletions bgp/bgp_collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package bgp

import "github.com/prometheus/client_golang/prometheus"

const prefix string = "junos_bgp_seesion_"

var (
upDesc *prometheus.Desc
receivedPrefixesDesc *prometheus.Desc
acceptedPrefixesDesc *prometheus.Desc
rejectedPrefixesDesc *prometheus.Desc
activePrefixesDesc *prometheus.Desc
inputMessagesDesc *prometheus.Desc
outputMessagesDesc *prometheus.Desc
flapsDesc *prometheus.Desc
)

func init() {
l := []string{"target", "asn", "ip"}
upDesc = prometheus.NewDesc(prefix+"up", "Session is up (1 = Established)", l, nil)
receivedPrefixesDesc = prometheus.NewDesc(prefix+"prefixes_received_count", "Number of received prefixes", l, nil)
acceptedPrefixesDesc = prometheus.NewDesc(prefix+"prefixes_accepted_count", "Number of accepted prefixes", l, nil)
rejectedPrefixesDesc = prometheus.NewDesc(prefix+"prefixes_rejected_count", "Number of rejected prefixes", l, nil)
activePrefixesDesc = prometheus.NewDesc(prefix+"prefixes_active_count", "Number of active prefixes (best route in RIB)", l, nil)
inputMessagesDesc = prometheus.NewDesc(prefix+"messages_input_count", "Number of received messages", l, nil)
outputMessagesDesc = prometheus.NewDesc(prefix+"messages_output_count", "Number of transmitted messages", l, nil)
flapsDesc = prometheus.NewDesc(prefix+"flap_count", "Number of session flaps", l, nil)
}

type BgpCollector struct {
}

func (*BgpCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- upDesc
ch <- receivedPrefixesDesc
ch <- acceptedPrefixesDesc
ch <- rejectedPrefixesDesc
ch <- activePrefixesDesc
ch <- inputMessagesDesc
ch <- outputMessagesDesc
ch <- flapsDesc
}

func (c *BgpCollector) Collect(datasource BgpDatasource, ch chan<- prometheus.Metric, labelValues []string) error {
sessions, err := datasource.BgpSessions()
if err != nil {
return err
}

for _, s := range sessions {
c.collectForSession(s, ch, labelValues)
}

return nil
}

func (*BgpCollector) collectForSession(s *BgpSession, ch chan<- prometheus.Metric, labelValues []string) {
l := append(labelValues, []string{s.Asn, s.Ip}...)

up := 0
if s.Up {
up = 1
}

ch <- prometheus.MustNewConstMetric(upDesc, prometheus.GaugeValue, float64(up), l...)
ch <- prometheus.MustNewConstMetric(receivedPrefixesDesc, prometheus.GaugeValue, float64(s.ReceivedPrefixes), l...)
ch <- prometheus.MustNewConstMetric(acceptedPrefixesDesc, prometheus.GaugeValue, float64(s.AcceptedPrefixes), l...)
ch <- prometheus.MustNewConstMetric(rejectedPrefixesDesc, prometheus.GaugeValue, float64(s.RejectedPrefixes), l...)
ch <- prometheus.MustNewConstMetric(activePrefixesDesc, prometheus.GaugeValue, float64(s.ActivePrefixes), l...)
ch <- prometheus.MustNewConstMetric(inputMessagesDesc, prometheus.GaugeValue, float64(s.InputMessages), l...)
ch <- prometheus.MustNewConstMetric(outputMessagesDesc, prometheus.GaugeValue, float64(s.OutputMessages), l...)
ch <- prometheus.MustNewConstMetric(flapsDesc, prometheus.GaugeValue, float64(s.Flaps), l...)
}
14 changes: 14 additions & 0 deletions bgp/bgp_session.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package bgp

type BgpSession struct {
Ip string
Asn string
Up bool
ReceivedPrefixes float64
AcceptedPrefixes float64
RejectedPrefixes float64
ActivePrefixes float64
InputMessages float64
OutputMessages float64
Flaps float64
}
5 changes: 5 additions & 0 deletions bgp/datasource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package bgp

type BgpDatasource interface {
BgpSessions() ([]*BgpSession, error)
}
85 changes: 85 additions & 0 deletions connector/connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package connector

import (
"bytes"
"io/ioutil"
"strings"

"golang.org/x/crypto/ssh"
)

func NewSshConnection(host, user, keyFile string) (*SshConnection, error) {
if !strings.Contains(host, ":") {
host = host + ":22"
}

c := &SshConnection{Host: host}
err := c.Connect(user, keyFile)
if err != nil {
return nil, err
}

return c, nil
}

type SshConnection struct {
conn *ssh.Client
Host string
}

func (c *SshConnection) Connect(user, keyFile string) error {
pk, err := loadPublicKeyFile(keyFile)
if err != nil {
return err
}

config := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{pk},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}

c.conn, err = ssh.Dial("tcp", c.Host, config)
return err
}

func (c *SshConnection) RunCommand(cmd string) ([]byte, error) {
session, err := c.conn.NewSession()
if err != nil {
return nil, err
}
defer session.Close()

var b = &bytes.Buffer{}
session.Stdout = b

err = session.Run(cmd)
if err != nil {
return nil, err
}

return b.Bytes(), nil
}

func (c *SshConnection) Close() {
if c.conn == nil {
return
}

c.conn.Close()
c.conn = nil
}

func loadPublicKeyFile(file string) (ssh.AuthMethod, error) {
b, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}

key, err := ssh.ParsePrivateKey(b)
if err != nil {
return nil, err
}

return ssh.PublicKeys(key), nil
}
9 changes: 0 additions & 9 deletions convert.go

This file was deleted.

5 changes: 5 additions & 0 deletions interfaces/datasource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package interfaces

type InterfaceStatsDatasource interface {
InterfaceStats() ([]*InterfaceStats, error)
}
62 changes: 62 additions & 0 deletions interfaces/interface_collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package interfaces

import "github.com/prometheus/client_golang/prometheus"

const prefix = "junos_interface_"

var (
receiveBytesDesc *prometheus.Desc
receiveErrorsDesc *prometheus.Desc
receiveDropsDesc *prometheus.Desc
transmitBytesDesc *prometheus.Desc
transmitErrorsDesc *prometheus.Desc
transmitDropsDesc *prometheus.Desc
)

func init() {
l := []string{"target", "name", "description", "mac"}
receiveBytesDesc = prometheus.NewDesc(prefix+"receive_bytes", "Received data in bytes", l, nil)
receiveErrorsDesc = prometheus.NewDesc(prefix+"receive_errors", "Number of errors caused by incoming packets", l, nil)
receiveDropsDesc = prometheus.NewDesc(prefix+"receive_drops", "Number of dropped incoming packets", l, nil)
transmitBytesDesc = prometheus.NewDesc(prefix+"transmit_bytes", "Transmitted data in bytes", l, nil)
transmitErrorsDesc = prometheus.NewDesc(prefix+"transmit_errors", "Number of errors caused by outgoing packets", l, nil)
transmitDropsDesc = prometheus.NewDesc(prefix+"transmit_drops", "Number of dropped outgoing packets", l, nil)
}

type InterfaceCollector struct {
}

func (*InterfaceCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- receiveBytesDesc
ch <- receiveErrorsDesc
ch <- receiveDropsDesc
ch <- transmitBytesDesc
ch <- transmitDropsDesc
ch <- transmitErrorsDesc
}

func (c *InterfaceCollector) Collect(datasource InterfaceStatsDatasource, ch chan<- prometheus.Metric, labelValues []string) error {
stats, err := datasource.InterfaceStats()
if err != nil {
return err
}

for _, s := range stats {
c.collectForInterface(s, ch, labelValues)
}

return nil
}

func (*InterfaceCollector) collectForInterface(s *InterfaceStats, ch chan<- prometheus.Metric, labelValues []string) {
l := append(labelValues, []string{s.Name, s.Description, s.Mac}...)
ch <- prometheus.MustNewConstMetric(receiveBytesDesc, prometheus.GaugeValue, s.ReceiveBytes, l...)
ch <- prometheus.MustNewConstMetric(transmitBytesDesc, prometheus.GaugeValue, s.TransmitBytes, l...)

if s.IsPhysical {
ch <- prometheus.MustNewConstMetric(transmitErrorsDesc, prometheus.GaugeValue, s.TransmitErrors, l...)
ch <- prometheus.MustNewConstMetric(transmitDropsDesc, prometheus.GaugeValue, s.TransmitDrops, l...)
ch <- prometheus.MustNewConstMetric(receiveErrorsDesc, prometheus.GaugeValue, s.ReceiveErrors, l...)
ch <- prometheus.MustNewConstMetric(receiveDropsDesc, prometheus.GaugeValue, s.ReceiveDrops, l...)
}
}
14 changes: 14 additions & 0 deletions interfaces/interface_stats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package interfaces

type InterfaceStats struct {
Name string
Description string
Mac string
IsPhysical bool
ReceiveBytes float64
ReceiveErrors float64
ReceiveDrops float64
TransmitBytes float64
TransmitErrors float64
TransmitDrops float64
}
Loading

0 comments on commit 8d7042b

Please sign in to comment.