// SPDX-License-Identifier: GPL-2.0-only // Copyright (C) 2020 Michael Crute . All rights reserved. // // Use of this source code is governed by a license that can be found in the // LICENSE file. package main import ( "flag" "fmt" "log" "net/http" _ "net/http/pprof" "os" "strconv" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/version" "code.crute.me/pomonaconsulting/six_monitoring/six" ) const ( namespace = "sixstatus" exporter = "six_status_exporter" ) var ( up = prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "up"), "Was the SIX query successful?", nil, nil, ) rsPrefixCount = prometheus.NewDesc( prometheus.BuildFQName(namespace, "route_server", "prefix_count"), "Number of prefixes advertised to a route server.", []string{"ipversion", "mtu", "server"}, nil, ) rsErrorCount = prometheus.NewDesc( prometheus.BuildFQName(namespace, "route_server", "error_count"), "Number of errors reported by a route server.", []string{"ipversion", "mtu", "server"}, nil, ) rsTransitErrorCount = prometheus.NewDesc( prometheus.BuildFQName(namespace, "route_server", "transit_error_count"), "Number of transit errors reported by a route server.", []string{"ipversion", "mtu", "server"}, nil, ) irrPrefixCount = prometheus.NewDesc( prometheus.BuildFQName(namespace, "irr", "prefix_count"), "Number of prefixes resolved from IRR records.", []string{"ipversion"}, nil, ) irrASNCount = prometheus.NewDesc( prometheus.BuildFQName(namespace, "irr", "asn_count"), "Number of ASNs resolved from IRR records.", []string{"ipversion"}, nil, ) irrAsSetPrefixCount = prometheus.NewDesc( prometheus.BuildFQName(namespace, "irr", "as_set_prefix_count"), "Number of prefixes in the AS-SET resolved from IRR records.", []string{"ipversion"}, nil, ) pdbPrefixCount = prometheus.NewDesc( prometheus.BuildFQName(namespace, "peeringdb", "prefix_count"), "Number of prefixes configured in PeeringDB.", []string{"ipversion"}, nil, ) roaCount = prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "roa_count"), "Number of resolved ROA records.", nil, nil, ) ) type SIXCollector struct { ASN int } var _ prometheus.Collector = (*SIXCollector)(nil) func (c *SIXCollector) Describe(ch chan<- *prometheus.Desc) { ch <- up ch <- rsPrefixCount ch <- rsErrorCount ch <- rsTransitErrorCount ch <- irrPrefixCount ch <- irrASNCount ch <- irrAsSetPrefixCount ch <- pdbPrefixCount ch <- roaCount } func routeServerMetrics(rs *six.RouteServer, ch chan<- prometheus.Metric) { ch <- prometheus.MustNewConstMetric( rsPrefixCount, prometheus.GaugeValue, float64(rs.IPv4.Prefixes), "4", "1500", string(strconv.Itoa(rs.Number)), ) ch <- prometheus.MustNewConstMetric( rsErrorCount, prometheus.GaugeValue, float64(rs.IPv4.Errors), "4", "1500", string(strconv.Itoa(rs.Number)), ) ch <- prometheus.MustNewConstMetric( rsTransitErrorCount, prometheus.GaugeValue, float64(rs.IPv4.TransitErrors), "4", "1500", string(strconv.Itoa(rs.Number)), ) ch <- prometheus.MustNewConstMetric( rsPrefixCount, prometheus.GaugeValue, float64(rs.IPv6.Prefixes), "6", "1500", string(strconv.Itoa(rs.Number)), ) ch <- prometheus.MustNewConstMetric( rsErrorCount, prometheus.GaugeValue, float64(rs.IPv6.Errors), "6", "1500", string(strconv.Itoa(rs.Number)), ) ch <- prometheus.MustNewConstMetric( rsTransitErrorCount, prometheus.GaugeValue, float64(rs.IPv6.TransitErrors), "6", "1500", string(strconv.Itoa(rs.Number)), ) ch <- prometheus.MustNewConstMetric( rsPrefixCount, prometheus.GaugeValue, float64(rs.IPv4Jumbo.Prefixes), "4", "9000", string(strconv.Itoa(rs.Number)), ) ch <- prometheus.MustNewConstMetric( rsErrorCount, prometheus.GaugeValue, float64(rs.IPv4Jumbo.Errors), "4", "9000", string(strconv.Itoa(rs.Number)), ) ch <- prometheus.MustNewConstMetric( rsTransitErrorCount, prometheus.GaugeValue, float64(rs.IPv4Jumbo.TransitErrors), "4", "9000", string(strconv.Itoa(rs.Number)), ) ch <- prometheus.MustNewConstMetric( rsPrefixCount, prometheus.GaugeValue, float64(rs.IPv6Jumbo.Prefixes), "6", "9000", string(strconv.Itoa(rs.Number)), ) ch <- prometheus.MustNewConstMetric( rsErrorCount, prometheus.GaugeValue, float64(rs.IPv6Jumbo.Errors), "6", "9000", string(strconv.Itoa(rs.Number)), ) ch <- prometheus.MustNewConstMetric( rsTransitErrorCount, prometheus.GaugeValue, float64(rs.IPv6Jumbo.TransitErrors), "6", "9000", string(strconv.Itoa(rs.Number)), ) } func (c *SIXCollector) Collect(ch chan<- prometheus.Metric) { sp, err := six.FetchParseSIXCSV() if err != nil { log.Printf("error: Fetching and parsing CSV: %s", err) ch <- prometheus.MustNewConstMetric(up, prometheus.GaugeValue, 0) return } ps, ok := sp.GetParticipantByASN(c.ASN) if !ok { log.Printf("error: No participant for ASN: %d", c.ASN) ch <- prometheus.MustNewConstMetric(up, prometheus.GaugeValue, 0) return } p := ps[0] ch <- prometheus.MustNewConstMetric(up, prometheus.GaugeValue, 1) // ipversion, mtu, server if p.RouteServer2 != nil { routeServerMetrics(p.RouteServer2, ch) } if p.RouteServer3 != nil { routeServerMetrics(p.RouteServer3, ch) } ch <- prometheus.MustNewConstMetric( irrPrefixCount, prometheus.GaugeValue, float64(p.IRRv4.PrefixCount), "4", ) ch <- prometheus.MustNewConstMetric( irrASNCount, prometheus.GaugeValue, float64(p.IRRv4.ASNCount), "4", ) ch <- prometheus.MustNewConstMetric( irrAsSetPrefixCount, prometheus.GaugeValue, float64(p.IRRv4.ASSetCount), "4", ) ch <- prometheus.MustNewConstMetric( pdbPrefixCount, prometheus.GaugeValue, float64(p.PeeringDBPrefixCountv4), "4", ) ch <- prometheus.MustNewConstMetric( irrPrefixCount, prometheus.GaugeValue, float64(p.IRRv6.PrefixCount), "6", ) ch <- prometheus.MustNewConstMetric( irrASNCount, prometheus.GaugeValue, float64(p.IRRv6.ASNCount), "6", ) ch <- prometheus.MustNewConstMetric( irrAsSetPrefixCount, prometheus.GaugeValue, float64(p.IRRv6.ASSetCount), "6", ) ch <- prometheus.MustNewConstMetric( pdbPrefixCount, prometheus.GaugeValue, float64(p.PeeringDBPrefixCountv6), "6", ) ch <- prometheus.MustNewConstMetric(roaCount, prometheus.GaugeValue, float64(p.ROACount)) } func main() { var ( showVersion = flag.Bool("version", false, "Print version information.") listenAddress = flag.String("web.listen-address", ":9119", "Address to listen on for web interface and telemetry.") ) flag.Parse() if len(os.Args) != 2 { fmt.Fprintf(os.Stdout, "usage: %s \n", os.Args[0]) os.Exit(1) } asn, err := strconv.Atoi(os.Args[1]) if err != nil { fmt.Fprintln(os.Stdout, "invalid ASN, must be numeric") fmt.Fprintf(os.Stdout, "usage: %s \n", os.Args[0]) os.Exit(1) } if *showVersion { fmt.Fprintln(os.Stdout, version.Print(exporter)) os.Exit(0) } log.Println("Starting", exporter, version.Info()) log.Println("Build context", version.BuildContext()) prometheus.MustRegister(version.NewCollector(exporter), &SIXCollector{asn}) log.Println("Starting Server: ", *listenAddress) http.Handle("/metrics", promhttp.Handler()) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(` SIX Status Exporter

SIX Status Exporter

Metrics

`)) }) log.Fatal(http.ListenAndServe(*listenAddress, nil)) }