feat: add live BGP protocol state tracking to peer listing
CI / changes (push) Successful in 6s
CI / openapi (push) Has been skipped
CI / go (push) Successful in 39s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Has been skipped
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Has been skipped
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 13s
CI / docker-go-prime (push) Successful in 24s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 59s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 6m19s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m28s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m22s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m25s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m10s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m23s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m22s
CI / changes (push) Successful in 6s
CI / openapi (push) Has been skipped
CI / go (push) Successful in 39s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Has been skipped
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Has been skipped
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 13s
CI / docker-go-prime (push) Successful in 24s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 59s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 6m19s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m28s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m22s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m25s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m10s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m23s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m22s
Enhanced the peer listing API to include live BGP session states by integrating a new function to retrieve protocol states from the BIRD socket. Added parsing logic for BGP protocol states and updated the peer JSON response to include session state information. Introduced unit tests for the new parsing functionality to ensure accuracy in state retrieval.
This commit is contained in:
@@ -319,15 +319,89 @@ func (s *Server) handleListPeers(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
peers := s.store.ListPeers(a.TenantID)
|
peers := s.store.ListPeers(a.TenantID)
|
||||||
|
liveStates := s.liveBGPProtocolStates(r.Context())
|
||||||
items := make([]map[string]any, 0, len(peers))
|
items := make([]map[string]any, 0, len(peers))
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
items = append(items, peerJSON(p))
|
row := peerJSON(p)
|
||||||
|
if st, ok := liveStates[peerProtocolNameForID(p.ID)]; ok && strings.TrimSpace(st) != "" {
|
||||||
|
row["session_state"] = strings.TrimSpace(st)
|
||||||
|
}
|
||||||
|
items = append(items, row)
|
||||||
}
|
}
|
||||||
writeJSON(w, http.StatusOK, map[string]any{
|
writeJSON(w, http.StatusOK, map[string]any{
|
||||||
"items": items, "next_cursor": nil, "has_more": false,
|
"items": items, "next_cursor": nil, "has_more": false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) liveBGPProtocolStates(ctx context.Context) map[string]string {
|
||||||
|
sock := strings.TrimSpace(os.Getenv("EVOBGP_BIRDC_SOCKET"))
|
||||||
|
if sock == "" {
|
||||||
|
return map[string]string{}
|
||||||
|
}
|
||||||
|
out, err := birdfmt.ShowProtocols(ctx, sock, strings.TrimSpace(os.Getenv("EVOBGP_BIRDC_BIN")))
|
||||||
|
if err != nil {
|
||||||
|
return map[string]string{}
|
||||||
|
}
|
||||||
|
return parseBGPProtocolStates(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseBGPProtocolStates parses `birdc show protocols all` summary rows into protocol_name -> state.
|
||||||
|
func parseBGPProtocolStates(output string) map[string]string {
|
||||||
|
out := make(map[string]string)
|
||||||
|
for _, raw := range strings.Split(output, "\n") {
|
||||||
|
line := strings.TrimSpace(raw)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
low := strings.ToLower(line)
|
||||||
|
if strings.HasPrefix(low, "bird ") || strings.HasPrefix(low, "name ") || strings.HasPrefix(low, "table ") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) < 4 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !strings.EqualFold(fields[1], "BGP") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
state := extractBGPSessionState(line)
|
||||||
|
if state == "" {
|
||||||
|
state = fields[3]
|
||||||
|
}
|
||||||
|
out[fields[0]] = state
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractBGPSessionState(line string) string {
|
||||||
|
known := []string{
|
||||||
|
"Established",
|
||||||
|
"Idle",
|
||||||
|
"Connect",
|
||||||
|
"Active",
|
||||||
|
"OpenSent",
|
||||||
|
"OpenConfirm",
|
||||||
|
}
|
||||||
|
for _, st := range known {
|
||||||
|
if strings.Contains(line, st) {
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// peerProtocolNameForID must stay in sync with pipeline peer protocol naming.
|
||||||
|
func peerProtocolNameForID(peerID string) string {
|
||||||
|
s := strings.ReplaceAll(strings.TrimSpace(peerID), "-", "")
|
||||||
|
if len(s) > 16 {
|
||||||
|
s = s[:16]
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
s = "x"
|
||||||
|
}
|
||||||
|
return "evobgp_p_" + s
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) handleListSpeakers(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) handleListSpeakers(w http.ResponseWriter, r *http.Request) {
|
||||||
a, ok := authFromContext(r.Context())
|
a, ok := authFromContext(r.Context())
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
@@ -341,3 +341,19 @@ func waitJob(t *testing.T, client *http.Client, base, token, jobID string) {
|
|||||||
}
|
}
|
||||||
t.Fatalf("job %s did not complete", jobID)
|
t.Fatalf("job %s did not complete", jobID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseBGPProtocolStates(t *testing.T) {
|
||||||
|
raw := `BIRD 2.16.2 ready.
|
||||||
|
name proto table state since info
|
||||||
|
device1 Device --- up 2026-04-09
|
||||||
|
evobgp_p_abcdef01 BGP master up 2026-04-09 Established
|
||||||
|
evobgp_p_01234567 BGP master start 2026-04-09 Connect
|
||||||
|
`
|
||||||
|
got := parseBGPProtocolStates(raw)
|
||||||
|
if got["evobgp_p_abcdef01"] != "Established" {
|
||||||
|
t.Fatalf("expected protocol state Established, got %q", got["evobgp_p_abcdef01"])
|
||||||
|
}
|
||||||
|
if got["evobgp_p_01234567"] != "Connect" {
|
||||||
|
t.Fatalf("expected protocol state Connect, got %q", got["evobgp_p_01234567"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user