Skip to content
Snippets Groups Projects
Commit 4dcf85c9 authored by Daniel Czerwonk's avatar Daniel Czerwonk Committed by takt
Browse files

BGP and VRF metrics (#204)

* first steps to metrics

* next small step

* fixed tests

* added comments

* postponed RouterAccepted and RoutesRejected to next step

* read update counters

* added test

* added missing test file

* Route not used any more

* added metrics for session state and time since establish

* increased test coverage, time since establish impl.

* added prom collector

* added metrics endpoint to BGP example

* added metrics for VRF

* removed commented metrics

* fixed logging inconsistency in example

* Update main.go

* gofmt

* fixed typo
parent 6525c1e9
No related branches found
No related tags found
No related merge requests found
Showing
with 859 additions and 16 deletions
......@@ -2,7 +2,13 @@ package main
import (
"log"
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
prom_bgp "github.com/bio-routing/bio-rd/metrics/bgp/adapter/prom"
prom_vrf "github.com/bio-routing/bio-rd/metrics/vrf/adapter/prom"
bnet "github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/protocols/bgp/server"
"github.com/bio-routing/bio-rd/routingtable/vrf"
......@@ -23,7 +29,19 @@ func main() {
log.Fatal(err)
}
go startMetricsEndpoint(b)
startServer(b, v)
select {}
}
func startMetricsEndpoint(server server.BGPServer) {
prometheus.MustRegister(prom_bgp.NewCollector(server))
prometheus.MustRegister(prom_vrf.NewCollector())
http.Handle("/metrics", promhttp.Handler())
logrus.Info("Metrics are available :8080/metrics")
logrus.Error(http.ListenAndServe(":8080", nil))
}
package prom
import (
"strconv"
"time"
"github.com/bio-routing/bio-rd/protocols/bgp/metrics"
"github.com/bio-routing/bio-rd/protocols/bgp/server"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
const (
prefix = "bio_bgp_"
)
var (
upDesc *prometheus.Desc
stateDesc *prometheus.Desc
uptimeDesc *prometheus.Desc
updatesReceivedDesc *prometheus.Desc
updatesSentDesc *prometheus.Desc
routesReceivedDesc *prometheus.Desc
routesSentDesc *prometheus.Desc
routesRejectedDesc *prometheus.Desc
routesAcceptedDesc *prometheus.Desc
)
func init() {
labels := []string{"peer_ip", "local_asn", "peer_asn", "vrf"}
upDesc = prometheus.NewDesc(prefix+"up", "Returns if the session is up", labels, nil)
stateDesc = prometheus.NewDesc(prefix+"state", "State of the BGP session (Down = 0, Idle = 1, Connect = 2, Active = 3, OpenSent = 4, OpenConfirm = 5, Established = 6)", labels, nil)
uptimeDesc = prometheus.NewDesc(prefix+"uptime_second", "Time since the session was established in seconds", labels, nil)
updatesReceivedDesc = prometheus.NewDesc(prefix+"update_received_count", "Number of updates received", labels, nil)
updatesSentDesc = prometheus.NewDesc(prefix+"update_sent_count", "Number of updates sent", labels, nil)
labels = append(labels, "afi", "safi")
routesReceivedDesc = prometheus.NewDesc(prefix+"route_received_count", "Number of routes received", labels, nil)
routesSentDesc = prometheus.NewDesc(prefix+"route_sent_count", "Number of routes sent", labels, nil)
routesRejectedDesc = prometheus.NewDesc(prefix+"route_rejected_count", "Number of routes rejected", labels, nil)
routesAcceptedDesc = prometheus.NewDesc(prefix+"route_accepted_count", "Number of routes accepted", labels, nil)
}
// NewCollector creates a new collector instance for the given BGP server
func NewCollector(server server.BGPServer) prometheus.Collector {
return &bgpCollector{server}
}
// BGPCollector provides a collector for BGP metrics of BIO to use with Prometheus
type bgpCollector struct {
server server.BGPServer
}
// Describe conforms to the prometheus collector interface
func (c *bgpCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- upDesc
ch <- stateDesc
ch <- uptimeDesc
ch <- updatesReceivedDesc
ch <- updatesSentDesc
ch <- routesReceivedDesc
ch <- routesSentDesc
ch <- routesRejectedDesc
ch <- routesAcceptedDesc
}
// Collect conforms to the prometheus collector interface
func (c *bgpCollector) Collect(ch chan<- prometheus.Metric) {
m, err := c.server.Metrics()
if err != nil {
log.Error(errors.Wrap(err, "Could not retrieve metrics from BGP server"))
return
}
for _, peer := range m.Peers {
c.collectForPeer(ch, peer)
}
}
func (c *bgpCollector) collectForPeer(ch chan<- prometheus.Metric, peer *metrics.BGPPeerMetrics) {
l := []string{
peer.IP.String(),
strconv.Itoa(int(peer.LocalASN)),
strconv.Itoa(int(peer.ASN)),
peer.VRF}
var up float64
var uptime float64
if peer.Up {
up = 1
uptime = float64(time.Since(peer.Since) * time.Second)
}
ch <- prometheus.MustNewConstMetric(upDesc, prometheus.GaugeValue, up, l...)
ch <- prometheus.MustNewConstMetric(uptimeDesc, prometheus.GaugeValue, uptime, l...)
ch <- prometheus.MustNewConstMetric(stateDesc, prometheus.GaugeValue, float64(peer.State), l...)
ch <- prometheus.MustNewConstMetric(updatesReceivedDesc, prometheus.CounterValue, float64(peer.UpdatesReceived), l...)
ch <- prometheus.MustNewConstMetric(updatesSentDesc, prometheus.CounterValue, float64(peer.UpdatesSent), l...)
for _, family := range peer.AddressFamilies {
c.collectForFamily(ch, family, l)
}
}
func (c *bgpCollector) collectForFamily(ch chan<- prometheus.Metric, family *metrics.BGPAddressFamilyMetrics, l []string) {
l = append(l, strconv.Itoa(int(family.AFI)), strconv.Itoa(int(family.SAFI)))
ch <- prometheus.MustNewConstMetric(routesReceivedDesc, prometheus.CounterValue, float64(family.RoutesReceived), l...)
ch <- prometheus.MustNewConstMetric(routesSentDesc, prometheus.CounterValue, float64(family.RoutesSent), l...)
}
package prom
import (
"strconv"
"github.com/bio-routing/bio-rd/protocols/bgp/server"
"github.com/bio-routing/bio-rd/routingtable/vrf"
"github.com/bio-routing/bio-rd/routingtable/vrf/metrics"
"github.com/prometheus/client_golang/prometheus"
)
const (
prefix = "bio_vrf_"
)
var (
routeCountDesc *prometheus.Desc
)
func init() {
labels := []string{"vrf", "rib", "afi", "safi"}
routeCountDesc = prometheus.NewDesc(prefix+"route_count", "Number of routes in the RIB", labels, nil)
}
// NewCollector creates a new collector instance for the given BGP server
func NewCollector() prometheus.Collector {
return &vrfCollector{}
}
// BGPCollector provides a collector for BGP metrics of BIO to use with Prometheus
type vrfCollector struct {
server server.BGPServer
}
// Describe conforms to the prometheus collector interface
func (c *vrfCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- routeCountDesc
}
// Collect conforms to the prometheus collector interface
func (c *vrfCollector) Collect(ch chan<- prometheus.Metric) {
for _, v := range vrf.Metrics() {
c.collectForVRF(ch, v)
}
}
func (c *vrfCollector) collectForVRF(ch chan<- prometheus.Metric, v *metrics.VRFMetrics) {
for _, rib := range v.RIBs {
ch <- prometheus.MustNewConstMetric(routeCountDesc, prometheus.GaugeValue, float64(rib.RouteCount),
v.Name, rib.Name, strconv.Itoa(int(rib.AFI)), strconv.Itoa(int(rib.SAFI)))
}
}
package metrics
// BGPAddressFamilyMetrics provides metrics on AFI/SAFI level for one session
type BGPAddressFamilyMetrics struct {
// AFI is the identifier for the address family
AFI uint16
// SAFI is the identifier for the sub address family
SAFI uint8
// RoutesReceived is the number of routes we recevied
RoutesReceived uint64
// RoutesAccepted is the number of routes we sent
RoutesSent uint64
}
package metrics
// BGPMetrics provides metrics for a single BGP server instance
type BGPMetrics struct {
// Peers is the collection of per peer metrics
Peers []*BGPPeerMetrics
}
package metrics
import (
"time"
bnet "github.com/bio-routing/bio-rd/net"
)
// BGPPeerMetrics provides metrics for one BGP session
type BGPPeerMetrics struct {
// IP is the remote IP of the peer
IP bnet.IP
// ASN is the ASN of the peer
ASN uint32
// LocalASN is our local ASN
LocalASN uint32
// VRF is the name of the VRF the peer is configured in
VRF string
// Since is the time the session was established
Since time.Time
// State of the BGP session (Down = 0, Idle = 1, Connect = 2, Active = 3, OpenSent = 4, OpenConfirm = 5, Established = 6)
State uint8
// Up returns if the session is established
Up bool
// UpdatesReceived is the number of update messages received on this session
UpdatesReceived uint64
// UpdatesReceived is the number of update messages we sent on this session
UpdatesSent uint64
// AddressFamilies provides metrics on AFI/SAFI level
AddressFamilies []*BGPAddressFamilyMetrics
}
......@@ -22,6 +22,13 @@ const (
AutomaticStartWithPassiveTcpEstablishment = 5
AutomaticStop = 8
Cease = 100
stateNameIdle = "idle"
stateNameConnect = "connect"
stateNameActive = "active"
stateNameOpenSent = "openSent"
stateNameOpenConfirm = "openConfirm"
stateNameEstablished = "established"
stateNameCease = "cease"
)
type state interface {
......@@ -70,6 +77,9 @@ type FSM struct {
reason string
active bool
establishedTime time.Time
counters fsmCounters
connectionCancelFunc context.CancelFunc
}
......@@ -100,6 +110,7 @@ func newFSM(peer *peer) *FSM {
msgRecvCh: make(chan []byte),
msgRecvFailCh: make(chan error),
stopMsgRecvCh: make(chan struct{}),
counters: fsmCounters{},
}
if peer.ipv4 != nil {
......@@ -162,10 +173,14 @@ func (fsm *FSM) run() {
}).Info("FSM: Neighbor state change")
}
if newState == "cease" {
if newState == stateNameCease {
return
}
if oldState != newState && newState == stateNameEstablished {
fsm.establishedTime = time.Now()
}
fsm.stateMu.Lock()
fsm.state = next
fsm.stateMu.Unlock()
......@@ -183,19 +198,19 @@ func (fsm *FSM) cancelRunningGoRoutines() {
func stateName(s state) string {
switch s.(type) {
case *idleState:
return "idle"
return stateNameIdle
case *connectState:
return "connect"
return stateNameConnect
case *activeState:
return "active"
return stateNameActive
case *openSentState:
return "openSent"
return stateNameOpenSent
case *openConfirmState:
return "openConfirm"
return stateNameOpenConfirm
case *establishedState:
return "established"
return stateNameEstablished
case *ceaseState:
return "cease"
return stateNameCease
default:
panic(fmt.Sprintf("Unknown state: %v", s))
}
......
package server
type fsmCounters struct {
updatesReceived uint64
updatesSent uint64
}
func (c *fsmCounters) reset() {
c.updatesReceived = 0
c.updatesSent = 0
}
......@@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"net"
"sync/atomic"
"time"
bnet "github.com/bio-routing/bio-rd/net"
......@@ -105,6 +106,8 @@ func (s *establishedState) uninit() {
if s.fsm.ipv6Unicast != nil {
s.fsm.ipv6Unicast.dispose()
}
s.fsm.counters.reset()
}
func (s *establishedState) manualStop() (state, string) {
......@@ -192,6 +195,8 @@ func (s *establishedState) notification() (state, string) {
}
func (s *establishedState) update(u *packet.BGPUpdate) (state, string) {
atomic.AddUint64(&s.fsm.counters.updatesReceived, 1)
if s.fsm.holdTime != 0 {
s.fsm.updateLastUpdateOrKeepalive()
}
......
package server
import (
"github.com/bio-routing/bio-rd/protocols/bgp/metrics"
)
const (
stateDown = 0
stateIdle = 1
stateConnect = 2
stateActive = 3
stateOpenSent = 4
stateOpenConfirm = 5
stateEstablished = 6
)
type metricsService struct {
server *bgpServer
}
func (b *metricsService) metrics() *metrics.BGPMetrics {
return &metrics.BGPMetrics{
Peers: b.peerMetrics(),
}
}
func (b *metricsService) peerMetrics() []*metrics.BGPPeerMetrics {
peers := make([]*metrics.BGPPeerMetrics, 0)
for _, peer := range b.server.peers.list() {
m := b.metricsForPeer(peer)
peers = append(peers, m)
}
return peers
}
func (b *metricsService) metricsForPeer(peer *peer) *metrics.BGPPeerMetrics {
m := &metrics.BGPPeerMetrics{
ASN: peer.peerASN,
LocalASN: peer.localASN,
IP: peer.addr,
AddressFamilies: make([]*metrics.BGPAddressFamilyMetrics, 0),
VRF: peer.vrf.Name(),
}
var fsms = peer.fsms
if len(fsms) == 0 {
return m
}
fsm := fsms[0]
m.State = b.statusFromFSM(fsm)
m.Up = m.State == stateEstablished
if m.Up {
m.Since = fsm.establishedTime
}
m.UpdatesReceived = fsm.counters.updatesReceived
m.UpdatesSent = fsm.counters.updatesSent
if peer.ipv4 != nil {
m.AddressFamilies = append(m.AddressFamilies, b.metricsForFamily(fsm.ipv4Unicast))
}
if peer.ipv6 != nil {
m.AddressFamilies = append(m.AddressFamilies, b.metricsForFamily(fsm.ipv6Unicast))
}
return m
}
func (b *metricsService) metricsForFamily(family *fsmAddressFamily) *metrics.BGPAddressFamilyMetrics {
return &metrics.BGPAddressFamilyMetrics{
AFI: family.afi,
SAFI: family.safi,
RoutesReceived: uint64(family.adjRIBIn.RouteCount()),
RoutesSent: uint64(family.adjRIBOut.RouteCount()),
}
}
func (b *metricsService) statusFromFSM(fsm *FSM) uint8 {
switch fsm.state.(type) {
case *idleState:
return stateIdle
case *connectState:
return stateConnect
case *activeState:
return stateActive
case *openSentState:
return stateOpenSent
case *openConfirmState:
return stateOpenConfirm
case *establishedState:
return stateEstablished
}
return stateDown
}
package server
import (
"testing"
"time"
"github.com/bio-routing/bio-rd/protocols/bgp/packet"
"github.com/bio-routing/bio-rd/routingtable"
"github.com/bio-routing/bio-rd/routingtable/vrf"
"github.com/stretchr/testify/assert"
bnet "github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/protocols/bgp/metrics"
)
func TestMetrics(t *testing.T) {
vrf, _ := vrf.New("inet.0")
establishedTime := time.Now()
tests := []struct {
name string
peer *peer
withoutFSM bool
state state
updatesReceived uint64
updatesSent uint64
ipv4RoutesReceived int64
ipv4RoutesSent int64
ipv6RoutesReceived int64
ipv6RoutesSent int64
expected *metrics.BGPMetrics
}{
{
name: "Established",
peer: &peer{
peerASN: 202739,
localASN: 201701,
addr: bnet.IPv4(100),
ipv4: &peerAddressFamily{},
ipv6: &peerAddressFamily{},
vrf: vrf,
},
state: &establishedState{},
updatesReceived: 3,
updatesSent: 4,
ipv4RoutesReceived: 5,
ipv4RoutesSent: 6,
ipv6RoutesReceived: 7,
ipv6RoutesSent: 8,
expected: &metrics.BGPMetrics{
Peers: []*metrics.BGPPeerMetrics{
{
IP: bnet.IPv4(100),
ASN: 202739,
LocalASN: 201701,
UpdatesReceived: 3,
UpdatesSent: 4,
VRF: "inet.0",
Up: true,
State: stateEstablished,
Since: establishedTime,
AddressFamilies: []*metrics.BGPAddressFamilyMetrics{
{
AFI: packet.IPv4AFI,
SAFI: packet.UnicastSAFI,
RoutesReceived: 5,
RoutesSent: 6,
},
{
AFI: packet.IPv6AFI,
SAFI: packet.UnicastSAFI,
RoutesReceived: 7,
RoutesSent: 8,
},
},
},
},
},
},
{
name: "Idle",
peer: &peer{
peerASN: 202739,
localASN: 201701,
addr: bnet.IPv4(100),
ipv4: &peerAddressFamily{},
ipv6: &peerAddressFamily{},
vrf: vrf,
},
state: &idleState{},
expected: &metrics.BGPMetrics{
Peers: []*metrics.BGPPeerMetrics{
{
IP: bnet.IPv4(100),
ASN: 202739,
LocalASN: 201701,
VRF: "inet.0",
State: stateIdle,
AddressFamilies: []*metrics.BGPAddressFamilyMetrics{
{
AFI: packet.IPv4AFI,
SAFI: packet.UnicastSAFI,
},
{
AFI: packet.IPv6AFI,
SAFI: packet.UnicastSAFI,
},
},
},
},
},
},
{
name: "Active",
peer: &peer{
peerASN: 202739,
localASN: 201701,
addr: bnet.IPv4(100),
ipv4: &peerAddressFamily{},
ipv6: &peerAddressFamily{},
vrf: vrf,
},
state: &activeState{},
expected: &metrics.BGPMetrics{
Peers: []*metrics.BGPPeerMetrics{
{
IP: bnet.IPv4(100),
ASN: 202739,
LocalASN: 201701,
VRF: "inet.0",
State: stateActive,
AddressFamilies: []*metrics.BGPAddressFamilyMetrics{
{
AFI: packet.IPv4AFI,
SAFI: packet.UnicastSAFI,
},
{
AFI: packet.IPv6AFI,
SAFI: packet.UnicastSAFI,
},
},
},
},
},
},
{
name: "OpenSent",
peer: &peer{
peerASN: 202739,
localASN: 201701,
addr: bnet.IPv4(100),
ipv4: &peerAddressFamily{},
ipv6: &peerAddressFamily{},
vrf: vrf,
},
state: &openSentState{},
expected: &metrics.BGPMetrics{
Peers: []*metrics.BGPPeerMetrics{
{
IP: bnet.IPv4(100),
ASN: 202739,
LocalASN: 201701,
VRF: "inet.0",
State: stateOpenSent,
AddressFamilies: []*metrics.BGPAddressFamilyMetrics{
{
AFI: packet.IPv4AFI,
SAFI: packet.UnicastSAFI,
},
{
AFI: packet.IPv6AFI,
SAFI: packet.UnicastSAFI,
},
},
},
},
},
},
{
name: "OpenConfirm",
peer: &peer{
peerASN: 202739,
localASN: 201701,
addr: bnet.IPv4(100),
ipv4: &peerAddressFamily{},
ipv6: &peerAddressFamily{},
vrf: vrf,
},
state: &openConfirmState{},
expected: &metrics.BGPMetrics{
Peers: []*metrics.BGPPeerMetrics{
{
IP: bnet.IPv4(100),
ASN: 202739,
LocalASN: 201701,
VRF: "inet.0",
State: stateOpenConfirm,
AddressFamilies: []*metrics.BGPAddressFamilyMetrics{
{
AFI: packet.IPv4AFI,
SAFI: packet.UnicastSAFI,
},
{
AFI: packet.IPv6AFI,
SAFI: packet.UnicastSAFI,
},
},
},
},
},
},
{
name: "Connect",
peer: &peer{
peerASN: 202739,
localASN: 201701,
addr: bnet.IPv4(100),
ipv4: &peerAddressFamily{},
ipv6: &peerAddressFamily{},
vrf: vrf,
},
state: &connectState{},
expected: &metrics.BGPMetrics{
Peers: []*metrics.BGPPeerMetrics{
{
IP: bnet.IPv4(100),
ASN: 202739,
LocalASN: 201701,
VRF: "inet.0",
State: stateConnect,
AddressFamilies: []*metrics.BGPAddressFamilyMetrics{
{
AFI: packet.IPv4AFI,
SAFI: packet.UnicastSAFI,
},
{
AFI: packet.IPv6AFI,
SAFI: packet.UnicastSAFI,
},
},
},
},
},
},
{
name: "without fsm",
withoutFSM: true,
peer: &peer{
peerASN: 202739,
localASN: 201701,
addr: bnet.IPv4(100),
ipv4: &peerAddressFamily{},
ipv6: &peerAddressFamily{},
vrf: vrf,
},
expected: &metrics.BGPMetrics{
Peers: []*metrics.BGPPeerMetrics{
{
IP: bnet.IPv4(100),
ASN: 202739,
LocalASN: 201701,
VRF: "inet.0",
AddressFamilies: []*metrics.BGPAddressFamilyMetrics{},
},
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if !test.withoutFSM {
fsm := newFSM(test.peer)
test.peer.fsms = append(test.peer.fsms, fsm)
fsm.state = test.state
fsm.counters.updatesReceived = test.updatesReceived
fsm.counters.updatesSent = test.updatesSent
fsm.ipv4Unicast.adjRIBIn = &routingtable.RTMockClient{FakeRouteCount: test.ipv4RoutesReceived}
fsm.ipv4Unicast.adjRIBOut = &routingtable.RTMockClient{FakeRouteCount: test.ipv4RoutesSent}
fsm.ipv6Unicast.adjRIBIn = &routingtable.RTMockClient{FakeRouteCount: test.ipv6RoutesReceived}
fsm.ipv6Unicast.adjRIBOut = &routingtable.RTMockClient{FakeRouteCount: test.ipv6RoutesSent}
fsm.establishedTime = establishedTime
}
s := newBgpServer()
s.peers.add(test.peer)
actual, err := s.Metrics()
if err != nil {
t.Fatalf("unecpected error: %v", err)
}
assert.Equal(t, test.expected, actual)
})
}
}
......@@ -2,6 +2,7 @@ package server
import (
"fmt"
"github.com/bio-routing/bio-rd/routingtable/vrf"
"sync"
"time"
......@@ -36,6 +37,7 @@ type peer struct {
ipv4MultiProtocolAdvertised bool
clusterID uint32
vrf *vrf.VRF
ipv4 *peerAddressFamily
ipv6 *peerAddressFamily
}
......@@ -164,6 +166,7 @@ func newPeer(c config.Peer, server *bgpServer) (*peer, error) {
routeServerClient: c.RouteServerClient,
routeReflectorClient: c.RouteReflectorClient,
clusterID: c.RouteReflectorClusterID,
vrf: c.VRF,
}
if c.IPv4 != nil {
......
package server
import (
"fmt"
"net"
"github.com/bio-routing/bio-rd/routingtable/adjRIBOut"
......@@ -9,6 +10,7 @@ import (
"github.com/bio-routing/bio-rd/config"
bnet "github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/protocols/bgp/metrics"
bnetutils "github.com/bio-routing/bio-rd/util/net"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
......@@ -24,21 +26,31 @@ type bgpServer struct {
peers *peerManager
routerID uint32
localASN uint32
metrics *metricsService
}
type BGPServer interface {
RouterID() uint32
Start(*config.Global) error
AddPeer(config.Peer) error
Metrics() (*metrics.BGPMetrics, error)
GetRIBIn(peerIP bnet.IP, afi uint16, safi uint8) *adjRIBIn.AdjRIBIn
GetRIBOut(peerIP bnet.IP, afi uint16, safi uint8) *adjRIBOut.AdjRIBOut
ConnectMockPeer(peer config.Peer, con net.Conn)
}
// NewBgpServer creates a new instance of bgpServer
func NewBgpServer() BGPServer {
return &bgpServer{
return newBgpServer()
}
func newBgpServer() *bgpServer {
server := &bgpServer{
peers: newPeerManager(),
}
server.metrics = &metricsService{server}
return server
}
func (b *bgpServer) RouterID() uint32 {
......@@ -163,3 +175,11 @@ func (b *bgpServer) AddPeer(c config.Peer) error {
return nil
}
func (b *bgpServer) Metrics() (*metrics.BGPMetrics, error) {
if b.metrics == nil {
return nil, fmt.Errorf("Server not started yet")
}
return b.metrics.metrics(), nil
}
......@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"sync"
"sync/atomic"
"time"
bnet "github.com/bio-routing/bio-rd/net"
......@@ -180,6 +181,7 @@ func (u *UpdateSender) sendUpdates(pathAttrs *packet.PathAttribute, updatePrefix
if err != nil {
log.Errorf("Failed to serialize and send: %v", err)
}
atomic.AddUint64(&u.fsm.counters.updatesSent, 1)
}
}
......
......@@ -48,12 +48,9 @@ func (a *LocRIB) GetContributingASNs() *routingtable.ContributingASNs {
return a.contributingASNs
}
//Count routes from the LocRIP
// Count routes from the LocRIB
func (a *LocRIB) Count() uint64 {
a.mu.RLock()
defer a.mu.RUnlock()
return uint64(len(a.rt.Dump()))
return uint64(a.rt.GetRouteCount())
}
// Dump dumps the RIB
......
......@@ -13,7 +13,8 @@ type RemovePathParams struct {
}
type RTMockClient struct {
removed []*RemovePathParams
removed []*RemovePathParams
FakeRouteCount int64
}
func NewRTMockClient() *RTMockClient {
......@@ -67,5 +68,5 @@ func (m *RTMockClient) RemovePath(pfx net.Prefix, p *route.Path) bool {
}
func (m *RTMockClient) RouteCount() int64 {
return 0
return m.FakeRouteCount
}
package vrf
import (
"github.com/bio-routing/bio-rd/routingtable/vrf/metrics"
)
// Metrics returns metrics for all VRFs
func Metrics() []*metrics.VRFMetrics {
vrfs := globalRegistry.list()
m := make([]*metrics.VRFMetrics, len(vrfs))
i := 0
for _, v := range vrfs {
m[i] = metricsForVRF(v)
i++
}
return m
}
func metricsForVRF(v *VRF) *metrics.VRFMetrics {
m := &metrics.VRFMetrics{
Name: v.Name(),
RIBs: make([]*metrics.RIBMetrics, 0),
}
for family, rib := range v.ribs {
m.RIBs = append(m.RIBs, &metrics.RIBMetrics{
Name: v.nameForRIB(rib),
AFI: family.afi,
SAFI: family.safi,
RouteCount: rib.Count(),
})
}
return m
}
package metrics
// RIBMetrics represents metrics of a RIB in a VRF
type RIBMetrics struct {
// Name of the RIB
Name string
// AFI is the identifier for the address family
AFI uint16
// SAFI is the identifier for the sub address family
SAFI uint8
// Number of routes in the RIB
RouteCount uint64
}
package metrics
// VRFMetrics represents a collection of metrics of one VRF
type VRFMetrics struct {
// Name of the VRF
Name string
// RIBs returns the RIB specific metrics
RIBs []*RIBMetrics
}
package vrf
import (
"sort"
"testing"
"github.com/stretchr/testify/assert"
bnet "github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/route"
"github.com/bio-routing/bio-rd/routingtable/vrf/metrics"
)
func TestMetrics(t *testing.T) {
green, err := New("green")
if err != nil {
t.Fatal(err)
}
green.IPv4UnicastRIB().AddPath(bnet.NewPfx(bnet.IPv4FromOctets(8, 0, 0, 0), 8), &route.Path{})
green.IPv4UnicastRIB().AddPath(bnet.NewPfx(bnet.IPv4FromOctets(8, 0, 0, 0), 16), &route.Path{})
green.IPv6UnicastRIB().AddPath(bnet.NewPfx(bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0), 48), &route.Path{})
red, err := New("red")
if err != nil {
t.Fatal(err)
}
red.IPv6UnicastRIB().AddPath(bnet.NewPfx(bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0x100, 0, 0, 0, 0), 64), &route.Path{})
red.IPv6UnicastRIB().AddPath(bnet.NewPfx(bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0x200, 0, 0, 0, 0), 64), &route.Path{})
expected := []*metrics.VRFMetrics{
{
Name: "green",
RIBs: []*metrics.RIBMetrics{
{
Name: "inet.0",
AFI: afiIPv4,
SAFI: safiUnicast,
RouteCount: 2,
},
{
Name: "inet6.0",
AFI: afiIPv6,
SAFI: safiUnicast,
RouteCount: 1,
},
},
},
{
Name: "red",
RIBs: []*metrics.RIBMetrics{
{
Name: "inet.0",
AFI: afiIPv4,
SAFI: safiUnicast,
RouteCount: 0,
},
{
Name: "inet6.0",
AFI: afiIPv6,
SAFI: safiUnicast,
RouteCount: 2,
},
},
},
}
actual := Metrics()
sortResult(actual)
assert.Equal(t, expected, actual)
}
func sortResult(m []*metrics.VRFMetrics) {
sort.Slice(m, func(i, j int) bool {
return m[i].Name < m[j].Name
})
for _, v := range m {
sort.Slice(v.RIBs, func(i, j int) bool {
return m[i].Name < m[j].Name
})
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment