Commit d5d929ed authored by Daniel Czerwonk's avatar Daniel Czerwonk

added new ospf specific metrics

parent 176ec6f1
package client
import (
"fmt"
"github.com/czerwonk/bird_exporter/parser"
"github.com/czerwonk/bird_exporter/protocol"
"github.com/czerwonk/bird_socket"
......@@ -11,11 +12,11 @@ type BirdClient struct {
}
type BirdClientOptions struct {
BirdV2 bool
BirdEnabled bool
BirdV2 bool
BirdEnabled bool
Bird6Enabled bool
BirdSocket string
Bird6Socket string
BirdSocket string
Bird6Socket string
}
func (c *BirdClient) GetProtocols() ([]*protocol.Protocol, error) {
......@@ -32,15 +33,25 @@ func (c *BirdClient) GetProtocols() ([]*protocol.Protocol, error) {
}
}
return c.getProtocolsFromBird(ipVersions)
return c.protocolsFromBird(ipVersions)
}
func (c *BirdClient) getProtocolsFromBird(ipVersions []string) ([]*protocol.Protocol, error) {
func (c *BirdClient) GetOspfAreas(protocol *protocol.Protocol) ([]*protocol.OspfArea, error) {
sock := c.socketFor(protocol.IpVersion)
b, err := birdsocket.Query(sock, fmt.Sprintf("show ospf %s", protocol.Name))
if err != nil {
return nil, err
}
return parser.ParseOspf(b), nil
}
func (c *BirdClient) protocolsFromBird(ipVersions []string) ([]*protocol.Protocol, error) {
protocols := make([]*protocol.Protocol, 0)
for _, ipVersion := range ipVersions {
sock := c.socketFor(ipVersion)
s, err := c.getProtocolsFromSocket(sock, ipVersion)
s, err := c.protocolsFromSocket(sock, ipVersion)
if err != nil {
return nil, err
}
......@@ -51,13 +62,13 @@ func (c *BirdClient) getProtocolsFromBird(ipVersions []string) ([]*protocol.Prot
return protocols, nil
}
func (c *BirdClient) getProtocolsFromSocket(socketPath string, ipVersion string) ([]*protocol.Protocol, error) {
func (c *BirdClient) protocolsFromSocket(socketPath string, ipVersion string) ([]*protocol.Protocol, error) {
b, err := birdsocket.Query(socketPath, "show protocols all")
if err != nil {
return nil, err
}
return parser.Parse(b, ipVersion), nil
return parser.ParseProtocols(b, ipVersion), nil
}
func (c *BirdClient) socketFor(ipVersion string) string {
......@@ -66,4 +77,4 @@ func (c *BirdClient) socketFor(ipVersion string) string {
}
return c.Options.BirdSocket
}
\ No newline at end of file
}
package client
import "github.com/czerwonk/bird_exporter/protocol"
type Client interface {
// GetProtocols retrieves protocol information and statistics from bird
GetProtocols() ([]*protocol.Protocol, error)
// GetOspfArea retrieves OSPF specific information from bird
GetOspfAreas(protocol *protocol.Protocol) ([]*protocol.OspfArea, error)
}
......@@ -27,9 +27,9 @@ var (
enableStatic = flag.Bool("proto.static", true, "Enables metrics for protocol Static")
enableDirect = flag.Bool("proto.direct", true, "Enables metrics for protocol Direct")
// pre bird 2.0
bird6Socket = flag.String("bird.socket6", "/var/run/bird6.ctl", "Socket to communicate with bird6 routing daemon (not compatible with -bird.v2)")
birdEnabled = flag.Bool("bird.ipv4", true, "Get protocols from bird (not compatible with -bird.v2)")
bird6Enabled = flag.Bool("bird.ipv6", true, "Get protocols from bird6 (not compatible with -bird.v2)")
bird6Socket = flag.String("bird.socket6", "/var/run/bird6.ctl", "Socket to communicate with bird6 routing daemon (not compatible with -bird.v2)")
birdEnabled = flag.Bool("bird.ipv4", true, "Get protocols from bird (not compatible with -bird.v2)")
bird6Enabled = flag.Bool("bird.ipv6", true, "Get protocols from bird6 (not compatible with -bird.v2)")
)
func init() {
......
package main
import (
"github.com/czerwonk/bird_exporter/client"
"github.com/czerwonk/bird_exporter/metrics"
"github.com/czerwonk/bird_exporter/ospf"
"github.com/czerwonk/bird_exporter/protocol"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
"github.com/czerwonk/bird_exporter/client"
)
type MetricCollector struct {
exporters map[int][]metrics.MetricExporter
client *client.BirdClient
client *client.BirdClient
enabledProtocols int
}
......@@ -30,11 +29,11 @@ func NewMetricCollector(newFormat bool, enabledProtocols int) *MetricCollector {
func getClient() *client.BirdClient {
o := &client.BirdClientOptions{
BirdSocket: *birdSocket,
Bird6Socket: *bird6Socket,
BirdSocket: *birdSocket,
Bird6Socket: *bird6Socket,
Bird6Enabled: *bird6Enabled,
BirdEnabled: *birdEnabled,
BirdV2: *birdV2,
BirdEnabled: *birdEnabled,
BirdV2: *birdV2,
}
return &client.BirdClient{Options: o}
......@@ -47,7 +46,7 @@ func exportersForLegacy(c *client.BirdClient) map[int][]metrics.MetricExporter {
protocol.BGP: []metrics.MetricExporter{metrics.NewLegacyMetricExporter("bgp4_session", "bgp6_session", l)},
protocol.Direct: []metrics.MetricExporter{metrics.NewLegacyMetricExporter("direct4", "direct6", l)},
protocol.Kernel: []metrics.MetricExporter{metrics.NewLegacyMetricExporter("kernel4", "kernel6", l)},
protocol.OSPF: []metrics.MetricExporter{metrics.NewLegacyMetricExporter("ospf", "ospfv3", l), ospf.NewExporter("", c), },
protocol.OSPF: []metrics.MetricExporter{metrics.NewLegacyMetricExporter("ospf", "ospfv3", l), metrics.NewOspfExporter("", c)},
protocol.Static: []metrics.MetricExporter{metrics.NewLegacyMetricExporter("static4", "static6", l)},
}
}
......@@ -60,7 +59,7 @@ func exportersForDefault(c *client.BirdClient) map[int][]metrics.MetricExporter
protocol.BGP: []metrics.MetricExporter{e},
protocol.Direct: []metrics.MetricExporter{e},
protocol.Kernel: []metrics.MetricExporter{e},
protocol.OSPF: []metrics.MetricExporter{e, ospf.NewExporter("bird_", c), },
protocol.OSPF: []metrics.MetricExporter{e, metrics.NewOspfExporter("bird_", c)},
protocol.Static: []metrics.MetricExporter{e},
}
}
......@@ -81,7 +80,7 @@ func (m *MetricCollector) Collect(ch chan<- prometheus.Metric) {
}
for _, p := range protocols {
if p.Proto == protocol.PROTO_UNKNOWN || (m.enabledProtocols & p.Proto != p.Proto) {
if p.Proto == protocol.PROTO_UNKNOWN || (m.enabledProtocols&p.Proto != p.Proto) {
continue
}
......
package ospf
package metrics
import (
"github.com/czerwonk/bird_exporter/metrics"
"github.com/czerwonk/bird_exporter/client"
"github.com/czerwonk/bird_exporter/protocol"
"github.com/prometheus/client_golang/prometheus"
"github.com/czerwonk/bird_exporter/client"
"github.com/prometheus/common/log"
)
type desc struct {
runningDesc *prometheus.Desc
interfaceCountDesc *prometheus.Desc
neighborCountDesc *prometheus.Desc
type ospfDesc struct {
runningDesc *prometheus.Desc
interfaceCountDesc *prometheus.Desc
neighborCountDesc *prometheus.Desc
neighborAdjacentCountDesc *prometheus.Desc
}
type ospfMetricExporter struct {
descriptions map[string]*desc
client *client.BirdClient
descriptions map[string]*ospfDesc
client client.Client
}
func NewExporter(prefix string, client *client.BirdClient) metrics.MetricExporter {
d := make(map[string]*desc)
func NewOspfExporter(prefix string, client client.Client) MetricExporter {
d := make(map[string]*ospfDesc)
d["4"] = getDesc(prefix + "ospf")
d["6"] = getDesc(prefix + "ospfv3")
return &ospfMetricExporter{descriptions: d, client: client}
}
func getDesc(prefix string) *desc {
func getDesc(prefix string) *ospfDesc {
labels := []string{"name"}
d := &desc{}
d := &ospfDesc{}
d.runningDesc = prometheus.NewDesc(prefix+"_running", "State of OSPF: 0 = Alone, 1 = Running (Neighbor-Adjacencies established)", labels, nil)
labels = append(labels, "area")
......@@ -42,10 +42,32 @@ func getDesc(prefix string) *desc {
}
func (m *ospfMetricExporter) Describe(ch chan<- *prometheus.Desc) {
ch <- m.descriptions["4"].runningDesc
ch <- m.descriptions["6"].runningDesc
m.describe("4", ch)
m.describe("6", ch)
}
func (m *ospfMetricExporter) describe(ipVersion string, ch chan<- *prometheus.Desc) {
d := m.descriptions[ipVersion]
ch <- d.runningDesc
ch <- d.interfaceCountDesc
ch <- d.neighborCountDesc
ch <- d.neighborAdjacentCountDesc
}
func (m *ospfMetricExporter) Export(p *protocol.Protocol, ch chan<- prometheus.Metric) {
ch <- prometheus.MustNewConstMetric(m.descriptions[p.IpVersion].runningDesc, prometheus.GaugeValue, p.Attributes["running"], p.Name)
d := m.descriptions[p.IpVersion]
ch <- prometheus.MustNewConstMetric(d.runningDesc, prometheus.GaugeValue, p.Attributes["running"], p.Name)
areas, err := m.client.GetOspfAreas(p)
if err != nil {
log.Errorln(err)
return
}
for _, area := range areas {
l := []string{p.Name, area.Name}
ch <- prometheus.MustNewConstMetric(d.interfaceCountDesc, prometheus.GaugeValue, float64(area.InterfaceCount), l...)
ch <- prometheus.MustNewConstMetric(d.neighborCountDesc, prometheus.GaugeValue, float64(area.NeighborCount), l...)
ch <- prometheus.MustNewConstMetric(d.neighborAdjacentCountDesc, prometheus.GaugeValue, float64(area.NeighborAdjacentCount), l...)
}
}
package parser
import (
"regexp"
"bufio"
"bytes"
"strings"
"github.com/czerwonk/bird_exporter/protocol"
)
type ospfRegex struct {
area *regexp.Regexp
counters *regexp.Regexp
}
type ospfContext struct {
line string
areas []*protocol.OspfArea
current *protocol.OspfArea
}
func init() {
ospf = &ospfRegex{
area: regexp.MustCompile("Area: [^\\s]+ \\(([^\\s]+)\\)"),
counters: regexp.MustCompile("Number of ([^:]+):\\s*(\\d+)"),
}
}
var ospf *ospfRegex
func ParseOspf(data []byte) []*protocol.OspfArea {
reader := bytes.NewReader(data)
scanner := bufio.NewScanner(reader)
c := &ospfContext{
areas: make([]*protocol.OspfArea, 0),
}
for scanner.Scan() {
c.line = strings.Trim(scanner.Text(), " ")
parseLineForOspfArea(c)
parseLineForOspfCounters(c)
}
return c.areas
}
func parseLineForOspfArea(c *ospfContext) {
m := ospf.area.FindStringSubmatch(c.line)
if m == nil {
return
}
a := &protocol.OspfArea{Name: m[1]}
c.current = a
c.areas = append(c.areas, a)
}
func parseLineForOspfCounters(c *ospfContext) {
if c.current == nil {
return
}
m := ospf.counters.FindStringSubmatch(c.line)
if m == nil {
return
}
name := m[1]
value := parseInt(m[2])
if name == "interfaces" {
c.current.InterfaceCount = value
}
if name == "neighbors" {
c.current.NeighborCount = value
}
if name == "adjacent neighbors" {
c.current.NeighborAdjacentCount = value
}
}
......@@ -38,7 +38,7 @@ func init() {
}
// Parser parses bird output and returns protocol.Protocol structs
func Parse(data []byte, ipVersion string) []*protocol.Protocol {
func ParseProtocols(data []byte, ipVersion string) []*protocol.Protocol {
reader := bytes.NewReader(data)
scanner := bufio.NewScanner(reader)
......
......@@ -10,7 +10,7 @@ import (
func TestEstablishedBgpOldTimeFormat(t *testing.T) {
data := "foo BGP master up 1481973060 Established\ntest\nbar\n Routes: 12 imported, 1 filtered, 34 exported, 100 preferred\nxxx"
p := parser.Parse([]byte(data), "4")
p := parser.ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
......@@ -26,7 +26,7 @@ func TestEstablishedBgpOldTimeFormat(t *testing.T) {
func TestEstablishedBgpCurrentTimeFormat(t *testing.T) {
data := "foo BGP master up 00:01:00 Established\ntest\nbar\n Routes: 12 imported, 1 filtered, 34 exported, 100 preferred\nxxx"
p := parser.Parse([]byte(data), "4")
p := parser.ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
......@@ -43,7 +43,7 @@ func TestEstablishedBgpCurrentTimeFormat(t *testing.T) {
func TestIpv6Bgp(t *testing.T) {
data := "foo BGP master up 00:01:00 Established\ntest\nbar\n Routes: 12 imported, 1 filtered, 34 exported, 100 preferred\nxxx"
p := parser.Parse([]byte(data), "6")
p := parser.ParseProtocols([]byte(data), "6")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
......@@ -52,7 +52,7 @@ func TestIpv6Bgp(t *testing.T) {
func TestActiveBgp(t *testing.T) {
data := "bar BGP master start 2016-01-01 Active\ntest\nbar"
p := parser.Parse([]byte(data), "4")
p := parser.ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
......@@ -67,7 +67,7 @@ func TestActiveBgp(t *testing.T) {
func Test2BgpSessions(t *testing.T) {
data := "foo BGP master up 00:01:00 Established\ntest\n Routes: 12 imported, 1 filtered, 34 exported, 100 preferred\nbar BGP master start 2016-01-01 Active\nxxx"
p := parser.Parse([]byte(data), "4")
p := parser.ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 2, len(p), t)
}
......@@ -79,7 +79,7 @@ func TestUpdateAndWithdrawCounts(t *testing.T) {
" Import withdraws: 6 7 8 9 10\n" +
" Export updates: 11 12 13 14 15\n" +
" Export withdraws: 16 17 18 19 ---"
p := parser.Parse([]byte(data), "4")
p := parser.ParseProtocols([]byte(data), "4")
x := p[0]
assert.Int64Equal("import updates received", 1, x.ImportUpdates.Received, t)
......@@ -141,7 +141,7 @@ func TestWithBird2(t *testing.T) {
" Routes: 4 imported, 3 filtered, 2 exported, 1 preferred\n" +
"\n"
p := parser.Parse([]byte(data), "")
p := parser.ParseProtocols([]byte(data), "")
assert.IntEqual("protocols", 4, len(p), t)
x := p[0]
......@@ -223,7 +223,7 @@ func TestWithBird2(t *testing.T) {
func TestOspfOldTimeFormat(t *testing.T) {
data := "ospf1 OSPF master up 1481973060 Running\ntest\nbar\n Routes: 12 imported, 34 exported, 100 preferred\nxxx"
p := parser.Parse([]byte(data), "4")
p := parser.ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
......@@ -238,7 +238,7 @@ func TestOspfOldTimeFormat(t *testing.T) {
func TestOspfCurrentTimeFormat(t *testing.T) {
data := "ospf1 OSPF master up 00:01:00 Running\ntest\nbar\n Routes: 12 imported, 34 exported, 100 preferred\nxxx"
p := parser.Parse([]byte(data), "4")
p := parser.ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
......@@ -254,7 +254,7 @@ func TestOspfCurrentTimeFormat(t *testing.T) {
func TestOspfProtocolDown(t *testing.T) {
data := "o_hrz OSPF t_hrz down 1494926415 \n Preference: 150\n Input filter: ACCEPT\n Output filter: REJECT\nxxx"
p := parser.Parse([]byte(data), "6")
p := parser.ParseProtocols([]byte(data), "6")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
......@@ -268,7 +268,7 @@ func TestOspfProtocolDown(t *testing.T) {
func TestOspfRunning(t *testing.T) {
data := "ospf1 OSPF master up 00:01:00 Running\ntest\nbar\n Routes: 12 imported, 34 exported, 100 preferred\nxxx"
p := parser.Parse([]byte(data), "4")
p := parser.ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
......@@ -277,9 +277,46 @@ func TestOspfRunning(t *testing.T) {
func TestOspfAlone(t *testing.T) {
data := "ospf1 OSPF master up 00:01:00 Alone\ntest\nbar\n Routes: 12 imported, 34 exported, 100 preferred\nxxx"
p := parser.Parse([]byte(data), "4")
p := parser.ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
assert.Float64Equal("running", 0, x.Attributes["running"], t)
}
func TestOspfArea(t *testing.T) {
data := "ospf1:\n" +
"RFC1583 compatibility: disabled\n" +
"Stub router: No\n" +
"RT scheduler tick: 1\n" +
"Number of areas: 2\n" +
"Number of LSAs in DB: 33\n" +
" Area: 0.0.0.0 (0) [BACKBONE]\n" +
" Stub: No\n" +
" NSSA: No\n" +
" Transit: No\n" +
" Number of interfaces: 3\n" +
" Number of neighbors: 2\n" +
" Number of adjacent neighbors: 1\n" +
" Area: 0.0.0.1 (1)\n" +
" Stub: No\n" +
" NSSA: No\n" +
" Transit: No\n" +
" Number of interfaces: 4\n" +
" Number of neighbors: 6\n" +
" Number of adjacent neighbors: 5\n"
a := parser.ParseOspf([]byte(data))
assert.IntEqual("areas", 2, len(a), t)
a1 := a[0]
assert.StringEqual("Area1 Name", "0", a1.Name, t)
assert.Int64Equal("Area1 InterfaceCount", 3, a1.InterfaceCount, t)
assert.Int64Equal("Area1 NeighborCount", 2, a1.NeighborCount, t)
assert.Int64Equal("Area1 NeighborAdjacentCount", 1, a1.NeighborAdjacentCount, t)
a2 := a[1]
assert.StringEqual("Area2 Name", "1", a2.Name, t)
assert.Int64Equal("Area2 InterfaceCount", 4, a2.InterfaceCount, t)
assert.Int64Equal("Area2 NeighborCount", 6, a2.NeighborCount, t)
assert.Int64Equal("Area2 NeighborAdjacentCount", 5, a2.NeighborAdjacentCount, t)
}
package protocol
type OspfArea struct {
Name string
InterfaceCount int64
NeighborCount int64
NeighborAdjacentCount int64
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment