diff --git a/main.go b/main.go index d8739e4..405188d 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,7 @@ import ( "github.com/prometheus/common/log" ) -const version string = "0.8.3" +const version string = "0.9.0" var ( showVersion = flag.Bool("version", false, "Print version information.") diff --git a/parser.go b/parser.go index e49b7de..dc920c9 100644 --- a/parser.go +++ b/parser.go @@ -17,12 +17,14 @@ var ( protocolRegex *regexp.Regexp routeRegex *regexp.Regexp uptimeRegex *regexp.Regexp + routeChangeRegex *regexp.Regexp ) func init() { - protocolRegex, _ = regexp.Compile("^(?:1002\\-)?([^\\s]+)\\s+(BGP|OSPF)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)(?:\\s+(.*?))?$") - routeRegex, _ = regexp.Compile("^\\s+Routes:\\s+(\\d+) imported, (?:(\\d+) filtered, )?(\\d+) exported(?:, (\\d+) preferred)?") - uptimeRegex, _ = regexp.Compile("^(?:((\\d+):(\\d{2}):(\\d{2}))|\\d+)$") + protocolRegex = regexp.MustCompile("^(?:1002\\-)?([^\\s]+)\\s+(BGP|OSPF)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)(?:\\s+(.*?))?$") + routeRegex = regexp.MustCompile("^\\s+Routes:\\s+(\\d+) imported, (?:(\\d+) filtered, )?(\\d+) exported(?:, (\\d+) preferred)?") + uptimeRegex = regexp.MustCompile("^(?:((\\d+):(\\d{2}):(\\d{2}))|\\d+)$") + routeChangeRegex = regexp.MustCompile("(Import|Export) (updates|withdraws):\\s+(\\d+|---)\\s+(\\d+|---)\\s+(\\d+|---)\\s+(\\d+|---)\\s+(\\d+|---)\\s*") } func parseOutput(data []byte, ipVersion int) []*protocol.Protocol { @@ -41,6 +43,7 @@ func parseOutput(data []byte, ipVersion int) []*protocol.Protocol { if current != nil { parseLineForRoutes(line, current) + parseLineForRouteChanges(line, current) } if line == "" { @@ -59,9 +62,11 @@ func parseLineForProtocol(line string, ipVersion int) (*protocol.Protocol, bool) } proto := parseProto(match[2]) - up := parseState(match[4]) ut := parseUptime(match[5]) - p := &protocol.Protocol{Proto: proto, Name: match[1], IpVersion: ipVersion, Up: up, Uptime: ut, Attributes: make(map[string]float64)} + + p := protocol.NewProtocol(match[1], proto, ipVersion, ut) + p.Up = parseState(match[4]) + fillAttributes(p, match) return p, true @@ -81,17 +86,19 @@ func parseProto(val string) int { func parseLineForRoutes(line string, p *protocol.Protocol) { match := routeRegex.FindStringSubmatch(line) - if match != nil { - p.Imported, _ = strconv.ParseInt(match[1], 10, 64) - p.Exported, _ = strconv.ParseInt(match[3], 10, 64) + if match == nil { + return + } - if len(match[2]) > 0 { - p.Filtered, _ = strconv.ParseInt(match[2], 10, 64) - } + p.Imported, _ = strconv.ParseInt(match[1], 10, 64) + p.Exported, _ = strconv.ParseInt(match[3], 10, 64) - if len(match[4]) > 0 { - p.Preferred, _ = strconv.ParseInt(match[4], 10, 64) - } + if len(match[2]) > 0 { + p.Filtered, _ = strconv.ParseInt(match[2], 10, 64) + } + + if len(match[4]) > 0 { + p.Preferred, _ = strconv.ParseInt(match[4], 10, 64) } } @@ -105,7 +112,6 @@ func parseState(state string) int { func parseUptime(value string) int { match := uptimeRegex.FindStringSubmatch(value) - if match == nil { return 0 } @@ -141,6 +147,44 @@ func parseUptimeForTimestamp(timestamp string) int { return int(d.Seconds()) } +func parseLineForRouteChanges(line string, p *protocol.Protocol) { + match := routeChangeRegex.FindStringSubmatch(line) + if match == nil { + return + } + + c := getRouteChangeCount(match, p) + c.Received = parseRouteChangeValue(match[3]) + c.Rejected = parseRouteChangeValue(match[4]) + c.Filtered = parseRouteChangeValue(match[5]) + c.Ignored = parseRouteChangeValue(match[6]) + c.Accepted = parseRouteChangeValue(match[7]) +} + +func getRouteChangeCount(values []string, p *protocol.Protocol) *protocol.RouteChangeCount { + if values[1] == "Import" { + if values[2] == "updates" { + return &p.ImportUpdates + } + + return &p.ImportWithdraws + } else { + if values[2] == "updates" { + return &p.ExportUpdates + } + + return &p.ExportWithdraws + } +} + +func parseRouteChangeValue(value string) int64 { + if value == "---" { + return 0 + } + + return parseInt(value) +} + func parseInt(value string) int64 { i, err := strconv.ParseInt(value, 10, 64) diff --git a/parser_test.go b/parser_test.go index edc71bf..44e344a 100644 --- a/parser_test.go +++ b/parser_test.go @@ -70,6 +70,39 @@ func Test2BgpSessions(t *testing.T) { assert.IntEqual("protocols", 2, len(p), t) } +func TestUpdateAndWithdrawCounts(t *testing.T) { + data := "foo BGP master up 00:01:00 Established\ntest\n" + + " Routes: 12 imported, 1 filtered, 34 exported, 100 preferred\n" + + " Route change stats: received rejected filtered ignored accepted\n" + + " Import updates: 1 2 3 4 5\n" + + " Import withdraws: 6 7 8 9 10\n" + + " Export updates: 11 12 13 14 15\n" + + " Export withdraws: 16 17 18 19 ---" + p := parseOutput([]byte(data), 4) + x := p[0] + + assert.Int64Equal("import updates received", 1, x.ImportUpdates.Received, t) + assert.Int64Equal("import updates rejected", 2, x.ImportUpdates.Rejected, t) + assert.Int64Equal("import updates filtered", 3, x.ImportUpdates.Filtered, t) + assert.Int64Equal("import updates ignored", 4, x.ImportUpdates.Ignored, t) + assert.Int64Equal("import updates accepted", 5, x.ImportUpdates.Accepted, t) + assert.Int64Equal("import withdraws received", 6, x.ImportWithdraws.Received, t) + assert.Int64Equal("import withdraws rejected", 7, x.ImportWithdraws.Rejected, t) + assert.Int64Equal("import withdraws filtered", 8, x.ImportWithdraws.Filtered, t) + assert.Int64Equal("import withdraws ignored", 9, x.ImportWithdraws.Ignored, t) + assert.Int64Equal("import withdraws accepted", 10, x.ImportWithdraws.Accepted, t) + assert.Int64Equal("export updates received", 11, x.ExportUpdates.Received, t) + assert.Int64Equal("export updates rejected", 12, x.ExportUpdates.Rejected, t) + assert.Int64Equal("export updates filtered", 13, x.ExportUpdates.Filtered, t) + assert.Int64Equal("export updates ignored", 14, x.ExportUpdates.Ignored, t) + assert.Int64Equal("export updates accepted", 15, x.ExportUpdates.Accepted, t) + assert.Int64Equal("export withdraws received", 16, x.ExportWithdraws.Received, t) + assert.Int64Equal("export withdraws rejected", 17, x.ExportWithdraws.Rejected, t) + assert.Int64Equal("export withdraws filtered", 18, x.ExportWithdraws.Filtered, t) + assert.Int64Equal("export withdraws ignored", 19, x.ExportWithdraws.Ignored, t) + assert.Int64Equal("export withdraws accepted", 0, x.ExportWithdraws.Accepted, 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 := parseOutput([]byte(data), 4) diff --git a/protocol/generic_exporter.go b/protocol/generic_exporter.go index eaef809..4aa652b 100644 --- a/protocol/generic_exporter.go +++ b/protocol/generic_exporter.go @@ -3,12 +3,32 @@ package protocol import "github.com/prometheus/client_golang/prometheus" type GenericProtocolMetricExporter struct { - upDesc *prometheus.Desc - importCountDesc *prometheus.Desc - exportCountDesc *prometheus.Desc - filterCountDesc *prometheus.Desc - preferredCountDesc *prometheus.Desc - uptimeDesc *prometheus.Desc + upDesc *prometheus.Desc + importCountDesc *prometheus.Desc + exportCountDesc *prometheus.Desc + filterCountDesc *prometheus.Desc + preferredCountDesc *prometheus.Desc + uptimeDesc *prometheus.Desc + updatesImportReceiveCountDesc *prometheus.Desc + updatesImportRejectCountDesc *prometheus.Desc + updatesImportFilterCountDesc *prometheus.Desc + updatesImportIgnoreCountDesc *prometheus.Desc + updatesImportAcceptCountDesc *prometheus.Desc + withdrawsImportReceiveCountDesc *prometheus.Desc + withdrawsImportRejectCountDesc *prometheus.Desc + withdrawsImportFilterCountDesc *prometheus.Desc + withdrawsImportIgnoreCountDesc *prometheus.Desc + withdrawsImportAcceptCountDesc *prometheus.Desc + updatesExportReceiveCountDesc *prometheus.Desc + updatesExportRejectCountDesc *prometheus.Desc + updatesExportFilterCountDesc *prometheus.Desc + updatesExportIgnoreCountDesc *prometheus.Desc + updatesExportAcceptCountDesc *prometheus.Desc + withdrawsExportReceiveCountDesc *prometheus.Desc + withdrawsExportRejectCountDesc *prometheus.Desc + withdrawsExportFilterCountDesc *prometheus.Desc + withdrawsExportIgnoreCountDesc *prometheus.Desc + withdrawsExportAcceptCountDesc *prometheus.Desc } func NewGenericProtocolMetricExporter(prefix string) *GenericProtocolMetricExporter { @@ -26,6 +46,26 @@ func (m *GenericProtocolMetricExporter) initDesc(prefix string) { m.filterCountDesc = prometheus.NewDesc(prefix+"_prefix_count_filter", "Number of filtered routes", labels, nil) m.preferredCountDesc = prometheus.NewDesc(prefix+"_prefix_count_preferred", "Number of preferred routes", labels, nil) m.uptimeDesc = prometheus.NewDesc(prefix+"_uptime", "Uptime of the protocol in seconds", labels, nil) + m.updatesImportIgnoreCountDesc = prometheus.NewDesc(prefix+"_changes_update_import_ignore_count", "Number of incoming updates beeing ignored", labels, nil) + m.updatesImportAcceptCountDesc = prometheus.NewDesc(prefix+"_changes_update_import_accept_count", "Number of incoming updates beeing accepted", labels, nil) + m.updatesImportFilterCountDesc = prometheus.NewDesc(prefix+"_changes_update_import_filter_count", "Number of incoming updates beeing filtered", labels, nil) + m.updatesImportRejectCountDesc = prometheus.NewDesc(prefix+"_changes_update_import_reject_count", "Number of incoming updates beeing rejected", labels, nil) + m.updatesImportReceiveCountDesc = prometheus.NewDesc(prefix+"_changes_update_import_receive_count", "Number of received updates", labels, nil) + m.withdrawsImportIgnoreCountDesc = prometheus.NewDesc(prefix+"_changes_withdraw_import_ignore_count", "Number of incoming withdraws beeing ignored", labels, nil) + m.withdrawsImportAcceptCountDesc = prometheus.NewDesc(prefix+"_changes_withdraw_import_accept_count", "Number of incoming withdraws beeing accepted", labels, nil) + m.withdrawsImportFilterCountDesc = prometheus.NewDesc(prefix+"_changes_withdraw_import_filter_count", "Number of incoming withdraws beeing filtered", labels, nil) + m.withdrawsImportRejectCountDesc = prometheus.NewDesc(prefix+"_changes_withdraw_import_reject_count", "Number of incoming withdraws beeing rejected", labels, nil) + m.withdrawsImportReceiveCountDesc = prometheus.NewDesc(prefix+"_changes_withdraw_import_receive_count", "Number of received withdraws", labels, nil) + m.updatesExportIgnoreCountDesc = prometheus.NewDesc(prefix+"_changes_update_export_ignore_count", "Number of outgoing updates beeing ignored", labels, nil) + m.updatesExportAcceptCountDesc = prometheus.NewDesc(prefix+"_changes_update_export_accept_count", "Number of outgoing updates beeing accepted", labels, nil) + m.updatesExportFilterCountDesc = prometheus.NewDesc(prefix+"_changes_update_export_filter_count", "Number of outgoing updates beeing filtered", labels, nil) + m.updatesExportRejectCountDesc = prometheus.NewDesc(prefix+"_changes_update_export_reject_count", "Number of outgoing updates beeing rejected", labels, nil) + m.updatesExportReceiveCountDesc = prometheus.NewDesc(prefix+"_changes_update_export_receive_count", "Number of sent updates", labels, nil) + m.withdrawsExportIgnoreCountDesc = prometheus.NewDesc(prefix+"_changes_withdraw_export_ignore_count", "Number of outgoing withdraws beeing ignored", labels, nil) + m.withdrawsExportAcceptCountDesc = prometheus.NewDesc(prefix+"_changes_withdraw_export_accept_count", "Number of outgoing withdraws beeing accepted", labels, nil) + m.withdrawsExportFilterCountDesc = prometheus.NewDesc(prefix+"_changes_withdraw_export_filter_count", "Number of outgoing withdraws beeing filtered", labels, nil) + m.withdrawsExportRejectCountDesc = prometheus.NewDesc(prefix+"_changes_withdraw_export_reject_count", "Number of outgoing withdraws beeing rejected", labels, nil) + m.withdrawsExportReceiveCountDesc = prometheus.NewDesc(prefix+"_changes_withdraw_export_receive_count", "Number of outgoing withdraws", labels, nil) } func (m *GenericProtocolMetricExporter) Describe(ch chan<- *prometheus.Desc) { @@ -35,6 +75,26 @@ func (m *GenericProtocolMetricExporter) Describe(ch chan<- *prometheus.Desc) { ch <- m.filterCountDesc ch <- m.preferredCountDesc ch <- m.uptimeDesc + ch <- m.updatesImportReceiveCountDesc + ch <- m.updatesImportRejectCountDesc + ch <- m.updatesImportFilterCountDesc + ch <- m.updatesImportIgnoreCountDesc + ch <- m.updatesImportAcceptCountDesc + ch <- m.updatesExportReceiveCountDesc + ch <- m.updatesExportRejectCountDesc + ch <- m.updatesExportFilterCountDesc + ch <- m.updatesExportIgnoreCountDesc + ch <- m.updatesExportAcceptCountDesc + ch <- m.withdrawsImportIgnoreCountDesc + ch <- m.withdrawsImportAcceptCountDesc + ch <- m.withdrawsImportFilterCountDesc + ch <- m.withdrawsImportRejectCountDesc + ch <- m.withdrawsImportReceiveCountDesc + ch <- m.withdrawsExportIgnoreCountDesc + ch <- m.withdrawsExportAcceptCountDesc + ch <- m.withdrawsExportFilterCountDesc + ch <- m.withdrawsExportRejectCountDesc + ch <- m.withdrawsExportReceiveCountDesc } func (m *GenericProtocolMetricExporter) Export(protocol *Protocol, ch chan<- prometheus.Metric) { @@ -44,4 +104,24 @@ func (m *GenericProtocolMetricExporter) Export(protocol *Protocol, ch chan<- pro ch <- prometheus.MustNewConstMetric(m.filterCountDesc, prometheus.GaugeValue, float64(protocol.Filtered), protocol.Name) ch <- prometheus.MustNewConstMetric(m.preferredCountDesc, prometheus.GaugeValue, float64(protocol.Preferred), protocol.Name) ch <- prometheus.MustNewConstMetric(m.uptimeDesc, prometheus.GaugeValue, float64(protocol.Uptime), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.updatesImportReceiveCountDesc, prometheus.GaugeValue, float64(protocol.ImportUpdates.Received), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.updatesImportRejectCountDesc, prometheus.GaugeValue, float64(protocol.ImportUpdates.Rejected), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.updatesImportFilterCountDesc, prometheus.GaugeValue, float64(protocol.ImportUpdates.Filtered), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.updatesImportAcceptCountDesc, prometheus.GaugeValue, float64(protocol.ImportUpdates.Accepted), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.updatesImportIgnoreCountDesc, prometheus.GaugeValue, float64(protocol.ImportUpdates.Ignored), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.updatesExportReceiveCountDesc, prometheus.GaugeValue, float64(protocol.ExportUpdates.Received), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.updatesExportRejectCountDesc, prometheus.GaugeValue, float64(protocol.ExportUpdates.Rejected), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.updatesExportFilterCountDesc, prometheus.GaugeValue, float64(protocol.ExportUpdates.Filtered), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.updatesExportAcceptCountDesc, prometheus.GaugeValue, float64(protocol.ExportUpdates.Accepted), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.updatesExportIgnoreCountDesc, prometheus.GaugeValue, float64(protocol.ExportUpdates.Ignored), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.withdrawsImportReceiveCountDesc, prometheus.GaugeValue, float64(protocol.ImportWithdraws.Received), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.withdrawsImportRejectCountDesc, prometheus.GaugeValue, float64(protocol.ImportWithdraws.Rejected), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.withdrawsImportFilterCountDesc, prometheus.GaugeValue, float64(protocol.ImportWithdraws.Filtered), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.withdrawsImportAcceptCountDesc, prometheus.GaugeValue, float64(protocol.ImportWithdraws.Accepted), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.withdrawsImportIgnoreCountDesc, prometheus.GaugeValue, float64(protocol.ImportWithdraws.Ignored), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.withdrawsExportReceiveCountDesc, prometheus.GaugeValue, float64(protocol.ExportWithdraws.Received), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.withdrawsExportRejectCountDesc, prometheus.GaugeValue, float64(protocol.ExportWithdraws.Rejected), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.withdrawsExportFilterCountDesc, prometheus.GaugeValue, float64(protocol.ExportWithdraws.Filtered), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.withdrawsExportAcceptCountDesc, prometheus.GaugeValue, float64(protocol.ExportWithdraws.Accepted), protocol.Name) + ch <- prometheus.MustNewConstMetric(m.withdrawsExportIgnoreCountDesc, prometheus.GaugeValue, float64(protocol.ExportWithdraws.Ignored), protocol.Name) } diff --git a/protocol/protocol.go b/protocol/protocol.go index b950642..d637b79 100644 --- a/protocol/protocol.go +++ b/protocol/protocol.go @@ -7,14 +7,30 @@ const ( ) type Protocol struct { - Name string - IpVersion int - Proto int - Up int - Imported int64 - Exported int64 - Filtered int64 - Preferred int64 - Uptime int - Attributes map[string]float64 + Name string + IpVersion int + Proto int + Up int + Imported int64 + Exported int64 + Filtered int64 + Preferred int64 + Uptime int + Attributes map[string]float64 + ImportUpdates RouteChangeCount + ImportWithdraws RouteChangeCount + ExportUpdates RouteChangeCount + ExportWithdraws RouteChangeCount +} + +type RouteChangeCount struct { + Received int64 + Rejected int64 + Filtered int64 + Ignored int64 + Accepted int64 +} + +func NewProtocol(name string, proto, ipVersion, uptime int) *Protocol { + return &Protocol{Name: name, Proto: proto, IpVersion: ipVersion, Uptime: uptime, Attributes: make(map[string]float64)} }