package birdfmt import ( "context" "os" "strings" ) // LocalBirdStatus is returned by GET /v1/bird/status (same host as birdc when socket is configured). type LocalBirdStatus struct { BirdcConfigured bool `json:"birdc_configured"` Message string `json:"message,omitempty"` Error string `json:"error,omitempty"` ProtocolsExcerpt string `json:"protocols_excerpt,omitempty"` BGPSessionsTotal int `json:"bgp_sessions_total"` BGPEstablished int `json:"bgp_established"` // Healthy: null if birdc not configured; false if birdc failed or BGP sessions exist but none Established; true otherwise. Healthy *bool `json:"healthy"` } // InspectLocalBird runs `birdc show protocols all` using EVOBGP_BIRDC_SOCKET / EVOBGP_BIRDC_BIN. func InspectLocalBird(ctx context.Context) LocalBirdStatus { sock := strings.TrimSpace(os.Getenv("EVOBGP_BIRDC_SOCKET")) if sock == "" { return LocalBirdStatus{ BirdcConfigured: false, Message: "EVOBGP_BIRDC_SOCKET не задан на этом процессе — статус BIRD недоступен (типично, если birdc только на ноде со спикером).", Healthy: nil, } } bin := strings.TrimSpace(os.Getenv("EVOBGP_BIRDC_BIN")) out, err := ShowProtocols(ctx, sock, bin) if err != nil { f := false return LocalBirdStatus{ BirdcConfigured: true, Error: err.Error(), Healthy: &f, } } sum := SummarizeProtocolsOutput(out) excerpt := out const maxExcerpt = 20000 if len(excerpt) > maxExcerpt { excerpt = excerpt[:maxExcerpt] + "\n# … truncated …\n" } ok := sum.BGPSessionsTotal == 0 || sum.BGPEstablished > 0 return LocalBirdStatus{ BirdcConfigured: true, ProtocolsExcerpt: excerpt, BGPSessionsTotal: sum.BGPSessionsTotal, BGPEstablished: sum.BGPEstablished, Healthy: &ok, } }