Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
436a080c04 | ||
|
b91420dac9 | ||
|
bc395d021d | ||
|
c33bf96597 | ||
|
bca0f75c46 | ||
|
728f44b32b | ||
|
89b333ecf7 | ||
|
1fb8f9a7c4 | ||
|
4aeeb8bbdf | ||
|
2121286c5d | ||
|
b73c8b33d8 | ||
|
8f8629c55a | ||
|
ae069e2ba6 | ||
|
91670f7db0 | ||
|
0cfee83602 | ||
|
37caf859c0 | ||
|
24770bc958 | ||
|
c2816008b2 | ||
|
7da417313e | ||
|
c89dbb6816 | ||
|
ff2b694a8d | ||
|
d058a1d8a0 | ||
|
304b44675b | ||
|
66c0abb5aa | ||
|
8565edd568 | ||
|
7483977d16 | ||
|
5ed1dea3ed | ||
|
8c428da635 | ||
|
ffcc5cbcf8 | ||
|
e406222613 | ||
|
914c7a4491 | ||
|
cb0ac8af66 | ||
|
848a8026ef | ||
|
dfe58edcbd |
2
.gitignore
vendored
2
.gitignore
vendored
@ -35,3 +35,5 @@ var/
|
||||
*coverage*
|
||||
|
||||
dist/
|
||||
|
||||
.idea/
|
56
README.md
56
README.md
@ -3,22 +3,32 @@ __"No, no! The adventures first, explanations take such a dreadful time."__
|
||||
_Lewis Carroll, Alice's Adventures in Wonderland & Through the Looking-Glass_
|
||||
|
||||
Take a look at Alice-LG production examples at:
|
||||
- https://lg.de-cix.net/
|
||||
- https://lg.ams-ix.net
|
||||
- https://lg.bcix.de/
|
||||
- https://lg.megaport.com/
|
||||
- https://lg.netnod.se/
|
||||
- https://alice-rs.linx.net/
|
||||
- https://lg.ix.br/
|
||||
- https://lg.bcix.de
|
||||
- https://lg.dd-ix.net
|
||||
- https://lg.de-cix.net
|
||||
- https://glass.gigapix.pt
|
||||
- https://lg.ix.br
|
||||
- https://alice.sfmix.org/
|
||||
- https://alice-rs.linx.net
|
||||
- https://lg.megaport.com
|
||||
- https://lg.netnod.se
|
||||
- https://lg.s-ix.de
|
||||
- https://lg.top-ix.org
|
||||
- https://alice.sfmix.org/
|
||||
- https://lg.ix.asn.au/
|
||||
- https://lg.ix.nz/
|
||||
|
||||
And checkout the API at:
|
||||
And check out the API at:
|
||||
|
||||
- https://lg.de-cix.net/api/v1/config
|
||||
- https://lg.de-cix.net/api/v1/status
|
||||
- https://lg.de-cix.net/api/v1/routeservers
|
||||
- https://lg.de-cix.net/api/v1/routeservers/rs1_fra_ipv4/status
|
||||
- https://lg.de-cix.net/api/v1/routeservers/rs1_fra_ipv4/neighbors
|
||||
- https://lg.de-cix.net/api/v1/routeservers/rs1_fra_ipv4/neighbors/R194_106/routes
|
||||
- https://lg.de-cix.net/api/v1/routeservers/rs1_fra_ipv4/neighbors/R194_106/routes/received
|
||||
- https://lg.de-cix.net/api/v1/routeservers/rs1_fra_ipv4/neighbors/R194_106/routes/filtered
|
||||
- https://lg.de-cix.net/api/v1/routeservers/rs1_fra_ipv4/neighbors/R194_106/routes/not-exported
|
||||
- https://lg.de-cix.net/api/v1/lookup/prefix?q=217.115.0.0
|
||||
|
||||
|
||||
@ -45,7 +55,7 @@ Currently Alice-LG supports the following APIs:
|
||||
|
||||
### Birdwatcher
|
||||
Normally you would first install the [birdwatcher API](https://github.com/alice-lg/birdwatcher) directly on the machine(s) where you run [BIRD](http://bird.network.cz/) on
|
||||
and then install Alice-LG on a seperate public facing server and point her to the afore mentioned [birdwatcher API](https://github.com/alice-lg/birdwatcher).
|
||||
and then install Alice-LG on a separate public facing server and point her to the afore mentioned [birdwatcher API](https://github.com/alice-lg/birdwatcher).
|
||||
|
||||
This project was a direct result of the [RIPE IXP Tools Hackathon](https://atlas.ripe.net/hackathon/ixp-tools/)
|
||||
just prior to [RIPE73](https://ripe73.ripe.net/) in Madrid, Spain.
|
||||
@ -190,10 +200,10 @@ with the optional parameter (the "mountpoint" of the theme)
|
||||
url_base = /theme
|
||||
|
||||
|
||||
You can put assets (images, fonts, javscript, css) in
|
||||
You can put assets (images, fonts, javascript, css) in
|
||||
this folder.
|
||||
|
||||
Stylesheets and Javascripts are automatically included in
|
||||
Stylesheets and JavaScripts are automatically included in
|
||||
the client's html and are served from the backend.
|
||||
|
||||
Alice provides early stages of an extension API, which is for now
|
||||
@ -221,6 +231,30 @@ Alice.onLayoutReady(function(page) {
|
||||
|
||||
For an example check out: https://github.com/alice-lg/alice-theme-example
|
||||
|
||||
## Metrics
|
||||
|
||||
When `enable_prometheus` is set to `true` in the configuration, Alice will expose metrics on `/metrics` in Prometheus
|
||||
format.
|
||||
|
||||
The following metrics are available:
|
||||
|
||||
- `neighbor_state` - The state of the BGP peers (1 = established, 0 = not established)
|
||||
- `neighbor_uptime` - The uptime of the BGP peers in seconds
|
||||
- `routes_accepted` - The number of routes accepted from a peer
|
||||
- `routes_filtered` - The number of routes filtered from a peer
|
||||
- `routes_preferred` - The number of routes preferred from a peer
|
||||
- `routes_received` - The number of routes received from a peer
|
||||
|
||||
The following labels are available:
|
||||
|
||||
- `neighbor_address` - The address of the BGP peer
|
||||
- `neighbor_asn` - The ASN of the BGP peer
|
||||
- `neighbor_description` - The description of the BGP peer
|
||||
- `neighbor_id` - The ID of the BGP peer
|
||||
- `route_server_group` - The group of the route server
|
||||
- `route_server_id` - The ID of the route server
|
||||
- `route_server_name` - The name of the route server
|
||||
|
||||
## Hacking
|
||||
|
||||
The client is a Single Page React Application.
|
||||
|
@ -133,6 +133,11 @@ func main() {
|
||||
go routesStore.Start(ctx)
|
||||
}
|
||||
|
||||
// Start exporting metrics
|
||||
if cfg.Server.EnableMetrics {
|
||||
go store.StartMetrics(ctx, neighborsStore)
|
||||
}
|
||||
|
||||
// Start the Housekeeping
|
||||
go store.StartHousekeeping(ctx, cfg)
|
||||
|
||||
|
@ -53,6 +53,12 @@ routes_store_query_limit = 200000
|
||||
# parsing a master4 table will take about 2.5 instead of 1.25 minutes.
|
||||
stream_parser_throttle = 10000
|
||||
|
||||
# Enable metrics endpoint
|
||||
# When `enable_metrics` is set to `true`, Alice will export metrics
|
||||
# on `/metrics` in Prometheus (openmetrics) format.
|
||||
# Default: false
|
||||
enable_metrics = false
|
||||
|
||||
# [postgres]
|
||||
# url = "postgres://postgres:postgres@localhost:5432/alice"
|
||||
|
||||
@ -83,8 +89,10 @@ routes_accepted_page_size = 250
|
||||
routes_not_exported_page_size = 250
|
||||
|
||||
[rejection_reasons]
|
||||
# a pair of a large BGP community value and a string to signal the processing
|
||||
# results of route filtering
|
||||
# BGP communities which, when present on a prefix represent the route as having
|
||||
# been filtered out.
|
||||
# Additionally, communities for searching and descriptions should be listed in
|
||||
# the [bgp_communities] section of this configuration.
|
||||
9033:65666:1 = An IP Bogon was detected
|
||||
9033:65666:2 = Prefix is longer than 64
|
||||
9033:65666:3 = Prefix is longer than 24
|
||||
@ -140,7 +148,8 @@ unknown = 23042:1000:2
|
||||
invalid = 23042:1000:4-*
|
||||
|
||||
|
||||
# Define other known bgp communities
|
||||
# Define known bgp communities which should be recognized and described in the
|
||||
# Alice web UI
|
||||
[bgp_communities]
|
||||
1:23 = some tag
|
||||
9033:65666:1 = ip bogon detected
|
||||
|
20
go.mod
20
go.mod
@ -8,12 +8,15 @@ require (
|
||||
github.com/jackc/pgx/v4 v4.18.1
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
github.com/osrg/gobgp v0.0.0-20190502094614-fd6618fed499
|
||||
github.com/prometheus/client_golang v1.20.4
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/stretchr/testify v1.9.0
|
||||
google.golang.org/grpc v1.60.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.14.1 // indirect
|
||||
@ -23,12 +26,17 @@ require (
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
|
||||
github.com/jackc/pgtype v1.14.0 // indirect
|
||||
github.com/jackc/puddle v1.3.0 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
48
go.sum
48
go.sum
@ -2,6 +2,10 @@ github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/armon/go-radix v0.0.0-20170727155443-1fca145dffbc/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
@ -26,7 +30,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/hashicorp/hcl v0.0.0-20170509225359-392dba7d905e/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
@ -85,16 +89,19 @@ github.com/jessevdk/go-flags v1.3.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.0.0-20160823170715-cfb55aafdaf3/go.mod h1:Bvhd+E3laJ0AVkG0c9rmtZcnhV0HQ3+c3YxxqTvc/gA=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.0.0-20160504234017-7cafcd837844/go.mod h1:sjUstKUATFIcff4qlB53Kml0wQPtJVc/3fWrmuUmcfA=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
@ -107,6 +114,8 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/osrg/gobgp v0.0.0-20190502094614-fd6618fed499 h1:uukk7LjpCIRDOnLORZG8m39q9y47SNsi56w0oUj3Xrg=
|
||||
github.com/osrg/gobgp v0.0.0-20190502094614-fd6618fed499/go.mod h1:ORFhbKMbE5PuTrFOETR32zPLBMJUGIP1uMOqVyEhTAU=
|
||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||
@ -115,7 +124,16 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
|
||||
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
@ -149,8 +167,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/vishvananda/netlink v0.0.0-20170802012344-a95659537721/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||
github.com/vishvananda/netns v0.0.0-20170707011535-86bef332bfc3/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
@ -177,8 +195,8 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
@ -190,8 +208,8 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -212,8 +230,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@ -225,8 +243,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
@ -251,11 +269,11 @@ google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
|
||||
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170721122051-25c4ec802a7d/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
|
@ -72,7 +72,7 @@ type Rpki struct {
|
||||
}
|
||||
|
||||
// Meta contains response meta information
|
||||
// like cacheing time and cache ttl or the API version
|
||||
// like caching time and cache ttl or the API version
|
||||
type Meta struct {
|
||||
Version string `json:"version"`
|
||||
CacheStatus CacheStatus `json:"cache_status"`
|
||||
|
@ -130,7 +130,7 @@ func TestHasCommunity(t *testing.T) {
|
||||
}
|
||||
|
||||
if bgp.HasLargeCommunity(Community{23, 42}) != false {
|
||||
t.Error("23:42 should not be present in large commnuties")
|
||||
t.Error("23:42 should not be present in large communities")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -372,7 +372,7 @@ func NewSearchFilters() *SearchFilters {
|
||||
return groups
|
||||
}
|
||||
|
||||
// GetGroupByKey retrievs a search filter group
|
||||
// GetGroupByKey retrieves a search filter group
|
||||
// by a string.
|
||||
func (s *SearchFilters) GetGroupByKey(key string) *SearchFilterGroup {
|
||||
// This is an optimization (this is basically a fixed hash map,
|
||||
|
@ -12,7 +12,7 @@ used key.
|
||||
*/
|
||||
type LRUMap map[string]time.Time
|
||||
|
||||
// LRU retrievs the least recently used key
|
||||
// LRU retrieves the least recently used key
|
||||
func (lrumap LRUMap) LRU() string {
|
||||
t := time.Now()
|
||||
key := ""
|
||||
|
@ -30,7 +30,7 @@ func NewNeighborsCache(disabled bool) *NeighborsCache {
|
||||
return cache
|
||||
}
|
||||
|
||||
// Get retrievs the neighbors response from the cache, if present,
|
||||
// Get retrieves the neighbors response from the cache, if present,
|
||||
// and makes sure the information is still up to date.
|
||||
func (cache *NeighborsCache) Get() *api.NeighborsResponse {
|
||||
if cache.disabled {
|
||||
|
@ -37,7 +37,7 @@ func NewRoutesCache(disabled bool, size int) *RoutesCache {
|
||||
return cache
|
||||
}
|
||||
|
||||
// Get retrievs all routes for a given neighbor
|
||||
// Get retrieves all routes for a given neighbor
|
||||
func (cache *RoutesCache) Get(neighborID string) *api.RoutesResponse {
|
||||
if cache.disabled {
|
||||
return nil
|
||||
|
@ -29,7 +29,7 @@ var (
|
||||
// not be identified from the section.
|
||||
ErrSourceTypeUnknown = errors.New("source type unknown")
|
||||
|
||||
// ErrPostgresUnconfigured will occure when the
|
||||
// ErrPostgresUnconfigured will occur when the
|
||||
// postgres database URL is required, but missing.
|
||||
ErrPostgresUnconfigured = errors.New(
|
||||
"the selected postgres backend requires configuration")
|
||||
@ -96,6 +96,7 @@ type ServerConfig struct {
|
||||
DefaultAsn int `ini:"asn"`
|
||||
EnableNeighborsStatusRefresh bool `ini:"enable_neighbors_status_refresh"`
|
||||
StreamParserThrottle int `ini:"stream_parser_throttle"`
|
||||
EnableMetrics bool `ini:"enable_metrics"`
|
||||
}
|
||||
|
||||
// PostgresConfig is the configuration for the database
|
||||
@ -731,7 +732,7 @@ func getSources(config *ini.File) ([]*SourceConfig, error) {
|
||||
|
||||
if len(sourceConfigSections) > 1 {
|
||||
// The source is ambiguous
|
||||
return nil, fmt.Errorf("%s has ambigous backends", section.Name())
|
||||
return nil, fmt.Errorf("%s has ambiguous backends", section.Name())
|
||||
}
|
||||
|
||||
// Configure backend
|
||||
@ -795,15 +796,14 @@ func getSources(config *ini.File) ([]*SourceConfig, error) {
|
||||
}
|
||||
srcCfg.Birdwatcher = c
|
||||
|
||||
log.Println("Adding birdwatcher source",
|
||||
c.Name, "of type", sourceType,
|
||||
"with peer_table_prefix", peerTablePrefix,
|
||||
"and pipe_protocol_prefix", pipeProtocolPrefix)
|
||||
if c.AltPipeProtocolSuffix != "" {
|
||||
log.Println(
|
||||
"Alt pipe protocol prefix:", c.AltPipeProtocolPrefix,
|
||||
"suffix:", c.AltPipeProtocolSuffix,
|
||||
)
|
||||
log.Println("Adding birdwatcher source", c.Name, "of type", sourceType)
|
||||
if sourceType == "multi_table" {
|
||||
log.Println(" Peer table prefix:", peerTablePrefix)
|
||||
log.Println(" Pipe protocol prefix:", pipeProtocolPrefix)
|
||||
if c.AltPipeProtocolSuffix != "" {
|
||||
log.Println(" Alternative pipe protocol prefix:", c.AltPipeProtocolPrefix)
|
||||
log.Println(" Alternative pipe protocol suffix:", c.AltPipeProtocolSuffix)
|
||||
}
|
||||
}
|
||||
|
||||
case SourceBackendGoBGP:
|
||||
@ -945,6 +945,7 @@ func LoadConfig(file string) (*Config, error) {
|
||||
RoutesStoreRefreshParallelism: 1,
|
||||
NeighborsStoreRefreshParallelism: 1,
|
||||
RoutesStoreQueryLimit: DefaultRoutesStoreQueryLimit,
|
||||
EnableMetrics: true,
|
||||
}
|
||||
if err := parsedConfig.Section("server").MapTo(&server); err != nil {
|
||||
return nil, err
|
||||
|
@ -35,7 +35,7 @@ func TestLoadConfigs(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
if label != "some tag" {
|
||||
t.Error("expcted to find example community 1:23 with 'some tag'",
|
||||
t.Error("expected to find example community 1:23 with 'some tag'",
|
||||
"but got:", label)
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package decoders
|
||||
|
||||
// MapGet retrievs a key from an expected map
|
||||
// MapGet retrieves a key from an expected map
|
||||
// it falls back if the input is not a map
|
||||
// or the key was not found.
|
||||
func MapGet(m interface{}, key string, fallback interface{}) interface{} {
|
||||
@ -15,7 +15,7 @@ func MapGet(m interface{}, key string, fallback interface{}) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
// MapGetString retrievs a key from a map and
|
||||
// MapGetString retrieves a key from a map and
|
||||
// asserts its type is a string. Otherwise fallback
|
||||
// will be returned.
|
||||
func MapGetString(m interface{}, key string, fallback string) string {
|
||||
|
@ -1,3 +1,3 @@
|
||||
// Package decoders contains functions for
|
||||
// decoding backend responses into internal datastrucures.
|
||||
// decoding backend responses into internal datastructures.
|
||||
package decoders
|
||||
|
@ -113,7 +113,7 @@ func Duration(value interface{}, fallback time.Duration) time.Duration {
|
||||
}
|
||||
|
||||
// DurationTimeframe decodes a duration: Bgpctl encodes
|
||||
// this using fmt_timeframe, whiuch outputs a format similar
|
||||
// this using fmt_timeframe, which outputs a format similar
|
||||
// to that being understood by time.ParseDuration - however
|
||||
// the time unit "w" (weeks) is not supported.
|
||||
// According to https://github.com/openbgpd-portable/openbgpd-openbsd/blob/master/src/usr.sbin/bgpctl/bgpctl.c#L586-L591
|
||||
|
@ -67,7 +67,7 @@ func (s *Server) apiRoutesListReceived(
|
||||
allRoutes := apiQueryFilterNextHopGateway(req, "q", result.Imported)
|
||||
routes := api.Routes{}
|
||||
|
||||
// Apply other (commmunity) filters
|
||||
// Apply other (community) filters
|
||||
filtersApplied, err := api.FiltersFromQuery(req.URL.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -145,7 +145,7 @@ func (s *Server) apiRoutesListFiltered(
|
||||
allRoutes := apiQueryFilterNextHopGateway(req, "q", result.Filtered)
|
||||
routes := api.Routes{}
|
||||
|
||||
// Apply other (commmunity) filters
|
||||
// Apply other (community) filters
|
||||
filtersApplied, err := api.FiltersFromQuery(req.URL.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -223,7 +223,7 @@ func (s *Server) apiRoutesListNotExported(
|
||||
allRoutes := apiQueryFilterNextHopGateway(req, "q", result.NotExported)
|
||||
routes := api.Routes{}
|
||||
|
||||
// Apply other (commmunity) filters
|
||||
// Apply other (community) filters
|
||||
filtersApplied, err := api.FiltersFromQuery(req.URL.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -31,7 +31,7 @@ func TestApiRoutesPagination(t *testing.T) {
|
||||
}
|
||||
|
||||
if pagination.Page != 0 {
|
||||
t.Error("Exptected current page to be 0, got:", pagination.Page)
|
||||
t.Error("Expected current page to be 0, got:", pagination.Page)
|
||||
}
|
||||
|
||||
// Check paginated slicing
|
||||
|
@ -48,7 +48,7 @@ func TestApiQueryFilterNextHopGateway(t *testing.T) {
|
||||
)
|
||||
|
||||
if len(filtered) != 2 {
|
||||
t.Error("Exptected 2 routes, got:", len(filtered))
|
||||
t.Error("Expected 2 routes, got:", len(filtered))
|
||||
}
|
||||
|
||||
// Check presence of route_01 and _03, matching prefix 123.
|
||||
|
@ -19,7 +19,7 @@ func (err *ErrValidationFailed) Error() string {
|
||||
return err.Reason
|
||||
}
|
||||
|
||||
// NewErrMissingParam returns a new error idicating
|
||||
// NewErrMissingParam returns a new error indicating
|
||||
// a missing query parameter.
|
||||
func NewErrMissingParam(key string) *ErrValidationFailed {
|
||||
return &ErrValidationFailed{
|
||||
@ -28,12 +28,12 @@ func NewErrMissingParam(key string) *ErrValidationFailed {
|
||||
}
|
||||
}
|
||||
|
||||
// NewErrAmbigousParam returns an ErrValidationFailed,
|
||||
// indicating that the parameter was ambigous.
|
||||
func NewErrAmbigousParam(key string) *ErrValidationFailed {
|
||||
// NewErrAmbiguousParam returns an ErrValidationFailed,
|
||||
// indicating that the parameter was ambiguous.
|
||||
func NewErrAmbiguousParam(key string) *ErrValidationFailed {
|
||||
return &ErrValidationFailed{
|
||||
Param: key,
|
||||
Reason: fmt.Sprintf("query parameter %s is ambigous", key),
|
||||
Reason: fmt.Sprintf("query parameter %s is ambiguous", key),
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ func validateQueryString(req *http.Request, key string) (string, error) {
|
||||
}
|
||||
|
||||
if len(values) != 1 {
|
||||
return "", NewErrAmbigousParam(key)
|
||||
return "", NewErrAmbiguousParam(key)
|
||||
}
|
||||
|
||||
value := values[0]
|
||||
|
23
pkg/http/metrics.go
Normal file
23
pkg/http/metrics.go
Normal file
@ -0,0 +1,23 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
func (s *Server) registerMetrics(
|
||||
ctx context.Context,
|
||||
router *httprouter.Router,
|
||||
) error {
|
||||
if s.cfg.Server.EnableMetrics == false {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Println("Metrics enabled and available on: /metrics")
|
||||
router.Handler("GET", "/metrics", promhttp.Handler())
|
||||
|
||||
return nil
|
||||
}
|
@ -8,10 +8,11 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/alice-lg/alice-lg/pkg/config"
|
||||
"github.com/alice-lg/alice-lg/pkg/store"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"github.com/alice-lg/alice-lg/pkg/config"
|
||||
"github.com/alice-lg/alice-lg/pkg/store"
|
||||
)
|
||||
|
||||
// Server provides the HTTP server for the API
|
||||
@ -51,6 +52,9 @@ func (s *Server) Start(ctx context.Context) {
|
||||
if err := s.apiRegisterEndpoints(router); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := s.registerMetrics(ctx, router); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
httpTimeout := time.Duration(s.cfg.Server.HTTPTimeout) * time.Second
|
||||
log.Println("Web server HTTP timeout set to:", httpTimeout)
|
||||
|
@ -9,7 +9,7 @@ package http
|
||||
directory will be included in the frontends HTML.
|
||||
|
||||
Additional files can be added in subdirectories.
|
||||
These are served aswell and can be used for additional
|
||||
These are served as well and can be used for additional
|
||||
assets. (E.g. a logo)
|
||||
*/
|
||||
|
||||
@ -108,7 +108,7 @@ func (t *Theme) StylesheetIncludes() string {
|
||||
return strings.Join(includes, "\n")
|
||||
}
|
||||
|
||||
// Scripts retrieve a list of includeable javascipts
|
||||
// Scripts retrieve a list of includeable javascripts
|
||||
func (t *Theme) Scripts() []string {
|
||||
return t.listIncludes(".js")
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
// Web Client
|
||||
// Handle assets and client app preprarations
|
||||
// Handle assets and client app preparations
|
||||
|
||||
// Prepare client HTML:
|
||||
// Set paths and add version to assets.
|
||||
@ -76,7 +76,7 @@ func (s *Server) webRegisterAssets(
|
||||
io.WriteString(res, themedHTML)
|
||||
})
|
||||
|
||||
// ...and all alice related paths aswell
|
||||
// ...and all alice related paths as well
|
||||
alicePaths := []string{
|
||||
"/routeservers/*path",
|
||||
"/search/*path",
|
||||
|
@ -57,7 +57,7 @@ func (p *StringListPool) Acquire(list []string) []string {
|
||||
return p.root.value
|
||||
}
|
||||
|
||||
// Make idenfier list
|
||||
// Make identifier list
|
||||
id := make([]int, len(list))
|
||||
for i, s := range list {
|
||||
// Resolve string value into int
|
||||
|
@ -96,7 +96,7 @@ func parseCacheStatus(
|
||||
|
||||
status := api.CacheStatus{
|
||||
CachedAt: cachedAtTime,
|
||||
// We ommit OrigTTL for now...
|
||||
// We omit OrigTTL for now...
|
||||
}
|
||||
|
||||
return status, nil
|
||||
@ -286,7 +286,7 @@ func parseBgpCommunities(data interface{}) []api.Community {
|
||||
return communities
|
||||
}
|
||||
|
||||
// Extract extended communtieis
|
||||
// Extract extended communities
|
||||
func parseExtBgpCommunities(data interface{}) []api.ExtCommunity {
|
||||
communities := []api.ExtCommunity{}
|
||||
ldata, ok := data.([]interface{})
|
||||
|
@ -51,7 +51,7 @@ func Test_ParseApiStatus(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
// Assertations
|
||||
// Assertions
|
||||
if apiStatus.Version != "1.7.11" {
|
||||
t.Error("Expected version: 1.7.11, got:", apiStatus.Version)
|
||||
}
|
||||
@ -106,7 +106,7 @@ func Test_RoutesParsing(t *testing.T) {
|
||||
t.Error("Expected parsed routes to be 1, not:", len(routes))
|
||||
}
|
||||
|
||||
// TODO: addo more tests
|
||||
// TODO: add more tests
|
||||
}
|
||||
|
||||
func Test_ParseServerTime(t *testing.T) {
|
||||
|
@ -30,7 +30,7 @@ type GenericBirdwatcher struct {
|
||||
routesRequiredCache *caches.RoutesCache
|
||||
routesNotExportedCache *caches.RoutesCache
|
||||
|
||||
// Mutices:
|
||||
// Mutexes:
|
||||
routesFetchMutex *LockMap
|
||||
}
|
||||
|
||||
@ -205,7 +205,7 @@ func (b *GenericBirdwatcher) ExpireCaches() int {
|
||||
return count
|
||||
}
|
||||
|
||||
// Status retrievs the current backend status
|
||||
// Status retrieves the current backend status
|
||||
func (b *GenericBirdwatcher) Status(ctx context.Context) (*api.StatusResponse, error) {
|
||||
bird, err := b.client.GetJSON(ctx, "/status")
|
||||
if err != nil {
|
||||
|
@ -57,7 +57,7 @@ func NewExtendedNexthopCapability(a *bgp.CapExtendedNexthop) *api.ExtendedNextho
|
||||
}
|
||||
}
|
||||
|
||||
// NewGracefulRestartCapability creates a new graceful resetart capabilty
|
||||
// NewGracefulRestartCapability creates a new graceful restart capability
|
||||
func NewGracefulRestartCapability(a *bgp.CapGracefulRestart) *api.GracefulRestartCapability {
|
||||
tuples := make([]*api.GracefulRestartCapabilityTuple, 0, len(a.Tuples))
|
||||
for _, t := range a.Tuples {
|
||||
@ -73,7 +73,7 @@ func NewGracefulRestartCapability(a *bgp.CapGracefulRestart) *api.GracefulRestar
|
||||
}
|
||||
}
|
||||
|
||||
// NewFourOctetASNumberCapability creates new 32bit ASN capabiliy
|
||||
// NewFourOctetASNumberCapability creates new 32bit ASN capability
|
||||
func NewFourOctetASNumberCapability(a *bgp.CapFourOctetASNumber) *api.FourOctetASNumberCapability {
|
||||
return &api.FourOctetASNumberCapability{
|
||||
As: a.CapValue,
|
||||
|
@ -52,7 +52,7 @@ func (gobgp *GoBGP) lookupNeighbor(
|
||||
return nil, fmt.Errorf("could not lookup neighbor")
|
||||
}
|
||||
|
||||
// GetNeighbors retrievs all neighbors and returns
|
||||
// GetNeighbors retrieves all neighbors and returns
|
||||
// a list of peers.
|
||||
func (gobgp *GoBGP) GetNeighbors(
|
||||
ctx context.Context,
|
||||
|
@ -93,7 +93,7 @@ func (gobgp *GoBGP) ExpireCaches() int {
|
||||
return count
|
||||
}
|
||||
|
||||
// NeighborsStatus retrievs all status information
|
||||
// NeighborsStatus retrieves all status information
|
||||
// for all peers on the RS.
|
||||
func (gobgp *GoBGP) NeighborsStatus(
|
||||
ctx context.Context,
|
||||
@ -130,12 +130,13 @@ func (gobgp *GoBGP) NeighborsStatus(
|
||||
_resp.Peer.Timers.State.Uptime.Seconds,
|
||||
int64(_resp.Peer.Timers.State.Uptime.Nanos)))
|
||||
}
|
||||
response.Neighbors = append(response.Neighbors, &ns)
|
||||
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// Status retrievs the routers status
|
||||
// Status retrieves the routers status
|
||||
func (gobgp *GoBGP) Status(
|
||||
ctx context.Context,
|
||||
) (*api.StatusResponse, error) {
|
||||
@ -154,7 +155,7 @@ func (gobgp *GoBGP) Status(
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// Neighbors retrievs a list of neighbors
|
||||
// Neighbors retrieves a list of neighbors
|
||||
func (gobgp *GoBGP) Neighbors(
|
||||
ctx context.Context,
|
||||
) (*api.NeighborsResponse, error) {
|
||||
|
@ -81,7 +81,7 @@ func (src *BgplgdSource) ShowNeighborsSummaryRequest(
|
||||
return http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
}
|
||||
|
||||
// ShowNeighborRIBRequest retrives the routes accepted from the neighbor
|
||||
// ShowNeighborRIBRequest retrieves the routes accepted from the neighbor
|
||||
// identified by bgp-id.
|
||||
func (src *BgplgdSource) ShowNeighborRIBRequest(
|
||||
ctx context.Context,
|
||||
@ -129,7 +129,7 @@ func (src *BgplgdSource) Status(ctx context.Context) (*api.StatusResponse, error
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Neighbors retrievs a full list of all neighbors
|
||||
// Neighbors retrieves a full list of all neighbors
|
||||
func (src *BgplgdSource) Neighbors(
|
||||
ctx context.Context,
|
||||
) (*api.NeighborsResponse, error) {
|
||||
@ -180,7 +180,7 @@ func (src *BgplgdSource) Neighbors(
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// NeighborsSummary retrievs list of neighbors, which
|
||||
// NeighborsSummary retrieves list of neighbors, which
|
||||
// might lack details like with number of rejected routes.
|
||||
// It is much faster though.
|
||||
func (src *BgplgdSource) NeighborsSummary(
|
||||
@ -227,8 +227,8 @@ func (src *BgplgdSource) NeighborsSummary(
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// NeighborsStatus retrives the status summary
|
||||
// for all neightbors
|
||||
// NeighborsStatus retrieves the status summary
|
||||
// for all neighbors
|
||||
func (src *BgplgdSource) NeighborsStatus(
|
||||
ctx context.Context,
|
||||
) (*api.NeighborsStatusResponse, error) {
|
||||
@ -411,7 +411,7 @@ func (src *BgplgdSource) RoutesFiltered(
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// RoutesNotExported retrievs the routes not exported
|
||||
// RoutesNotExported retrieves the routes not exported
|
||||
// from the rs for a neighbor.
|
||||
func (src *BgplgdSource) RoutesNotExported(
|
||||
ctx context.Context,
|
||||
@ -428,7 +428,7 @@ func (src *BgplgdSource) RoutesNotExported(
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// AllRoutes retrievs the entire RIB from the source. This is never
|
||||
// AllRoutes retrieves the entire RIB from the source. This is never
|
||||
// cached as it is processed by the store.
|
||||
func (src *BgplgdSource) AllRoutes(
|
||||
ctx context.Context,
|
||||
|
@ -43,7 +43,7 @@ func decodeNeighbor(n interface{}) (*api.Neighbor, error) {
|
||||
prefixes := decoders.MapGet(stats, "prefixes", map[string]interface{}{})
|
||||
|
||||
neighbor := &api.Neighbor{
|
||||
// ID: decoders.MapGetString(nb, "remote_addr", "invalid_id"),
|
||||
ID: decoders.MapGetString(nb, "remote_addr", "invalid_id"),
|
||||
Address: decoders.MapGetString(nb, "remote_addr", "invalid_address"),
|
||||
ASN: decoders.IntFromString(decoders.MapGetString(nb, "remote_as", ""), -1),
|
||||
State: decodeState(decoders.MapGetString(nb, "state", "unknown")),
|
||||
@ -70,7 +70,7 @@ func describeNeighbor(nb interface{}) string {
|
||||
return fmt.Sprintf("PEER AS%s %s", asn, addr)
|
||||
}
|
||||
|
||||
// decodeNeighbors retrievs neighbors data from
|
||||
// decodeNeighbors retrieves neighbors data from
|
||||
// the bgpctl response.
|
||||
func decodeNeighbors(res map[string]interface{}) (api.Neighbors, error) {
|
||||
nbs := decoders.MapGet(res, "neighbors", nil)
|
||||
@ -92,7 +92,7 @@ func decodeNeighbors(res map[string]interface{}) (api.Neighbors, error) {
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// decodeNeighborsStatus retrievs a neighbors summary
|
||||
// decodeNeighborsStatus retrieves a neighbors summary
|
||||
// and decodes the status.
|
||||
func decodeNeighborsStatus(res map[string]interface{}) (api.NeighborsStatus, error) {
|
||||
nbs := decoders.MapGet(res, "neighbors", nil)
|
||||
@ -234,7 +234,7 @@ func decodeCommunities(c interface{}) api.Communities {
|
||||
return comms
|
||||
}
|
||||
|
||||
// decodeExtendedCommunities decodes extended communties
|
||||
// decodeExtendedCommunities decodes extended communities
|
||||
// into a list of (str, int, int).
|
||||
func decodeExtendedCommunities(c interface{}) api.ExtCommunities {
|
||||
details := decoders.StringList(c)
|
||||
|
@ -20,7 +20,7 @@ const (
|
||||
)
|
||||
|
||||
// StateServerSource implements the OpenBGPD source for Alice.
|
||||
// It is intendet to consume structured bgpctl output
|
||||
// It is intended to consume structured bgpctl output
|
||||
// queried over HTTP using the:
|
||||
//
|
||||
// openbgpd-state-server
|
||||
@ -93,7 +93,7 @@ func (src *StateServerSource) ShowNeighborsSummaryRequest(
|
||||
return http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
}
|
||||
|
||||
// ShowNeighborRIBRequest retrives the routes accepted from the neighbor
|
||||
// ShowNeighborRIBRequest retrieves the routes accepted from the neighbor
|
||||
// identified by bgp-id.
|
||||
func (src *StateServerSource) ShowNeighborRIBRequest(
|
||||
ctx context.Context,
|
||||
@ -153,7 +153,7 @@ func (src *StateServerSource) Status(
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Neighbors retrievs a full list of all neighbors
|
||||
// Neighbors retrieves a full list of all neighbors
|
||||
func (src *StateServerSource) Neighbors(
|
||||
ctx context.Context,
|
||||
) (*api.NeighborsResponse, error) {
|
||||
@ -250,8 +250,8 @@ func (src *StateServerSource) NeighborsSummary(
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// NeighborsStatus retrives the status summary
|
||||
// for all neightbors
|
||||
// NeighborsStatus retrieves the status summary
|
||||
// for all neighbors
|
||||
func (src *StateServerSource) NeighborsStatus(
|
||||
ctx context.Context,
|
||||
) (*api.NeighborsStatusResponse, error) {
|
||||
@ -430,7 +430,7 @@ func (src *StateServerSource) RoutesFiltered(
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// RoutesNotExported retrievs the routes not exported
|
||||
// RoutesNotExported retrieves the routes not exported
|
||||
// from the rs for a neighbor.
|
||||
func (src *StateServerSource) RoutesNotExported(
|
||||
ctx context.Context,
|
||||
@ -447,7 +447,7 @@ func (src *StateServerSource) RoutesNotExported(
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// AllRoutes retrievs the entire RIB from the source. This is never
|
||||
// AllRoutes retrieves the entire RIB from the source. This is never
|
||||
// cached as it is processed by the store.
|
||||
func (src *StateServerSource) AllRoutes(
|
||||
ctx context.Context,
|
||||
|
@ -17,7 +17,7 @@ type NeighborsBackend struct {
|
||||
neighbors *sync.Map
|
||||
}
|
||||
|
||||
// NewNeighborsBackend instanciates a new in memory
|
||||
// NewNeighborsBackend instantiates a new in memory
|
||||
// neighbors backend.
|
||||
func NewNeighborsBackend() *NeighborsBackend {
|
||||
return &NeighborsBackend{
|
||||
@ -79,7 +79,7 @@ func (b *NeighborsBackend) GetNeighborsMapAt(
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CountNeighborsAt retrievs the number of neighbors
|
||||
// CountNeighborsAt retrieves the number of neighbors
|
||||
// at this source.
|
||||
func (b *NeighborsBackend) CountNeighborsAt(
|
||||
ctx context.Context,
|
||||
|
@ -112,7 +112,7 @@ func (r *RoutesBackend) FindByPrefix(
|
||||
return false
|
||||
}
|
||||
for _, route := range rs.(api.LookupRoutes) {
|
||||
// Naiive string filtering:
|
||||
// Naive string filtering:
|
||||
if hasPrefix && !strings.HasPrefix(strings.ToLower(route.Network), prefix) {
|
||||
continue
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ func (m *Manager) Start(ctx context.Context) {
|
||||
}
|
||||
|
||||
// Status retrieves the current schema version
|
||||
// and checks if migrated. In case an error occures,
|
||||
// and checks if migrated. In case an error occurs,
|
||||
// it will be included in the result.
|
||||
func (m *Manager) Status(ctx context.Context) *Status {
|
||||
status := &Status{}
|
||||
@ -82,7 +82,7 @@ func (m *Manager) Status(ctx context.Context) *Status {
|
||||
return status
|
||||
}
|
||||
|
||||
// Migrate applies the database intialisation script if required.
|
||||
// Migrate applies the database initialization script if required.
|
||||
func (m *Manager) Migrate(ctx context.Context) error {
|
||||
s := m.Status(ctx)
|
||||
if s.Migrated {
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// StartHousekeeping is a background task flushing
|
||||
// memory and expireing caches.
|
||||
// memory and expiring caches.
|
||||
func StartHousekeeping(ctx context.Context, cfg *config.Config) {
|
||||
|
||||
for {
|
||||
|
190
pkg/store/metrics.go
Normal file
190
pkg/store/metrics.go
Normal file
@ -0,0 +1,190 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
type metrics struct {
|
||||
neighborsStore *NeighborsStore
|
||||
|
||||
neighborInfo *prometheus.GaugeVec
|
||||
neighborUptime *prometheus.GaugeVec
|
||||
|
||||
routesReceived *prometheus.GaugeVec
|
||||
routesFiltered *prometheus.GaugeVec
|
||||
routesPreferred *prometheus.GaugeVec
|
||||
routesAccepted *prometheus.GaugeVec
|
||||
}
|
||||
|
||||
// Initialize
|
||||
func initMetrics(s *NeighborsStore) *metrics {
|
||||
log.Println("[metrics] Initializing export.")
|
||||
|
||||
labels := []string{
|
||||
// The route server ID
|
||||
"route_server_id",
|
||||
"route_server_name",
|
||||
"route_server_group",
|
||||
"neighbor_id",
|
||||
"neighbor_description",
|
||||
"neighbor_asn",
|
||||
"neighbor_address",
|
||||
}
|
||||
|
||||
neighborInfo := prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "neighbor_info",
|
||||
Help: "Information about the neighbor including the state",
|
||||
},
|
||||
append(labels, "neighbor_state"),
|
||||
)
|
||||
|
||||
neighborUptime := prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "neighbor_uptime_seconds_total",
|
||||
Help: "The uptime of a neighbor on a route server in seconds",
|
||||
},
|
||||
labels,
|
||||
)
|
||||
|
||||
routesReceived := prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "routes_received",
|
||||
Help: "Total number of routes received by a route server for a given neighbor",
|
||||
},
|
||||
labels,
|
||||
)
|
||||
|
||||
routesFiltered := prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "routes_filtered",
|
||||
Help: "Total number of routes filtered by a route server for a given neighbor",
|
||||
},
|
||||
labels,
|
||||
)
|
||||
|
||||
routesPreferred := prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "routes_preferred",
|
||||
Help: "Total number of routes preferred by a route server for a given neighbor",
|
||||
},
|
||||
labels,
|
||||
)
|
||||
|
||||
routesAccepted := prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "routes_accepted",
|
||||
Help: "Total number of routes accepted by a route server for a given neighbor",
|
||||
},
|
||||
labels,
|
||||
)
|
||||
|
||||
prometheus.MustRegister(neighborInfo)
|
||||
prometheus.MustRegister(neighborUptime)
|
||||
prometheus.MustRegister(routesReceived)
|
||||
prometheus.MustRegister(routesFiltered)
|
||||
prometheus.MustRegister(routesPreferred)
|
||||
prometheus.MustRegister(routesAccepted)
|
||||
|
||||
return &metrics{
|
||||
neighborsStore: s,
|
||||
|
||||
neighborInfo: neighborInfo,
|
||||
neighborUptime: neighborUptime,
|
||||
|
||||
routesReceived: routesReceived,
|
||||
routesFiltered: routesFiltered,
|
||||
routesPreferred: routesPreferred,
|
||||
routesAccepted: routesAccepted,
|
||||
}
|
||||
}
|
||||
|
||||
// Update the metrics with information from the store.
|
||||
func (m *metrics) update(ctx context.Context) error {
|
||||
rsIDs := m.neighborsStore.sources.GetSourceIDs()
|
||||
|
||||
// For all route servers, fetch neighbors list and
|
||||
// update statistics.
|
||||
for _, rsID := range rsIDs {
|
||||
if !m.neighborsStore.IsInitialized(rsID) {
|
||||
continue // No data from RS yet
|
||||
}
|
||||
rs := m.neighborsStore.sources.Get(rsID)
|
||||
|
||||
neighbors, err := m.neighborsStore.GetNeighborsAt(ctx, rsID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get neighbors
|
||||
for _, neighbor := range neighbors {
|
||||
|
||||
m.neighborInfo.With(prometheus.Labels{
|
||||
"route_server_id": rs.ID,
|
||||
"route_server_name": rs.Name,
|
||||
"route_server_group": rs.Group,
|
||||
"neighbor_id": neighbor.ID,
|
||||
"neighbor_description": neighbor.Description,
|
||||
"neighbor_asn": strconv.Itoa(neighbor.ASN),
|
||||
"neighbor_address": neighbor.Address,
|
||||
"neighbor_state": neighbor.State,
|
||||
}).Set(1.0)
|
||||
|
||||
labels := prometheus.Labels{
|
||||
"route_server_id": rs.ID,
|
||||
"route_server_name": rs.Name,
|
||||
"route_server_group": rs.Group,
|
||||
"neighbor_id": neighbor.ID,
|
||||
"neighbor_description": neighbor.Description,
|
||||
"neighbor_asn": strconv.Itoa(neighbor.ASN),
|
||||
"neighbor_address": neighbor.Address,
|
||||
}
|
||||
m.neighborUptime.
|
||||
With(labels).
|
||||
Set(neighbor.Uptime.Seconds())
|
||||
m.routesReceived.
|
||||
With(labels).
|
||||
Set(float64(neighbor.RoutesReceived))
|
||||
m.routesFiltered.
|
||||
With(labels).
|
||||
Set(float64(neighbor.RoutesFiltered))
|
||||
m.routesPreferred.
|
||||
With(labels).
|
||||
Set(float64(neighbor.RoutesPreferred))
|
||||
m.routesAccepted.
|
||||
With(labels).
|
||||
Set(float64(neighbor.RoutesAccepted))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Startmetrics registers the metrics and starts a
|
||||
// periodical refresh.
|
||||
func StartMetrics(
|
||||
ctx context.Context,
|
||||
neighborsStore *NeighborsStore,
|
||||
) {
|
||||
m := initMetrics(neighborsStore)
|
||||
|
||||
// Every 5 second, update the metrics
|
||||
log.Println("[metrics] Starting refresh.")
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(time.Second * 5):
|
||||
err := m.update(ctx)
|
||||
if err != nil {
|
||||
log.Println(
|
||||
"[metrics] Error while updating:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -108,7 +108,7 @@ func (s *NeighborsStore) Start(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetStatus retrievs the status for a route server
|
||||
// GetStatus retrieves the status for a route server
|
||||
// identified by sourceID.
|
||||
func (s *NeighborsStore) GetStatus(sourceID string) (*Status, error) {
|
||||
return s.sources.GetStatus(sourceID)
|
||||
@ -150,7 +150,7 @@ func (s *NeighborsStore) safeUpdateSource(ctx context.Context, id string) {
|
||||
}
|
||||
|
||||
if err := s.sources.LockSource(id); err != nil {
|
||||
log.Println("Cloud not start neighbor refresh:", err)
|
||||
log.Println("[neighbors store] could not start neighbor refresh:", err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -165,15 +165,15 @@ func (s *NeighborsStore) safeUpdateSource(ctx context.Context, id string) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Println(
|
||||
"Recovering after failed neighbors refresh of",
|
||||
srcName, "from:", err)
|
||||
"[neighbors store] recovering after failed neighbors refresh from",
|
||||
srcName, "with error:", err)
|
||||
s.sources.RefreshError(id, err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := s.updateSource(ctx, src, id); err != nil {
|
||||
log.Println(
|
||||
"Refeshing neighbors of", srcName, "failed:", err)
|
||||
"[neighbors store] refreshing neighbors from", srcName, "failed:", err)
|
||||
s.sources.RefreshError(id, err)
|
||||
}
|
||||
|
||||
@ -181,7 +181,7 @@ func (s *NeighborsStore) safeUpdateSource(ctx context.Context, id string) {
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Println("Refreshed neighbors of", srcName, "in", status.LastRefreshDuration)
|
||||
log.Println("[neighbors store] refreshed neighbors from", srcName, "in", status.LastRefreshDuration)
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,7 +332,7 @@ func (s *NeighborsStore) Stats(
|
||||
ncount, err := s.backend.CountNeighborsAt(ctx, sourceID)
|
||||
if err != nil {
|
||||
if !errors.Is(err, sources.ErrSourceNotFound) {
|
||||
log.Println("error during neighbor count:", err)
|
||||
log.Println("[neighbors store] error during neighbor count:", err)
|
||||
}
|
||||
}
|
||||
totalNeighbors += ncount
|
||||
@ -381,7 +381,7 @@ func (s *NeighborsStore) Status(ctx context.Context) *api.StoreStatus {
|
||||
func (s *NeighborsStore) SourceCachedAt(sourceID string) time.Time {
|
||||
status, err := s.sources.GetStatus(sourceID)
|
||||
if err != nil {
|
||||
log.Println("error while getting source cached at:", err)
|
||||
log.Println("[neighbors store] error while getting source cached at:", err)
|
||||
return time.Time{}
|
||||
}
|
||||
return status.LastRefresh
|
||||
|
@ -142,7 +142,7 @@ func (s *RoutesStore) safeUpdateSource(ctx context.Context, id string) {
|
||||
}
|
||||
|
||||
if err := s.sources.LockSource(id); err != nil {
|
||||
log.Println("Cloud not start routes refresh:", err)
|
||||
log.Println("[routes store] could not start routes refresh:", err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -153,28 +153,28 @@ func (s *RoutesStore) safeUpdateSource(ctx context.Context, id string) {
|
||||
src := s.sources.Get(id)
|
||||
srcName := s.sources.GetName(id)
|
||||
|
||||
log.Println("[routes store] begin routes refresh of:", srcName)
|
||||
log.Println("[routes store] begin routes refresh from", srcName)
|
||||
|
||||
// Prepare for impact.
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Println(
|
||||
"Recovering after failed routes refresh of",
|
||||
src.Name, "from:", err)
|
||||
"[routes store] recovering after failed routes refresh from",
|
||||
src.Name, "with error:", err)
|
||||
s.sources.RefreshError(id, err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := s.updateSource(ctx, src); err != nil {
|
||||
log.Println(
|
||||
"Refeshing routes of", src.Name, "failed:", err)
|
||||
"[routes store] refreshing routes from", src.Name, "failed:", err)
|
||||
s.sources.RefreshError(id, err)
|
||||
} else {
|
||||
status, err := s.sources.GetStatus(id)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Println("Refreshed routes of", srcName, "in", status.LastRefreshDuration)
|
||||
log.Println("[routes store] refreshed routes from", srcName, "in", status.LastRefreshDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -194,7 +194,7 @@ func (s *RoutesStore) updateSource(
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("[routes store] finished fetching routes dump from RS", src.Name)
|
||||
log.Println("[routes store] finished fetching routes dump from", src.Name)
|
||||
|
||||
neighbors, err := s.neighbors.GetNeighborsMapAt(ctx, src.ID)
|
||||
if err != nil {
|
||||
@ -203,7 +203,7 @@ func (s *RoutesStore) updateSource(
|
||||
|
||||
log.Println(
|
||||
"[routes store] retrieved", len(res.Imported),
|
||||
"accepted and", len(res.Filtered), "filtered routes for:", src.Name)
|
||||
"accepted and", len(res.Filtered), "filtered routes from", src.Name)
|
||||
|
||||
// Prepare imported routes for lookup
|
||||
srcRS := &api.LookupRouteServer{
|
||||
@ -214,11 +214,11 @@ func (s *RoutesStore) updateSource(
|
||||
filtered := res.Filtered.ToLookupRoutes("filtered", srcRS, neighbors)
|
||||
lookupRoutes := append(imported, filtered...)
|
||||
|
||||
log.Println("[routes store] importing", len(lookupRoutes), "into store from", src.Name)
|
||||
log.Println("[routes store] importing", len(lookupRoutes), "routes into store from", src.Name)
|
||||
if err = s.backend.SetRoutes(ctx, src.ID, lookupRoutes); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("[routes store] import success")
|
||||
log.Println("[routes store] successfully imported", len(lookupRoutes), "routes into store from", src.Name)
|
||||
|
||||
return s.sources.RefreshSuccess(src.ID)
|
||||
}
|
||||
@ -277,7 +277,7 @@ func (s *RoutesStore) Stats(ctx context.Context) *api.RoutesStoreStats {
|
||||
for _, sourceID := range s.sources.GetSourceIDs() {
|
||||
status, err := s.sources.GetStatus(sourceID)
|
||||
if err != nil {
|
||||
log.Println("error while getting source status:", err)
|
||||
log.Println("[routes store] error while getting source status:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -285,7 +285,7 @@ func (s *RoutesStore) Stats(ctx context.Context) *api.RoutesStoreStats {
|
||||
nImported, nFiltered, err := s.backend.CountRoutesAt(ctx, sourceID)
|
||||
if err != nil {
|
||||
if !errors.Is(err, sources.ErrSourceNotFound) {
|
||||
log.Println("error during routes count:", err)
|
||||
log.Println("[routes store] error during routes count:", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,7 @@ func (s *SourcesStore) ShouldRefresh(
|
||||
}
|
||||
if status.State == StateError {
|
||||
// The refresh interval in the config is ok if the
|
||||
// success case. When an error occures it is desirable
|
||||
// success case. When an error occurs it is desirable
|
||||
// to retry sooner, without spamming the server.
|
||||
nextRefresh = status.LastRefresh.Add(10 * time.Second)
|
||||
}
|
||||
@ -202,7 +202,7 @@ func (s *SourcesStore) ShouldRefresh(
|
||||
return true // Go for it
|
||||
}
|
||||
|
||||
// CachedAt retrievs the oldest refresh time
|
||||
// CachedAt retrieves the oldest refresh time
|
||||
// from all sources. All data is then guaranteed to be older
|
||||
// than the CachedAt date.
|
||||
func (s *SourcesStore) CachedAt(ctx context.Context) time.Time {
|
||||
|
@ -14,11 +14,11 @@ func ContainsCi(s, substr string) bool {
|
||||
)
|
||||
}
|
||||
|
||||
// SerializeReasons asserts the bgp communitiy parts are
|
||||
// SerializeReasons asserts the bgp community parts are
|
||||
// actually strings, because there are no such things as
|
||||
// integers as keys in json.
|
||||
// Serialization of this is undefined behaviour, so we
|
||||
// keep these interallybut provide a string as a key for
|
||||
// keep these internally but provide a string as a key for
|
||||
// serialization
|
||||
func SerializeReasons(reasons map[int]string) map[string]string {
|
||||
res := make(map[string]string)
|
||||
|
@ -76,7 +76,7 @@ const Routing = () => (
|
||||
|
||||
/**
|
||||
* The application main entry point.
|
||||
* Instanciate global providers and the router.
|
||||
* Instantiate global providers and the router.
|
||||
*/
|
||||
const Main = () => {
|
||||
return (
|
||||
|
@ -2,7 +2,7 @@
|
||||
import moment from 'moment'
|
||||
|
||||
/**
|
||||
* Render the formated 'absolute' time when given a
|
||||
* Render the formatted 'absolute' time when given a
|
||||
* relative timestamp (in nanoseconds).
|
||||
*
|
||||
* The timestamp is the duration from now to the absolute
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
/**
|
||||
* Errors Component renders a stack of dismissable errors.
|
||||
* Errors Component renders a stack of dismissible errors.
|
||||
*/
|
||||
import { FontAwesomeIcon }
|
||||
from '@fortawesome/react-fontawesome';
|
||||
@ -25,7 +25,7 @@ const infoFromError = (error) => {
|
||||
|
||||
|
||||
/**
|
||||
* Error renders a single dismissable error
|
||||
* Error renders a single dismissible error
|
||||
*/
|
||||
const Error = ({error, onDismiss}) => {
|
||||
const routeServers = useRouteServers();
|
||||
|
@ -116,7 +116,7 @@ const GroupSelect = ({groups, selected, onSelect}) => {
|
||||
<GroupSelectOption key={group} group={group} onSelect={selectGroup} />
|
||||
);
|
||||
|
||||
// Partition options into n coulumns with a maximum
|
||||
// Partition options into n columns with a maximum
|
||||
// of 10 rows per column.
|
||||
const maxRows = 10;
|
||||
const n = Math.ceil(options.length / maxRows);
|
||||
|
@ -19,11 +19,17 @@ const Sidebar = () => {
|
||||
<div className="sidebar-header">
|
||||
<div className="logo">
|
||||
<Link to='/'>
|
||||
<i>{/* Theme compatbility */}
|
||||
<FontAwesomeIcon
|
||||
className="logo-icon"
|
||||
icon={faCloud} size="lg" transform="grow-11" />
|
||||
</i>
|
||||
<span>
|
||||
<Content id="header.logo">
|
||||
<i>
|
||||
<FontAwesomeIcon
|
||||
className="logo-icon"
|
||||
icon={faCloud}
|
||||
size="lg"
|
||||
transform="grow-11" />
|
||||
</i>
|
||||
</Content>
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="title">
|
||||
|
@ -87,9 +87,9 @@ const Neighbors = () => {
|
||||
let s = n.state.toLowerCase();
|
||||
if (s.includes("up") || s.includes("established") ) {
|
||||
up.push(n);
|
||||
} else if (s.includes("down")) {
|
||||
} else if (s.includes("down") || s.includes("start") || s.includes("active") || s.includes("connect")) {
|
||||
down.push(n);
|
||||
} else if (s.includes("start") || s.includes("active")) {
|
||||
} else if (s.includes("idle")) {
|
||||
idle.push(n);
|
||||
} else {
|
||||
console.error("Couldn't classify neighbor by state:", n);
|
||||
|
@ -123,14 +123,14 @@ const Section = ({state}) => {
|
||||
sectionTitle = 'BGP Sessions Established';
|
||||
sectionCls += 'established ';
|
||||
break;
|
||||
case 'idle':
|
||||
sectionTitle = 'Idle BGP Sessions';
|
||||
sectionCls += 'idle ';
|
||||
break;
|
||||
case 'down':
|
||||
sectionTitle = 'BGP Sessions Down';
|
||||
sectionCls += 'down ';
|
||||
break;
|
||||
case 'start':
|
||||
sectionTitle = 'BGP Sessions Start';
|
||||
sectionCls += '';
|
||||
break;
|
||||
default:
|
||||
}
|
||||
return (<p className={sectionCls}>{sectionTitle}</p>);
|
||||
@ -262,7 +262,7 @@ const NeighborColumn = ({neighbor, column}) => {
|
||||
"Description": ColDescription,
|
||||
};
|
||||
|
||||
// For openbgpd the value is ommitted
|
||||
// For openbgpd the value is omitted
|
||||
if (rs.type === "openbgpd") {
|
||||
widgets["routes_not_exported"] = ColNotAvailable;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ const QuickLinks = () => {
|
||||
const filtered = useRoutesFiltered();
|
||||
const notExported = useRoutesNotExported();
|
||||
|
||||
const locRecevied = useMemo(() => ({...location, hash: "routes-received"}), [
|
||||
const locReceived = useMemo(() => ({...location, hash: "routes-received"}), [
|
||||
location,
|
||||
]);
|
||||
const locFiltered = useMemo(() => ({...location, hash: "routes-filtered"}), [
|
||||
@ -57,7 +57,7 @@ const QuickLinks = () => {
|
||||
<Link to={locFiltered}>Filtered</Link></li>}
|
||||
{ showReceived &&
|
||||
<li className="received">
|
||||
<Link to={locRecevied}>Accepted</Link></li>}
|
||||
<Link to={locReceived}>Accepted</Link></li>}
|
||||
{ showNotExported &&
|
||||
<li className="not-exported">
|
||||
<Link to={locNotExported}>Not Exported</Link></li>}
|
||||
|
@ -10,7 +10,7 @@ import { Modal
|
||||
, ModalFooter
|
||||
}
|
||||
from 'app/components/modal/Modal';
|
||||
import BgpCommunitiyLabel
|
||||
import BgpCommunityLabel
|
||||
from 'app/components/routes/BgpCommunityLabel';
|
||||
import { RouteAgeDetails }
|
||||
from 'app/components/routes/Age';
|
||||
@ -78,21 +78,21 @@ const RouteDetailsModal = () => {
|
||||
<tr>
|
||||
<th>Communities:</th>
|
||||
<td>
|
||||
{communities.map((c) => <BgpCommunitiyLabel community={c} key={communityKey(c)} />)}
|
||||
{communities.map((c) => <BgpCommunityLabel community={c} key={communityKey(c)} />)}
|
||||
</td>
|
||||
</tr>}
|
||||
{extCommunities.length > 0 &&
|
||||
<tr>
|
||||
<th>Ext. Communities:</th>
|
||||
<td>
|
||||
{extCommunities.map((c) => <BgpCommunitiyLabel community={c} key={communityKey(c)} />)}
|
||||
{extCommunities.map((c) => <BgpCommunityLabel community={c} key={communityKey(c)} />)}
|
||||
</td>
|
||||
</tr>}
|
||||
{largeCommunities.length > 0 &&
|
||||
<tr>
|
||||
<th>Large Communities:</th>
|
||||
<td>
|
||||
{largeCommunities.map((c) => <BgpCommunitiyLabel community={c} key={communityKey(c)} />)}
|
||||
{largeCommunities.map((c) => <BgpCommunityLabel community={c} key={communityKey(c)} />)}
|
||||
</td>
|
||||
</tr>}
|
||||
</tbody>
|
||||
|
@ -117,7 +117,7 @@ const RoutesNotExported = createRoutesSet(
|
||||
|
||||
/**
|
||||
* Show a button to load routes not exported on demand.
|
||||
* IF config states loading routes shoud be done automatically
|
||||
* IF config states loading routes should be done automatically
|
||||
* update the query parameter.
|
||||
*/
|
||||
const RoutesNotExportedRequest = () => {
|
||||
|
@ -63,7 +63,7 @@ const RpkiIndicator = ({route}) => {
|
||||
}
|
||||
|
||||
// RPKI INVALID
|
||||
// Depending on the configration this can either be a
|
||||
// Depending on the configuration this can either be a
|
||||
// single flag or a range with a given reason.
|
||||
let rpkiInvalidReason = 0;
|
||||
for (const invalid of rpkiInvalid) {
|
||||
|
@ -19,9 +19,9 @@ export const useErrorHandler = () => {
|
||||
};
|
||||
|
||||
|
||||
// Unfortunatley this does not really act as an error
|
||||
// Unfortunately this does not really act as an error
|
||||
// boundary. But we need to catch http errors from axios.
|
||||
// Those are not cought using the ErrorBoundary approach.
|
||||
// Those are not caught using the ErrorBoundary approach.
|
||||
export const ErrorsProvider = ({children}) => {
|
||||
const [errors, setErrors] = useState([]);
|
||||
|
||||
|
@ -11,8 +11,8 @@ import { useQuery
|
||||
|
||||
|
||||
/**
|
||||
* usePageQuery retrievs the pagination
|
||||
* query paramters and decodes the value
|
||||
* usePageQuery retrieves the pagination
|
||||
* query parameters and decodes the value
|
||||
*/
|
||||
export const usePageQuery = () => {
|
||||
const [query] = useQuery({
|
||||
|
@ -207,7 +207,7 @@ const createRoutesProvider = (Context, useFetchRoutesState) => ({
|
||||
|
||||
|
||||
/**
|
||||
* RoutesReceivedProvider loads all routes recieved for a neighbor
|
||||
* RoutesReceivedProvider loads all routes received for a neighbor
|
||||
*/
|
||||
export const RoutesReceivedProvider = createRoutesProvider(
|
||||
RoutesReceivedContext,
|
||||
|
@ -61,7 +61,7 @@ const initialState = {
|
||||
}
|
||||
|
||||
/**
|
||||
* useSearchParam retrievs the query parameter
|
||||
* useSearchParam retrieves the query parameter
|
||||
*/
|
||||
export const useSearchQuery = () => {
|
||||
const [query] = useQuery({
|
||||
@ -125,7 +125,7 @@ const decodeSearchResult = (result) => {
|
||||
|
||||
|
||||
/**
|
||||
* useSearchResult retrievs the url and returns the state
|
||||
* useSearchResult retrieves the url and returns the state
|
||||
*/
|
||||
const useSearchResults = ({
|
||||
query,
|
||||
@ -207,7 +207,7 @@ export const SearchStatusProvider = ({children, api}) => {
|
||||
|
||||
/**
|
||||
* RoutesSearchProvider provides routes received, filtered
|
||||
* and not exportet.
|
||||
* and not exported.
|
||||
*/
|
||||
export const RoutesSearchProvider = ({
|
||||
children,
|
||||
|
@ -52,5 +52,9 @@
|
||||
&.down {
|
||||
color: red;
|
||||
}
|
||||
|
||||
&.idle {
|
||||
color: orange;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user