Browse Source

Add network-exporter

Change-Id: I51a951dc94cf488685d2b80b90b9235173c660a2
changes/30/728530/4
Guilherme Steinmüller 2 years ago
committed by Mohammed Naser
parent
commit
cdbd6909d6
  1. 34
      .zuul.yaml
  2. 8
      Dockerfile
  3. 28
      README.md
  4. 90
      collector/bgp.go
  5. 61
      collector/bgp_test.go
  6. 101
      collector/device.go
  7. 50
      collector/device_test.go
  8. 151
      collector/interface.go
  9. 89
      collector/interface_test.go
  10. 77
      config/file.go
  11. 52
      config/file_test.go
  12. 20
      go.mod
  13. 130
      go.sum
  14. 76
      main.go
  15. 133
      network_api/arista.go
  16. 45
      network_api/interface.go
  17. 163
      network_api/mikrotik.go
  18. 237
      network_api/snmp.go

34
.zuul.yaml

@ -0,0 +1,34 @@
- job:
name: network_exporter:image:build
parent: vexxhost-build-docker-image
provides: network_exporter:image
vars: &network_exporter_images
docker_images:
- context: .
repository: vexxhost/network-exporter
- job:
name: network_exporter:image:upload
parent: vexxhost-upload-docker-image
provides: network_exporter:image
vars: *network_exporter_images
- job:
name: network_exporter:image:promote
parent: vexxhost-promote-docker-image
vars: *network_exporter_images
- project:
check:
jobs:
- golangci-lint
- golang-go-test
- network_exporter:image:build
gate:
jobs:
- golangci-lint
- golang-go-test
- network_exporter:image:upload
promote:
jobs:
- network_exporter:image:promote

8
Dockerfile

@ -0,0 +1,8 @@
FROM golang:1.13 AS builder
WORKDIR /go/src/app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
FROM scratch
COPY --from=builder /go/src/app/network_exporter /network_exporter
ENTRYPOINT ["/network_exporter"]

28
README.md

@ -0,0 +1,28 @@
# Network Exporter
Prometheus exporter for a variety of network devices using their respective
APIs. The exporter implements a generic standarized way of delivering
information regardless of the device used, which makes it useful for
environments with heterogeous network infrastructure.
## Supported Devices
- Arista
- MikroTik
## Supported Collectors
- BGP
- Device Info
- Interface statistics
## SNMP
There is an extremely useful and powerful SNMP exporter which already exists
however we've found that we could never consistently retrieve information
across several device manufactureres (for example, some do not expose BGP
peer information over SNMP).
The library embedded in here does the majority of the work of generalizing
that information retrieval so you can have very clear and clean metrics
regardless of the devices used in your environment.

90
collector/bgp.go

@ -0,0 +1,90 @@
package collector
import (
"strconv"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
"opendev.org/vexxhost/network_exporter/network_api"
)
type BgpCollector struct {
prometheus.Collector
API network_api.API
PrefixCount *prometheus.Desc
PeerUp *prometheus.Desc
MessagesReceived *prometheus.Desc
MessagesSent *prometheus.Desc
}
func NewBgpCollector(device string, api network_api.API) *BgpCollector {
return &BgpCollector{
API: api,
PrefixCount: prometheus.NewDesc(
"network_bgp_prefix_count",
"BGP prefix count",
[]string{"as", "remote"}, prometheus.Labels{"device": device},
),
PeerUp: prometheus.NewDesc(
"network_bgp_up",
"BGP session is established (up = 1)",
[]string{"as", "remote"}, prometheus.Labels{"device": device},
),
MessagesReceived: prometheus.NewDesc(
"network_bgp_messages_received",
"BGP messages received",
[]string{"as", "remote"}, prometheus.Labels{"device": device},
),
MessagesSent: prometheus.NewDesc(
"network_bgp_messages_sent",
"BGP messages sent",
[]string{"as", "remote"}, prometheus.Labels{"device": device},
),
}
}
func (c *BgpCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.PrefixCount
ch <- c.PeerUp
ch <- c.MessagesReceived
ch <- c.MessagesSent
}
func (c *BgpCollector) Collect(ch chan<- prometheus.Metric) {
peers, err := c.API.Peers()
if err != nil {
log.Errorln(err)
return
}
for _, peer := range peers {
up := 0
if peer.Up {
up = 1
}
ch <- prometheus.MustNewConstMetric(
c.PrefixCount, prometheus.GaugeValue,
peer.PrefixCount,
strconv.FormatInt(peer.AS, 10), peer.Remote,
)
ch <- prometheus.MustNewConstMetric(
c.PeerUp, prometheus.GaugeValue,
float64(up),
strconv.FormatInt(peer.AS, 10), peer.Remote,
)
ch <- prometheus.MustNewConstMetric(
c.MessagesReceived, prometheus.GaugeValue,
peer.MessagesReceived,
strconv.FormatInt(peer.AS, 10), peer.Remote,
)
ch <- prometheus.MustNewConstMetric(
c.MessagesSent, prometheus.GaugeValue,
peer.MessagesSent,
strconv.FormatInt(peer.AS, 10), peer.Remote,
)
}
}

61
collector/bgp_test.go

