set new metric format as default, add RPKI protocol

This commit is contained in:
Daniel Czerwonk 2022-01-14 11:27:11 +01:00
parent ae7bb26676
commit cde3432490
10 changed files with 84 additions and 84 deletions

View File

@ -26,6 +26,8 @@ The new format handles protocols more generic and allows a better query structur
Also it adheres more to the metric naming best practices.
In both formats protocol specific metrics are prefixed with the protocol name (e.g. OSPF running metric).
Since verson 1.3 the new metric format is the default.
This is a short example of the different formats:
### old format

20
main.go
View File

@ -12,7 +12,7 @@ import (
log "github.com/sirupsen/logrus"
)
const version string = "1.2.7"
const version string = "1.3.0"
var (
showVersion = flag.Bool("version", false, "Print version information.")
@ -20,13 +20,14 @@ var (
metricsPath = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
birdSocket = flag.String("bird.socket", "/var/run/bird.ctl", "Socket to communicate with bird routing daemon")
birdV2 = flag.Bool("bird.v2", false, "Bird major version >= 2.0 (multi channel protocols)")
newFormat = flag.Bool("format.new", false, "New metric format (more convenient / generic)")
enableBgp = flag.Bool("proto.bgp", true, "Enables metrics for protocol BGP")
enableOspf = flag.Bool("proto.ospf", true, "Enables metrics for protocol OSPF")
newFormat = flag.Bool("format.new", true, "New metric format (more convenient / generic)")
enableBGP = flag.Bool("proto.bgp", true, "Enables metrics for protocol BGP")
enableOSPF = flag.Bool("proto.ospf", true, "Enables metrics for protocol OSPF")
enableKernel = flag.Bool("proto.kernel", true, "Enables metrics for protocol Kernel")
enableStatic = flag.Bool("proto.static", true, "Enables metrics for protocol Static")
enableDirect = flag.Bool("proto.direct", true, "Enables metrics for protocol Direct")
enableBabel = flag.Bool("proto.babel", true, "Enables metrics for protocol Babel")
enableRPKI = flag.Bool("proto.rpki", true, "Enables metrics for protocol RPKI")
// 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)")
@ -97,13 +98,13 @@ func handleMetricsRequest(w http.ResponseWriter, r *http.Request) {
ErrorHandling: promhttp.ContinueOnError}).ServeHTTP(w, r)
}
func enabledProtocols() int {
res := 0
func enabledProtocols() protocol.Proto {
res := protocol.Proto(0)
if *enableBgp {
if *enableBGP {
res |= protocol.BGP
}
if *enableOspf {
if *enableOSPF {
res |= protocol.OSPF
}
if *enableKernel {
@ -118,6 +119,9 @@ func enabledProtocols() int {
if *enableBabel {
res |= protocol.Babel
}
if *enableRPKI {
res |= protocol.RPKI
}
return res
}

View File

@ -9,15 +9,15 @@ import (
)
type MetricCollector struct {
exporters map[int][]metrics.MetricExporter
exporters map[protocol.Proto][]metrics.MetricExporter
client *client.BirdClient
enabledProtocols int
enabledProtocols protocol.Proto
newFormat bool
}
func NewMetricCollector(newFormat bool, enabledProtocols int, descriptionLabels bool) *MetricCollector {
func NewMetricCollector(newFormat bool, enabledProtocols protocol.Proto, descriptionLabels bool) *MetricCollector {
c := getClient()
var e map[int][]metrics.MetricExporter
var e map[protocol.Proto][]metrics.MetricExporter
if newFormat {
e = exportersForDefault(c, descriptionLabels)
@ -45,30 +45,32 @@ func getClient() *client.BirdClient {
return &client.BirdClient{Options: o}
}
func exportersForLegacy(c *client.BirdClient) map[int][]metrics.MetricExporter {
func exportersForLegacy(c *client.BirdClient) map[protocol.Proto][]metrics.MetricExporter {
l := metrics.NewLegacyLabelStrategy()
return map[int][]metrics.MetricExporter{
return map[protocol.Proto][]metrics.MetricExporter{
protocol.BGP: {metrics.NewLegacyMetricExporter("bgp4_session", "bgp6_session", l), metrics.NewBGPStateExporter("", c)},
protocol.Direct: {metrics.NewLegacyMetricExporter("direct4", "direct6", l)},
protocol.Kernel: {metrics.NewLegacyMetricExporter("kernel4", "kernel6", l)},
protocol.OSPF: {metrics.NewLegacyMetricExporter("ospf", "ospfv3", l), metrics.NewOSPFExporter("", c)},
protocol.Static: {metrics.NewLegacyMetricExporter("static4", "static6", l)},
protocol.Babel: {metrics.NewLegacyMetricExporter("babel4", "babel6", l)},
protocol.RPKI: {metrics.NewLegacyMetricExporter("rpki4", "rpki6", l)},
}
}
func exportersForDefault(c *client.BirdClient, descriptionLabels bool) map[int][]metrics.MetricExporter {
func exportersForDefault(c *client.BirdClient, descriptionLabels bool) map[protocol.Proto][]metrics.MetricExporter {
l := metrics.NewDefaultLabelStrategy(descriptionLabels)
e := metrics.NewGenericProtocolMetricExporter("bird_protocol", true, l)
return map[int][]metrics.MetricExporter{
return map[protocol.Proto][]metrics.MetricExporter{
protocol.BGP: {e, metrics.NewBGPStateExporter("bird_", c)},
protocol.Direct: {e},
protocol.Kernel: {e},
protocol.OSPF: {e, metrics.NewOSPFExporter("bird_", c)},
protocol.Static: {e},
protocol.Babel: {e},
protocol.RPKI: {e},
}
}

View File

@ -21,8 +21,8 @@ func (m *bgpStateMetricExporter) Describe(ch chan<- *prometheus.Desc) {
}
func (m *bgpStateMetricExporter) Export(p *protocol.Protocol, ch chan<- prometheus.Metric, newFormat bool) {
labels := []string{"name", "proto", "state"}
bgpstateDesc := prometheus.NewDesc(m.prefix+"bgp_state_count", "Number of BGP connections at each state", labels, nil)
state, err := m.client.GetBGPStates(p)
if err != nil {

View File

@ -80,6 +80,8 @@ func protoString(p *protocol.Protocol) string {
return "Direct"
case protocol.Babel:
return "Babel"
case protocol.RPKI:
return "RPKI"
}
return ""

View File

@ -32,7 +32,7 @@ func (m *GenericProtocolMetricExporter) Export(p *protocol.Protocol, ch chan<- p
var filterCountDesc *prometheus.Desc
var preferredCountDesc *prometheus.Desc
upDesc := prometheus.NewDesc(m.prefix+"_up", "Protocol is up", labels, nil)
upDesc := prometheus.NewDesc(m.prefix+"_up", "Protocol is up", append(labels, "state"), nil)
if newNaming {
importCountDesc = prometheus.NewDesc(m.prefix+"_prefix_import_count", "Number of imported routes", labels, nil)
@ -69,7 +69,7 @@ func (m *GenericProtocolMetricExporter) Export(p *protocol.Protocol, ch chan<- p
withdrawsExportReceiveCountDesc := prometheus.NewDesc(m.prefix+"_changes_withdraw_export_receive_count", "Number of outgoing withdraws", labels, nil)
l := m.labelStrategy.LabelValues(p)
ch <- prometheus.MustNewConstMetric(upDesc, prometheus.GaugeValue, float64(p.Up), l...)
ch <- prometheus.MustNewConstMetric(upDesc, prometheus.GaugeValue, float64(p.Up), append(l, p.State)...)
ch <- prometheus.MustNewConstMetric(importCountDesc, prometheus.GaugeValue, float64(p.Imported), l...)
ch <- prometheus.MustNewConstMetric(exportCountDesc, prometheus.GaugeValue, float64(p.Exported), l...)
ch <- prometheus.MustNewConstMetric(filterCountDesc, prometheus.GaugeValue, float64(p.Filtered), l...)

View File

@ -57,7 +57,13 @@ func (m *ospfMetricExporter) describe(ipVersion string, ch chan<- *prometheus.De
func (m *ospfMetricExporter) Export(p *protocol.Protocol, ch chan<- prometheus.Metric, newFormat bool) {
d := m.descriptions[p.IPVersion]
ch <- prometheus.MustNewConstMetric(d.runningDesc, prometheus.GaugeValue, p.Attributes["running"], p.Name)
var running float64
if p.State == "Running" {
running = 1
}
ch <- prometheus.MustNewConstMetric(d.runningDesc, prometheus.GaugeValue, running, p.Name)
areas, err := m.client.GetOSPFAreas(p)
if err != nil {

View File

@ -93,8 +93,7 @@ func parseLineForProtocol(c *context) {
c.current = protocol.NewProtocol(match[1], proto, c.ipVersion, ut)
c.current.Up = parseState(match[4])
fillAttributes(c.current, match)
c.current.State = match[6]
c.protocols = append(c.protocols, c.current)
c.handled = true
@ -114,7 +113,7 @@ func parseLineForDescription(c *context) {
c.current.Description = strings.Join(match[1:], " ")
}
func parseProto(val string) int {
func parseProto(val string) protocol.Proto {
switch val {
case "BGP":
return protocol.BGP
@ -128,6 +127,8 @@ func parseProto(val string) int {
return protocol.Static
case "Babel":
return protocol.Babel
case "RPKI":
return protocol.RPKI
}
return protocol.PROTO_UNKNOWN
@ -315,17 +316,3 @@ func parseInt(value string) int64 {
return i
}
func fillAttributes(p *protocol.Protocol, m []string) {
if p.Proto == protocol.OSPF {
p.Attributes["running"] = float64(parseOspfRunning(m[6]))
}
}
func parseOspfRunning(state string) int {
if state == "Running" {
return 1
}
return 0
}

View File

@ -17,7 +17,7 @@ func TestEstablishedBgpOldTimeFormat(t *testing.T) {
x := p[0]
assert.StringEqual("name", "foo", x.Name, t)
assert.IntEqual("proto", protocol.BGP, x.Proto, t)
assert.IntEqual("proto", int(protocol.BGP), int(x.Proto), t)
assert.IntEqual("established", 1, x.Up, t)
assert.Int64Equal("imported", 12, x.Imported, t)
assert.Int64Equal("exported", 34, x.Exported, t)
@ -34,7 +34,7 @@ func TestEstablishedBgpCurrentTimeFormat(t *testing.T) {
x := p[0]
assert.StringEqual("name", "foo", x.Name, t)
assert.IntEqual("proto", protocol.BGP, x.Proto, t)
assert.IntEqual("proto", int(protocol.BGP), int(x.Proto), t)
assert.IntEqual("established", 1, x.Up, t)
assert.Int64Equal("imported", 12, x.Imported, t)
assert.Int64Equal("exported", 34, x.Exported, t)
@ -55,7 +55,7 @@ func TestEstablishedBgpIsoLongTimeFormat(t *testing.T) {
x := p[0]
assert.StringEqual("name", "foo", x.Name, t)
assert.IntEqual("proto", protocol.BGP, x.Proto, t)
assert.IntEqual("proto", int(protocol.BGP), int(x.Proto), t)
assert.IntEqual("established", 1, x.Up, t)
assert.Int64Equal("imported", 12, x.Imported, t)
assert.Int64Equal("exported", 34, x.Exported, t)
@ -65,7 +65,7 @@ func TestEstablishedBgpIsoLongTimeFormat(t *testing.T) {
assert.That("uptime", "uptime is feasable", func() bool { return x.Uptime >= min && max <= x.Uptime }, t)
}
func TestIpv6Bgp(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 := ParseProtocols([]byte(data), "6")
assert.IntEqual("protocols", 1, len(p), t)
@ -74,14 +74,14 @@ func TestIpv6Bgp(t *testing.T) {
assert.StringEqual("ipVersion", "6", x.IPVersion, t)
}
func TestActiveBgp(t *testing.T) {
func TestActiveBGP(t *testing.T) {
data := "bar BGP master start 2016-01-01 Active\ntest\nbar"
p := ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
assert.StringEqual("name", "bar", x.Name, t)
assert.IntEqual("proto", protocol.BGP, x.Proto, t)
assert.IntEqual("proto", int(protocol.BGP), int(x.Proto), t)
assert.IntEqual("established", 0, x.Up, t)
assert.IntEqual("imported", 0, int(x.Imported), t)
assert.IntEqual("exported", 0, int(x.Exported), t)
@ -89,7 +89,7 @@ func TestActiveBgp(t *testing.T) {
assert.IntEqual("uptime", 0, int(x.Uptime), t)
}
func Test2BgpSessions(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 := ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 2, len(p), t)
@ -172,7 +172,7 @@ func TestWithBird2(t *testing.T) {
x := p[0]
assert.StringEqual("BGP ipv6 name", "bgp1", x.Name, t)
assert.IntEqual("BGP ipv6 proto", protocol.BGP, x.Proto, t)
assert.IntEqual("BGP ipv6 proto", int(protocol.BGP), int(x.Proto), t)
assert.StringEqual("BGP ipv6 ip version", "6", x.IPVersion, t)
assert.Int64Equal("BGP ipv6 imported", 1, x.Imported, t)
assert.Int64Equal("BGP ipv6 exported", 3, x.Exported, t)
@ -183,7 +183,7 @@ func TestWithBird2(t *testing.T) {
x = p[1]
assert.StringEqual("Direct ipv4 name", "direct1", x.Name, t)
assert.IntEqual("Direct ipv4 proto", protocol.Direct, x.Proto, t)
assert.IntEqual("Direct ipv4 proto", int(protocol.Direct), int(x.Proto), t)
assert.StringEqual("Direct ipv4 ip version", "4", x.IPVersion, t)
assert.Int64Equal("Direct ipv4 imported", 12, x.Imported, t)
assert.Int64Equal("Direct ipv4 exported", 34, x.Exported, t)
@ -212,7 +212,7 @@ func TestWithBird2(t *testing.T) {
x = p[2]
assert.StringEqual("Direct ipv6 name", "direct1", x.Name, t)
assert.IntEqual("Direct ipv6 proto", protocol.Direct, x.Proto, t)
assert.IntEqual("Direct ipv6 proto", int(protocol.Direct), int(x.Proto), t)
assert.StringEqual("Direct ipv6 ip version", "6", x.IPVersion, t)
assert.Int64Equal("Direct ipv6 imported", 3, x.Imported, t)
assert.Int64Equal("Direct ipv6 exported", 5, x.Exported, t)
@ -241,7 +241,7 @@ func TestWithBird2(t *testing.T) {
x = p[3]
assert.StringEqual("OSPF ipv4 name", "ospf1", x.Name, t)
assert.IntEqual("OSPF ipv4 proto", protocol.OSPF, x.Proto, t)
assert.IntEqual("OSPF ipv4 proto", int(protocol.OSPF), int(x.Proto), t)
assert.StringEqual("OSPF ipv4 ip version", "4", x.IPVersion, t)
assert.Int64Equal("OSPF ipv4 imported", 4, x.Imported, t)
assert.Int64Equal("OSPF ipv4 exported", 2, x.Exported, t)
@ -249,14 +249,14 @@ func TestWithBird2(t *testing.T) {
assert.Int64Equal("OSPF ipv4 preferred", 1, x.Preferred, t)
}
func TestOspfOldTimeFormat(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 := ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
assert.StringEqual("name", "ospf1", x.Name, t)
assert.IntEqual("proto", protocol.OSPF, x.Proto, t)
assert.IntEqual("proto", int(protocol.OSPF), int(x.Proto), t)
assert.IntEqual("up", 1, x.Up, t)
assert.Int64Equal("imported", 12, x.Imported, t)
assert.Int64Equal("exported", 34, x.Exported, t)
@ -264,14 +264,14 @@ func TestOspfOldTimeFormat(t *testing.T) {
assert.StringEqual("ipVersion", "4", x.IPVersion, t)
}
func TestOspfCurrentTimeFormat(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 := ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
assert.StringEqual("name", "ospf1", x.Name, t)
assert.IntEqual("proto", protocol.OSPF, x.Proto, t)
assert.IntEqual("proto", int(protocol.OSPF), int(x.Proto), t)
assert.IntEqual("up", 1, x.Up, t)
assert.Int64Equal("imported", 12, x.Imported, t)
assert.Int64Equal("exported", 34, x.Exported, t)
@ -280,39 +280,21 @@ func TestOspfCurrentTimeFormat(t *testing.T) {
assert.IntEqual("uptime", 60, x.Uptime, t)
}
func TestOspfProtocolDown(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 := ParseProtocols([]byte(data), "6")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
assert.StringEqual("name", "o_hrz", x.Name, t)
assert.IntEqual("proto", protocol.OSPF, x.Proto, t)
assert.IntEqual("proto", int(protocol.OSPF), int(x.Proto), t)
assert.IntEqual("up", 0, x.Up, t)
assert.Int64Equal("imported", 0, x.Imported, t)
assert.Int64Equal("exported", 0, x.Exported, t)
assert.StringEqual("ipVersion", "6", x.IPVersion, 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 := ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
assert.Float64Equal("running", 1, x.Attributes["running"], 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 := 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) {
func TestOSPFArea(t *testing.T) {
data := "ospf1:\n" +
"RFC1583 compatibility: disabled\n" +
"Stub router: No\n" +
@ -348,3 +330,15 @@ func TestOspfArea(t *testing.T) {
assert.Int64Equal("Area2 NeighborCount", 6, a2.NeighborCount, t)
assert.Int64Equal("Area2 NeighborAdjacentCount", 5, a2.NeighborAdjacentCount, t)
}
func TestRPKIUp(t *testing.T) {
data := "rpki1 RPKI --- up 2021-12-31 13:04:29 Established"
p := ParseProtocols([]byte(data), "4")
assert.IntEqual("protocols", 1, len(p), t)
x := p[0]
assert.StringEqual("name", "rpki1", x.Name, t)
assert.IntEqual("proto", int(protocol.RPKI), int(x.Proto), t)
assert.StringEqual("state", "Established", x.State, t)
assert.IntEqual("up", 1, x.Up, t)
}

View File

@ -1,29 +1,32 @@
package protocol
const (
PROTO_UNKNOWN = 0
BGP = 1
OSPF = 2
Kernel = 4
Static = 8
Direct = 16
Babel = 32
PROTO_UNKNOWN = Proto(0)
BGP = Proto(1)
OSPF = Proto(2)
Kernel = Proto(4)
Static = Proto(8)
Direct = Proto(16)
Babel = Proto(32)
RPKI = Proto(64)
)
type Proto int
type Protocol struct {
Name string
Description string
IPVersion string
ImportFilter string
ExportFilter string
Proto int
Proto Proto
Up int
State string
Imported int64
Exported int64
Filtered int64
Preferred int64
Uptime int
Attributes map[string]float64
ImportUpdates RouteChangeCount
ImportWithdraws RouteChangeCount
ExportUpdates RouteChangeCount
@ -38,6 +41,6 @@ type RouteChangeCount struct {
Accepted int64
}
func NewProtocol(name string, proto int, ipVersion string, uptime int) *Protocol {
return &Protocol{Name: name, Proto: proto, IPVersion: ipVersion, Uptime: uptime, Attributes: make(map[string]float64)}
func NewProtocol(name string, proto Proto, ipVersion string, uptime int) *Protocol {
return &Protocol{Name: name, Proto: proto, IPVersion: ipVersion, Uptime: uptime}
}