feat(api): enhance peer session management and documentation
CI / changes (push) Successful in 8s
CI / commitlint (push) Has been skipped
CI / openapi (push) Successful in 27s
CI / web (push) Failing after 35s
CI / go (push) Successful in 55s
CI / bird2 (push) Successful in 15s
CI / release (push) Has been skipped

- Added new fields to the API for tracking connected speakers and session states across multiple nodes, including `connected_speaker_id`, `connected_speaker_label`, `session_on_speakers`, `established_on_speakers`, and `session_mismatch`.
- Implemented a new endpoint for retrieving bird protocol sessions, enhancing the agent server functionality.
- Updated the OpenAPI documentation to reflect the new fields and query parameters, improving clarity for API consumers.
- Modified the frontend to display connected speaker information and session states, providing better visibility into peer connections.
- Updated deployment documentation to clarify the configuration requirements for enabling IP forwarding on VPS.
This commit is contained in:
Denozordec
2026-05-21 15:18:21 +07:00
parent e0a912a693
commit 1c0d78b552
16 changed files with 659 additions and 38 deletions
+48
View File
@@ -0,0 +1,48 @@
package birdfmt
import (
"strings"
)
// BGPSession is one BGP protocol block from `birdc show protocols all`.
type BGPSession struct {
Name string `json:"name"`
Neighbor string `json:"neighbor,omitempty"`
State string `json:"state"`
}
// ParseBGPSessions extracts BGP protocol name, state, and neighbor (if present) from birdc output.
func ParseBGPSessions(output string) []BGPSession {
var out []BGPSession
var cur *BGPSession
for _, raw := range strings.Split(output, "\n") {
line := strings.TrimRight(raw, "\r")
trim := strings.TrimSpace(line)
if trim == "" {
continue
}
low := strings.ToLower(trim)
if strings.HasPrefix(low, "bird ") || strings.HasPrefix(low, "name ") || strings.HasPrefix(low, "table ") {
continue
}
if !strings.HasPrefix(line, " ") && !strings.HasPrefix(line, "\t") && isBGPProtocolSummaryRow(trim) {
fields := strings.Fields(trim)
state := extractBGPSessionStateLine(trim)
if state == "" && len(fields) >= 4 {
state = fields[3]
}
cur = &BGPSession{Name: fields[0], State: state}
out = append(out, *cur)
cur = &out[len(out)-1]
continue
}
if cur == nil {
continue
}
const neighborPrefix = "Neighbor address:"
if idx := strings.Index(trim, neighborPrefix); idx >= 0 {
cur.Neighbor = strings.TrimSpace(trim[idx+len(neighborPrefix):])
}
}
return out
}
+26
View File
@@ -0,0 +1,26 @@
package birdfmt
import "testing"
func TestParseBGPSessions_neighborAndState(t *testing.T) {
sample := `
BIRD 2.14 ready.
Name Proto Table State Since Info
device1 Device --- up 10:00:00
evobgp_p_abc123 BGP master4 up 10:00:05 Established
Neighbor address: 198.51.100.2
Neighbor AS: 65001
evobgp_p_def456 BGP master4 up 10:00:06 Active
Neighbor address: 2001:db8::2
`
sessions := ParseBGPSessions(sample)
if len(sessions) != 2 {
t.Fatalf("got %d sessions want 2", len(sessions))
}
if sessions[0].Name != "evobgp_p_abc123" || sessions[0].State != "Established" || sessions[0].Neighbor != "198.51.100.2" {
t.Fatalf("session0: %+v", sessions[0])
}
if sessions[1].Neighbor != "2001:db8::2" || sessions[1].State != "Active" {
t.Fatalf("session1: %+v", sessions[1])
}
}
+16
View File
@@ -0,0 +1,16 @@
package birdfmt
import "strings"
// PeerProtocolName returns the BIRD protocol name for a control-plane peer UUID.
// Must stay in sync with pipeline peer rendering.
func PeerProtocolName(peerID string) string {
s := strings.ReplaceAll(strings.TrimSpace(peerID), "-", "")
if len(s) > 16 {
s = s[:16]
}
if s == "" {
s = "x"
}
return "evobgp_p_" + s
}