@ -0,0 +1,61 @@
package collector
import (
"strings"
"testing"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
"opendev.org/vexxhost/network_exporter/network_api"
)
var bgpExpected = `
# HELP network_bgp_messages_received BGP messages received
# TYPE network_bgp_messages_received gauge
network_bgp_messages_received{as="65001",device="fake-dev",remote="10.10.10.1"} 1234
network_bgp_messages_received{as="65002",device="fake-dev",remote="10.10.10.2"} 0
# HELP network_bgp_messages_sent BGP messages sent
# TYPE network_bgp_messages_sent gauge
network_bgp_messages_sent{as="65001",device="fake-dev",remote="10.10.10.1"} 6789
network_bgp_messages_sent{as="65002",device="fake-dev",remote="10.10.10.2"} 0
# HELP network_bgp_prefix_count BGP prefix count
# TYPE network_bgp_prefix_count gauge
network_bgp_prefix_count{as="65001",device="fake-dev",remote="10.10.10.1"} 1337
network_bgp_prefix_count{as="65002",device="fake-dev",remote="10.10.10.2"} 0
# HELP network_bgp_up BGP session is established (up = 1)
# TYPE network_bgp_up gauge
network_bgp_up{as="65001",device="fake-dev",remote="10.10.10.1"} 1
network_bgp_up{as="65002",device="fake-dev",remote="10.10.10.2"} 0
`
type FakeBgpApi struct {
network_api.API
}
func (a *FakeBgpApi) Peers() ([]network_api.BgpPeer, error) {
return []network_api.BgpPeer{
network_api.BgpPeer{
Up: true,
AS: 65001,
Remote: "10.10.10.1",
PrefixCount: float64(1337),
MessagesReceived: float64(1234),
MessagesSent: float64(6789),
},
network_api.BgpPeer{
Up: false,
AS: 65002,
Remote: "10.10.10.2",
PrefixCount: float64(0),
MessagesReceived: float64(0),
MessagesSent: float64(0),
},
}, nil
}
func TestBGPCollect(t *testing.T) {
collector := NewBgpCollector("fake-dev", &FakeBgpApi{})
err := testutil.CollectAndCompare(collector, strings.NewReader(bgpExpected))
assert.NoError(t, err)
}

101
collector/device.go

@ -0,0 +1,101 @@
package collector
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
"opendev.org/vexxhost/network_exporter/network_api"
)
type DeviceCollector struct {
prometheus.Collector
API network_api.API
Up *prometheus.Desc
Info *prometheus.Desc
Uptime *prometheus.Desc
FreeMemory *prometheus.Desc
TotalMemory *prometheus.Desc
}
func NewDeviceCollector(device string, api network_api.API) *DeviceCollector {
return &DeviceCollector{
API: api,
Up: prometheus.NewDesc(
"network_device_up",
"Network device up",
[]string{}, prometheus.Labels{"device": device},
),
Info: prometheus.NewDesc(
"network_device_info",
"Network device info",
[]string{"model", "serial", "version"}, prometheus.Labels{"device": device},
),
Uptime: prometheus.NewDesc(
"network_device_uptime",
"Network device uptime",
[]string{}, prometheus.Labels{"device": device},
),
FreeMemory: prometheus.NewDesc(
"network_device_free_memory",
"Network device free memory",
[]string{}, prometheus.Labels{"device": device},
),
TotalMemory: prometheus.NewDesc(
"network_device_total_memory",
"Network device total memory",
[]string{}, prometheus.Labels{"device": device},
),
}
}
func (c *DeviceCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.Up
ch <- c.Info
ch <- c.Uptime
ch <- c.FreeMemory
ch <- c.TotalMemory
}
func (c *DeviceCollector) Collect(ch chan<- prometheus.Metric) {
info, err := c.API.Info()
if err != nil {
ch <- prometheus.MustNewConstMetric(
c.Up, prometheus.GaugeValue,
float64(0),
)
log.Errorln(err)
return
}
ch <- prometheus.MustNewConstMetric(
c.Up, prometheus.GaugeValue,
float64(1),
)
ch <- prometheus.MustNewConstMetric(
c.Info, prometheus.GaugeValue,
float64(1),
info.Model, info.Serial, info.Version,
)
ch <- prometheus.MustNewConstMetric(
c.Uptime, prometheus.GaugeValue,
info.Uptime,
)
ch <- prometheus.MustNewConstMetric(
c.FreeMemory, prometheus.GaugeValue,
info.FreeMemory,
)
ch <- prometheus.MustNewConstMetric(
c.TotalMemory, prometheus.GaugeValue,
info.TotalMemory,
)
}

50
collector/device_test.go

@ -0,0 +1,50 @@
package collector
import (
"strings"
"testing"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
"opendev.org/vexxhost/network_exporter/network_api"
)
var deviceExpected = `
# HELP network_device_free_memory Network device free memory
# TYPE network_device_free_memory gauge
network_device_free_memory{device="fake-dev"} 1.024e+06
# HELP network_device_info Network device info
# TYPE network_device_info gauge
network_device_info{device="fake-dev",model="fast",serial="1337",version="1.33.7"} 1
# HELP network_device_total_memory Network device total memory
# TYPE network_device_total_memory gauge
network_device_total_memory{device="fake-dev"} 2.048e+06
# HELP network_device_up Network device up
# TYPE network_device_up gauge
network_device_up{device="fake-dev"} 1
# HELP network_device_uptime Network device uptime
# TYPE network_device_uptime gauge
network_device_uptime{device="fake-dev"} 542594
`
type FakeInfoApi struct {
network_api.API
}
func (a *FakeInfoApi) Info() (*network_api.Info, error) {
return &network_api.Info{
Uptime: 542594,
FreeMemory: 1024000,
TotalMemory: 2048000,
Model: "fast",
Serial: "1337",
Version: "1.33.7",
}, nil
}
func TestDeviceCollect(t *testing.T) {
collector := NewDeviceCollector("fake-dev", &FakeInfoApi{})
err := testutil.CollectAndCompare(collector, strings.NewReader(deviceExpected))
assert.NoError(t, err)
}

151
collector/interface.go

@ -0,0 +1,151 @@
package collector
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
"opendev.org/vexxhost/network_exporter/network_api"
)
type InterfaceCollector struct {
prometheus.Collector
API network_api.API
Info *prometheus.Desc
RxPackets *prometheus.Desc
RxBits *prometheus.Desc
RxDrops *prometheus.Desc
RxErrors *prometheus.Desc
TxPackets *prometheus.Desc
TxBits *prometheus.Desc
TxDrops *prometheus.Desc
TxErrors *prometheus.Desc
}
func NewInterfaceCollector(device string, api network_api.API) *InterfaceCollector {
return &InterfaceCollector{
API: api,
Info: prometheus.NewDesc(
"network_interface_info",
"Interface information",
[]string{"interface", "description"}, prometheus.Labels{"device": device},
),
RxPackets: prometheus.NewDesc(
"network_interface_rx_packets",
"Number of packets received",
[]string{"interface"}, prometheus.Labels{"device": device},
),
RxBits: prometheus.NewDesc(
"network_interface_rx_bits",
"Number of bits received",
[]string{"interface"}, prometheus.Labels{"device": device},
),
RxDrops: prometheus.NewDesc(
"network_interface_rx_drops",
"Number of receive packet drops",
[]string{"interface"}, prometheus.Labels{"device": device},
),
RxErrors: prometheus.NewDesc(
"network_interface_rx_errors",
"Number of receive packet errors",
[]string{"interface"}, prometheus.Labels{"device": device},
),
TxPackets: prometheus.NewDesc(
"network_interface_tx_packets",
"Number of packets transmitted",
[]string{"interface"}, prometheus.Labels{"device": device},
),
TxBits: prometheus.NewDesc(
"network_interface_tx_bits",
"Number of bits transmitted",
[]string{"interface"}, prometheus.Labels{"device": device},
),
TxDrops: prometheus.NewDesc(
"network_interface_tx_drops",
"Number of transmit packet drops",
[]string{"interface"}, prometheus.Labels{"device": device},
),
TxErrors: prometheus.NewDesc(
"network_interface_tx_errors",
"Number of transmit packet errors",
[]string{"interface"}, prometheus.Labels{"device": device},
),
}
}
func (c *InterfaceCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.Info
ch <- c.RxPackets
ch <- c.RxBits
ch <- c.RxDrops
ch <- c.RxErrors
ch <- c.TxPackets
ch <- c.TxBits
ch <- c.TxDrops
ch <- c.TxErrors
}
func (c *InterfaceCollector) Collect(ch chan<- prometheus.Metric) {
interfaces, err := c.API.Interfaces()
if err != nil {
log.Errorln(err)
return
}
for _, iface := range interfaces {
ch <- prometheus.MustNewConstMetric(
c.Info, prometheus.GaugeValue,
float64(1),
iface.Name, iface.Description,
)
ch <- prometheus.MustNewConstMetric(
c.RxPackets, prometheus.CounterValue,
iface.RxPackets,
iface.Name,
)
ch <- prometheus.MustNewConstMetric(
c.RxBits, prometheus.CounterValue,
iface.RxBits,
iface.Name,
)
ch <- prometheus.MustNewConstMetric(
c.RxDrops, prometheus.CounterValue,
iface.RxDrops,
iface.Name,
)
ch <- prometheus.MustNewConstMetric(
c.RxErrors, prometheus.CounterValue,
iface.RxErrors,
iface.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TxPackets, prometheus.CounterValue,
iface.TxPackets,
iface.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TxBits, prometheus.CounterValue,
iface.TxBits,
iface.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TxDrops, prometheus.CounterValue,
iface.TxDrops,
iface.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TxErrors, prometheus.CounterValue,
iface.TxErrors,
iface.Name,
)
}
}

89
collector/interface_test.go

@ -0,0 +1,89 @@
package collector
import (
"strings"
"testing"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
"opendev.org/vexxhost/network_exporter/network_api"
)
var interfaceExpected = `
# HELP network_interface_info Interface information
# TYPE network_interface_info gauge
network_interface_info{description="bad server",device="fake-dev",interface="Ethernet2"} 1
network_interface_info{description="uplink",device="fake-dev",interface="Ethernet1"} 1
# HELP network_interface_rx_bits Number of bits received
# TYPE network_interface_rx_bits counter
network_interface_rx_bits{device="fake-dev",interface="Ethernet1"} 6452523
network_interface_rx_bits{device="fake-dev",interface="Ethernet2"} 2345
# HELP network_interface_rx_drops Number of receive packet drops
# TYPE network_interface_rx_drops counter
network_interface_rx_drops{device="fake-dev",interface="Ethernet1"} 0
network_interface_rx_drops{device="fake-dev",interface="Ethernet2"} 1
# HELP network_interface_rx_errors Number of receive packet errors
# TYPE network_interface_rx_errors counter
network_interface_rx_errors{device="fake-dev",interface="Ethernet1"} 0
network_interface_rx_errors{device="fake-dev",interface="Ethernet2"} 9
# HELP network_interface_rx_packets Number of packets received
# TYPE network_interface_rx_packets counter
network_interface_rx_packets{device="fake-dev",interface="Ethernet1"} 54325
network_interface_rx_packets{device="fake-dev",interface="Ethernet2"} 1234
# HELP network_interface_tx_bits Number of bits transmitted
# TYPE network_interface_tx_bits counter
network_interface_tx_bits{device="fake-dev",interface="Ethernet1"} 7443533453
network_interface_tx_bits{device="fake-dev",interface="Ethernet2"} 6345
# HELP network_interface_tx_drops Number of transmit packet drops
# TYPE network_interface_tx_drops counter
network_interface_tx_drops{device="fake-dev",interface="Ethernet1"} 0
network_interface_tx_drops{device="fake-dev",interface="Ethernet2"} 4
# HELP network_interface_tx_errors Number of transmit packet errors
# TYPE network_interface_tx_errors counter
network_interface_tx_errors{device="fake-dev",interface="Ethernet1"} 0
network_interface_tx_errors{device="fake-dev",interface="Ethernet2"} 6
# HELP network_interface_tx_packets Number of packets transmitted
# TYPE network_interface_tx_packets counter
network_interface_tx_packets{device="fake-dev",interface="Ethernet1"} 3454534
network_interface_tx_packets{device="fake-dev",interface="Ethernet2"} 9876
`
type FakeInterfaceApi struct {
network_api.API
}
func (a *FakeInterfaceApi) Interfaces() ([]network_api.Interface, error) {
return []network_api.Interface{
network_api.Interface{
Name: "Ethernet1",
Description: "uplink",
RxPackets: 54325,
RxBits: 6452523,
RxDrops: 0,
RxErrors: 0,
TxPackets: 3454534,
TxBits: 7443533453,
TxDrops: 0,
TxErrors: 0,
},
network_api.Interface{
Name: "Ethernet2",
Description: "bad server",
RxPackets: 1234,
RxBits: 2345,
RxDrops: 1,
RxErrors: 9,
TxPackets: 9876,
TxBits: 6345,
TxDrops: 4,
TxErrors: 6,
},
}, nil
}
func TestInterfaceCollect(t *testing.T) {
collector := NewInterfaceCollector("fake-dev", &FakeInterfaceApi{})
err := testutil.CollectAndCompare(collector, strings.NewReader(interfaceExpected))
assert.NoError(t, err)
}

77
config/file.go

@ -0,0 +1,77 @@
package config
import (
"fmt"
"io"
"io/ioutil"
"opendev.org/vexxhost/network_exporter/network_api"
"gopkg.in/yaml.v2"
)
type Config struct {
Devices []DeviceConfig `yaml:"devices"`
}
type DeviceConfig struct {
Type string `yaml:"type"`
Name string `yaml:"name"`
Community string `yaml:"community"`
Transport string `yaml:"transport"`
Hostname string `yaml:"hostname"`
Port int `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}
func Load(handle io.Reader) (*Config, error) {
config := &Config{}
data, err := ioutil.ReadAll(handle)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(data, config)
if err != nil {
return nil, err
}
return config, nil
}
func (c *DeviceConfig) String() string {
return fmt.Sprintf(
"[%s %s://%s@%s:%d]",
c.Name,
c.Transport, c.Username, c.Hostname, c.Port,
)
}
func (c *DeviceConfig) API() network_api.API {
switch c.Type {
case "mikrotik":
return network_api.NewMikrotikAPI(
c.Hostname,
c.Port,
c.Username,
c.Password,
)
case "arista":
return network_api.NewAristaAPI(
c.Transport,
c.Hostname,
c.Username,
c.Password,
c.Port,
)
case "snmp":
return network_api.NewSnmpAPI(
c.Hostname,
c.Community,
)
}
return nil
}

52
config/file_test.go

@ -0,0 +1,52 @@
package config
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestLoadConfigWithNoDevices(t *testing.T) {
configFile := `
---
devices: []
`
config, err := Load(strings.NewReader(configFile))
assert.NoError(t, err)
assert.Empty(t, config.Devices)
}
func TestLoadConfig(t *testing.T) {
configFile := `
---
devices:
- type: mikrotik
name: switch-1
community: prometheus
transport: https
hostname: switch.ip
port: 443
username: foo
password: bar
`
config, err := Load(strings.NewReader(configFile))
assert.NoError(t, err)
if assert.Len(t, config.Devices, 1) {
assert.Equal(t, "mikrotik", config.Devices[0].Type)
assert.Equal(t, "switch-1", config.Devices[0].Name)
assert.Equal(t, "prometheus", config.Devices[0].Community)
assert.Equal(t, "https", config.Devices[0].Transport)
assert.Equal(t, "switch.ip", config.Devices[0].Hostname)
assert.Equal(t, 443, config.Devices[0].Port)
assert.Equal(t, "foo", config.Devices[0].Username)
assert.Equal(t, "bar", config.Devices[0].Password)
assert.Equal(t, "[switch-1 https://foo@switch.ip:443]", config.Devices[0].String())
}
}

20
go.mod

@ -0,0 +1,20 @@
module opendev.org/vexxhost/network_exporter
go 1.13
require (
github.com/aristanetworks/goeapi v0.3.1-0.20191106033133-fe66bef98ad5
github.com/go-routeros/routeros v0.0.0-20190905230431-4e69e5fc3b22
github.com/google/martian v2.1.0+incompatible
github.com/karrick/tparse v2.4.2+incompatible
github.com/mitchellh/mapstructure v1.1.2 // indirect
github.com/prometheus/client_golang v1.3.0
github.com/prometheus/common v0.7.0
github.com/soniah/gosnmp v1.22.0
github.com/stretchr/testify v1.4.0
github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec // indirect
golang.org/x/tools v0.0.0-20200110213125-a7a6caa82ab2 // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/yaml.v2 v2.2.2
honnef.co/go/tools v0.0.1-2019.2.3
)

130
go.sum

@ -0,0 +1,130 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
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=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/aristanetworks/goeapi v0.3.0 h1:iu4VRdBE2NZ5jWz8MsJUrIaFpM5lQ3SItrc/8i3GRv8=
github.com/aristanetworks/goeapi v0.3.0/go.mod h1:l2NklnkGMAycH+WLsjBpjGikgTKtqaSKi43EbiY87i0=
github.com/aristanetworks/goeapi v0.3.1-0.20191106033133-fe66bef98ad5 h1:hnG7tKO/SA9DYX3JNQ24b0SyjmFznhNA1B8g7nzWvDo=
github.com/aristanetworks/goeapi v0.3.1-0.20191106033133-fe66bef98ad5/go.mod h1:l2NklnkGMAycH+WLsjBpjGikgTKtqaSKi43EbiY87i0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-routeros/routeros v0.0.0-20190905230431-4e69e5fc3b22 h1:C6c62mxsyTBNZVXQ5nuWfNkYCsfklS7Ji0IR5+f1KQQ=
github.com/go-routeros/routeros v0.0.0-20190905230431-4e69e5fc3b22/go.mod h1:em1mEqFKnoeQuQP9Sg7i26yaW8o05WwcNj7yLhrXxSQ=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/karrick/tparse v2.4.2+incompatible h1:+cW306qKAzrASC5XieHkgN7/vPaGKIuK62Q7nI7DIRc=
github.com/karrick/tparse v2.4.2+incompatible/go.mod h1:ASPA+vrIcN1uEW6BZg8vfWbzm69ODPSYZPU6qJyfdK0=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/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/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
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=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.1.0 h1:ElTg5tNp4DqfV7UQjDqv2+RJlNzsDtvNAWccbItceIE=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/soniah/gosnmp v1.22.0 h1:jVJi8+OGvR+JHIaZKMmnyNP0akJd2vEgNatybwhZvxg=
github.com/soniah/gosnmp v1.22.0/go.mod h1:DuEpAS0az51+DyVBQwITDsoq4++e3LTNckp2GoasF2I=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec h1:DGmKwyZwEB8dI7tbLt/I/gQuP559o/0FrAkHKlQM/Ks=
github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec/go.mod h1:owBmyHYMLkxyrugmfwE/DLJyW8Ro9mkphwuVErQ0iUw=
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=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f h1:68K/z8GLUxV76xGSqwTWw2gyk/jwn79LUL43rES2g8o=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20200110213125-a7a6caa82ab2 h1:V9r/14uGBqLgNlHRYWdVqjMdWkcOHnE2KG8DwVqQSEc=
golang.org/x/tools v0.0.0-20200110213125-a7a6caa82ab2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

76
main.go

@ -0,0 +1,76 @@
package main
import (
"net/http"
"os"
"opendev.org/vexxhost/network_exporter/collector"
"opendev.org/vexxhost/network_exporter/config"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/common/log"
"github.com/prometheus/common/version"
"gopkg.in/alecthomas/kingpin.v2"
)
func main() {
var (
listenAddress = kingpin.Flag(
"web.listen-address",
"Address on which to expose metrics and web interface.",
).Default(":9615").String()
metricsPath = kingpin.Flag(
"web.telemetry-path",
"Path under which to expose metrics.",
).Default("/metrics").String()
configFile = kingpin.Flag(
"config.file",
"Configuration file path",
).Default("config.yaml").String()
)
kingpin.Version(version.Print("network-exporter"))
kingpin.HelpFlag.Short('h')
kingpin.Parse()
file, err := os.Open(*configFile)
if err != nil {
log.Fatalln(err)
}
config, err := config.Load(file)
if err != nil {
log.Fatalln(err)
}
for _, device := range config.Devices {
api := device.API()
bgp := collector.NewBgpCollector(device.Name, api)
prometheus.MustRegister(bgp)
deviceInfo := collector.NewDeviceCollector(device.Name, api)
prometheus.MustRegister(deviceInfo)
iface := collector.NewInterfaceCollector(device.Name, api)
prometheus.MustRegister(iface)
}
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(`<html>
<head><title>Network Exporter</title></head>
<body>
<h1>Network Exporter</h1>
<p><a href="` + *metricsPath + `">Metrics</a></p>
</body>
</html>`))
if err != nil {
log.Errorln(err)
}
})
log.Infoln("Listening on", *listenAddress)
log.Fatal(http.ListenAndServe(*listenAddress, nil))
}

133
network_api/arista.go

@ -0,0 +1,133 @@
package network_api
import (
"time"
"github.com/aristanetworks/goeapi"
"github.com/aristanetworks/goeapi/module"
)
type AristaAPI struct {
API
Transport string
Host string
Username string
Password string
Port int
}
func NewAristaAPI(transport string, host string, username string, password string, port int) API {
return &AristaAPI{
Transport: transport,
Host: host,
Username: username,
Password: password,
Port: port,
}
}
func (a *AristaAPI) getNode() (*goeapi.Node, error) {
return goeapi.Connect(a.Transport, a.Host, a.Username, a.Password, a.Port)
}
func (a *AristaAPI) Info() (*Info, error) {
node, err := a.getNode()
if err != nil {
return nil, err
}
var version module.ShowVersion
handle, _ := node.GetHandle("json")
if err := handle.Enable(&version); err != nil {
return nil, err
}
return &Info{
Uptime: float64(time.Now().Unix()) - version.BootupTimestamp,
FreeMemory: float64(version.MemFree),
TotalMemory: float64(version.MemTotal),
Model: version.ModelName,
Serial: version.SerialNumber,
Version: version.Version,
}, nil
}
func (a *AristaAPI) Interfaces() ([]Interface, error) {
node, err := a.getNode()
if err != nil {
return nil, err
}
interfaces := []Interface{}
show := module.Show(node)
ifaceData := show.ShowInterfaces()
for _, iface := range ifaceData.Interfaces {
counters := iface.InterfaceCounters
rxPackets := float64(counters.InBroadcastPkts) + float64(counters.InMulticastPkts) + float64(counters.InUcastPkts)
rxBits := float64(counters.InOctets) * 8
rxDrops := float64(counters.InDiscards)
rxErrors := float64(counters.TotalInErrors)
txPackets := float64(counters.OutBroadcastPkts) + float64(counters.OutMulticastPkts) + float64(counters.OutUcastPkts)
txBits := float64(counters.OutOctets) * 8
txDrops := float64(counters.OutDiscards)
txErrors := float64(counters.TotalOutErrors)
iface := Interface{
Name: iface.Name,
Description: iface.Description,
RxPackets: rxPackets,
RxBits: rxBits,
RxDrops: rxDrops,
RxErrors: rxErrors,
TxPackets: txPackets,
TxBits: txBits,
TxDrops: txDrops,
TxErrors: txErrors,
}
interfaces = append(interfaces, iface)
}
return interfaces, nil
}
func (a *AristaAPI) Peers() ([]BgpPeer, error) {
node, err := a.getNode()
if err != nil {
return nil, err
}
peers := []BgpPeer{}
show := module.Show(node)
summary, err := show.ShowIPBGPSummary()
if err != nil {
return peers, err
}
for _, vrf := range summary.VRFs {
for peerId, peer := range vrf.Peers {
peer := BgpPeer{
Up: (peer.PeerState == "Established"),
AS: peer.ASN,
Remote: peerId,
PrefixCount: float64(peer.PrefixAccepted),
MessagesReceived: float64(peer.MsgReceived),
MessagesSent: float64(peer.MsgSent),
}
peers = append(peers, peer)
}
}
return peers, nil
}

45
network_api/interface.go

@ -0,0 +1,45 @@
package network_api
type API interface {
Info() (*Info, error)
Peers() ([]BgpPeer, error)
Interfaces() ([]Interface, error)
}
type Info struct {
Uptime float64
FreeMemory float64
TotalMemory float64
Model string
Serial string
Version string
}
type BgpPeer struct {
Up bool
AS int64
Remote string
PrefixCount float64
MessagesReceived float64
MessagesSent float64
}
type Interface struct {
Name string
Description string
RxPackets float64
RxBits float64
RxDrops float64
RxErrors float64
TxPackets float64
TxBits float64
TxDrops float64
TxErrors float64
}

163
network_api/mikrotik.go

@ -0,0 +1,163 @@
package network_api
import (
"fmt"
"strconv"
"time"
"github.com/go-routeros/routeros"
"github.com/karrick/tparse"
)
type MikrotikAPI struct {
API
Address string
Username string
Password string
}
func NewMikrotikAPI(hostname string, port int, username string, password string) API {
address := fmt.Sprintf("%s:%d", hostname, port)
return &MikrotikAPI{
Address: address,
Username: username,
Password: password,
}
}
func (a *MikrotikAPI) getClient() (*routeros.Client, error) {
return routeros.Dial(a.Address, a.Username, a.Password)
}
func (a *MikrotikAPI) Info() (*Info, error) {
client, err := a.getClient()
if err != nil {
return nil, err
}
systemData, err := client.Run(
"/system/resource/print",
"=.proplist=uptime,free-memory,total-memory,board-name,version",
)
if err != nil {
return nil, err
}
boardData, err := client.Run(
"/system/routerboard/print",
"=.proplist=serial-number",
)
if err != nil {
return nil, err
}
bootTimestamp, _ := tparse.ParseNow(time.RFC3339, "now-"+systemData.Re[0].Map["uptime"])
freeMemory, _ := strconv.ParseFloat(systemData.Re[0].Map["free-memory"], 64)
totalMemory, _ := strconv.ParseFloat(systemData.Re[0].Map["total-memory"], 64)
return &Info{
Uptime: float64(time.Now().Unix()) - float64(bootTimestamp.Unix()),
FreeMemory: freeMemory,
TotalMemory: totalMemory,
Model: systemData.Re[0].Map["board-name"],
Serial: boardData.Re[0].Map["serial-number"],
Version: systemData.Re[0].Map["version"],
}, nil
}
func (a *MikrotikAPI) Interfaces() ([]Interface, error) {
client, err := a.getClient()
if err != nil {
return nil, err
}
interfaces := []Interface{}
interfaceData, err := client.Run(
"/interface/print",
"?type=ether",
"=.proplist=name,rx-packet,rx-byte,rx-drop,rx-error,tx-packet,tx-byte,tx-drop,tx-error",
)
if err != nil {
return nil, err
}
for _, iface := range interfaceData.Re {
rxPackets, _ := strconv.ParseFloat(iface.Map["rx-packet"], 64)
rxBytes, _ := strconv.ParseFloat(iface.Map["rx-byte"], 64)
rxDrops, _ := strconv.ParseFloat(iface.Map["rx-drop"], 64)
rxErrors, _ := strconv.ParseFloat(iface.Map["rx-error"], 64)
txPackets, _ := strconv.ParseFloat(iface.Map["tx-packet"], 64)
txBytes, _ := strconv.ParseFloat(iface.Map["tx-byte"], 64)
txDrops, _ := strconv.ParseFloat(iface.Map["tx-drop"], 64)
txErrors, _ := strconv.ParseFloat(iface.Map["tx-error"], 64)
iface := Interface{
Name: iface.Map["name"],
Description: iface.Map["comment"],
RxPackets: rxPackets,
RxBits: rxBytes * 8,
RxDrops: rxDrops,
RxErrors: rxErrors,
TxPackets: txPackets,
TxBits: txBytes * 8,
TxDrops: txDrops,
TxErrors: txErrors,
}
interfaces = append(interfaces, iface)
}
return interfaces, nil
}
func (a *MikrotikAPI) Peers() ([]BgpPeer, error) {
client, err := a.getClient()
if err != nil {
return nil, err
}
peers := []BgpPeer{}
peerData, err := client.Run(
"/routing/bgp/peer/print",
"=.proplist=state,remote-as,remote-address,prefix-count,updates-sent,updates-received,withdrawn-sent,withdrawn-received",
)
if err != nil {
return nil, err
}
for _, peer := range peerData.Re {
asn, _ := strconv.ParseInt(peer.Map["remote-as"], 10, 64)
prefixCount, _ := strconv.ParseFloat(peer.Map["prefix-count"], 64)
updatesSent, _ := strconv.ParseFloat(peer.Map["updates-sent"], 64)
updatesReceived, _ := strconv.ParseFloat(peer.Map["updates-received"], 64)
withdrawnSent, _ := strconv.ParseFloat(peer.Map["withdrawn-sent"], 64)
withdrawnReceived, _ := strconv.ParseFloat(peer.Map["withdrawn-received"], 64)
messagesSent := updatesSent + withdrawnSent
messagesReceived := updatesReceived + withdrawnReceived
peer := BgpPeer{
Up: (peer.Map["state"] == "established"),
AS: asn,
Remote: peer.Map["remote-address"],
PrefixCount: prefixCount,
MessagesReceived: messagesSent,
MessagesSent: messagesReceived,
}
peers = append(peers, peer)
}
return peers, nil
}

237
network_api/snmp.go

@ -0,0 +1,237 @@
package network_api
import (
"errors"
"fmt"
"strings"
"sync"
"time"
"github.com/soniah/gosnmp"
)
type SnmpAPI struct {
API
Target string
Community string
GoSnmp *gosnmp.GoSNMP
}
func NewSnmpAPI(target string, community string) *SnmpAPI {
return &SnmpAPI{
Target: target,
Community: community,
GoSnmp: &gosnmp.GoSNMP{
Target: target,
Port: 161,
Transport: "udp",
Community: community,
Version: gosnmp.Version2c,
Timeout: time.Duration(2) * time.Second,
Retries: 3,
ExponentialTimeout: true,
MaxOids: gosnmp.MaxOids,
},
}
}
func (a *SnmpAPI) getClient() *gosnmp.GoSNMP {
return &gosnmp.GoSNMP{
Target: a.Target,
Port: 161,
Transport: "udp",
Community: a.Community,
Version: gosnmp.Version2c,
Timeout: time.Duration(2) * time.Second,
Retries: 3,
ExponentialTimeout: true,
MaxOids: gosnmp.MaxOids,
}
}
func (a *SnmpAPI) Info() (*Info, error) {
info := &Info{}
// Uptime: 1.3.6.1.2.1.1.3
walks, err := a.walk([]string{
"1.3.6.1.2.1.1",
"1.0.8802.1.1.2.1.3.4",
})
if err != nil {
return nil, err
}
for _, pdus := range walks {
for _, pdu := range pdus {
fmt.Println(pdu.Name)
// Uptime
if pdu.Name == ".1.3.6.1.2.1.1.3.0" {
info.Uptime = float64(pdu.Value.(uint))
}
// Model
if pdu.Name == ".1.0.8802.1.1.2.1.3.4.0" {
info.Model = string(pdu.Value.([]byte))
}
}
}
return info, nil
}
func (a *SnmpAPI) Peers() ([]BgpPeer, error) {
return nil, errors.New("Unimplemented")
}
type SnmpWalkResult struct {
OID string
PDUs []gosnmp.SnmpPDU
Error error
}
func (a *SnmpAPI) walk(oids []string) (map[string][]gosnmp.SnmpPDU, error) {
var err error
walks := map[string][]gosnmp.SnmpPDU{}
pdus := make(chan SnmpWalkResult, len(oids))
errors := make(chan error, len(oids))
wg := sync.WaitGroup{}
for _, oid := range oids {
wg.Add(1)
go func(oid string) {
defer wg.Done()
client := a.getClient()
err = client.Connect()
if err != nil {
errors <- err
return
}
pduData, err := client.WalkAll(oid)
errors <- err
pdus <- SnmpWalkResult{
OID: oid,
PDUs: pduData,
Error: err,
}
}(oid)
}
wg.Wait()
close(pdus)
close(errors)
for r := range pdus {
walks[r.OID] = r.PDUs
}
for e := range errors {
if e != nil {
return nil, err
}
}
return walks, err
}
func (a *SnmpAPI) Interfaces() ([]Interface, error) {
interfaces := []Interface{}
walks, err := a.walk([]string{
"1.3.6.1.2.1.31.1.1.1.1",
"1.3.6.1.2.1.31.1.1.1.18",
"1.3.6.1.2.1.2.2.1.3",
"1.3.6.1.2.1.31.1.1.1.6",
"1.3.6.1.2.1.2.2.1.11",
"1.3.6.1.2.1.2.2.1.12",
"1.3.6.1.2.1.2.2.1.13",
"1.3.6.1.2.1.2.2.1.14",
"1.3.6.1.2.1.31.1.1.1.10",
"1.3.6.1.2.1.2.2.1.17",
"1.3.6.1.2.1.2.2.1.18",
"1.3.6.1.2.1.2.2.1.19",
"1.3.6.1.2.1.2.2.1.20",
})
if err != nil {
return nil, err
}
ifaceMap := map[string]*Interface{}
// NOTE(mnaser): Create interfaces for L2 interfaces only
for _, pdu := range walks["1.3.6.1.2.1.2.2.1.3"] {
s := strings.Split(pdu.Name, ".")
index := s[len(s)-1]
if pdu.Value.(int) == 6 {
ifaceMap[index] = &Interface{}
}
}
for oid, pdus := range walks {
for _, pdu := range pdus {
s := strings.Split(pdu.Name, ".")
index := s[len(s)-1]
// Skip if we don't have a map (means non L2 device)
if _, ok := ifaceMap[index]; !ok {
continue
}
// Name
if oid == "1.3.6.1.2.1.31.1.1.1.1" {
ifaceMap[index].Name = string(pdu.Value.([]byte))
}
// Description
if oid == "1.3.6.1.2.1.31.1.1.1.18" {
ifaceMap[index].Description = string(pdu.Value.([]byte))
}
// RxPackets
if oid == "1.3.6.1.2.1.2.2.1.11" || oid == "1.3.6.1.2.1.2.2.1.12" {
ifaceMap[index].RxPackets += float64(pdu.Value.(uint))
}
// RxBits
if oid == "1.3.6.1.2.1.31.1.1.1.6" {
ifaceMap[index].RxBits = float64(pdu.Value.(uint64)) * 8
}
// RxDrops
if oid == "1.3.6.1.2.1.2.2.1.13" {
ifaceMap[index].RxDrops = float64(pdu.Value.(uint))
}
// RxErrors
if oid == "1.3.6.1.2.1.2.2.1.14" {
ifaceMap[index].RxErrors = float64(pdu.Value.(uint))
}
// TxPackets
if oid == "1.3.6.1.2.1.2.2.1.17" || oid == "1.3.6.1.2.1.2.2.1.18" {
ifaceMap[index].TxPackets += float64(pdu.Value.(uint))
}
// TxBits
if oid == "1.3.6.1.2.1.31.1.1.1.10" {
ifaceMap[index].TxBits = float64(pdu.Value.(uint64)) * 8
}
// TxDrops
if oid == "1.3.6.1.2.1.2.2.1.19" {
ifaceMap[index].TxDrops = float64(pdu.Value.(uint))
}
// TxErrors
if oid == "1.3.6.1.2.1.2.2.1.20" {
ifaceMap[index].TxErrors = float64(pdu.Value.(uint))
}
}
}
for _, iface := range ifaceMap {
interfaces = append(interfaces, *iface)
}
return interfaces, nil
}
Loading…
Cancel
Save