|
|
@@ -202,6 +202,9 @@ func (m *Manager) applyBirdConfig() error {
|
|
|
|
|
|
sb.WriteString("# /etc/bird/bird.conf\n")
|
|
|
sb.WriteString("# Generated dynamically by Proxmox Router (BIRD 2 Engine)\n\n")
|
|
|
+ sb.WriteString("timeformat protocol iso long;\n")
|
|
|
+ sb.WriteString("timeformat base iso long;\n")
|
|
|
+ sb.WriteString("timeformat log iso long;\n")
|
|
|
sb.WriteString("log syslog all;\n")
|
|
|
sb.WriteString(fmt.Sprintf("router id %s;\n\n", routerID))
|
|
|
|
|
|
@@ -818,13 +821,23 @@ func (m *Manager) ListBGPPeers() []BGPPeer {
|
|
|
lines := strings.Split(string(out), "\n")
|
|
|
|
|
|
var cur *livePeerData
|
|
|
+ now := time.Now()
|
|
|
for _, line := range lines {
|
|
|
trimmed := strings.TrimSpace(line)
|
|
|
- if strings.Contains(line, " BGP ") {
|
|
|
- cur = &livePeerData{}
|
|
|
+ // A protocol header line in BIRD is unindented and has "BGP" in column 2
|
|
|
+ if !strings.HasPrefix(line, " ") && !strings.HasPrefix(line, "\t") {
|
|
|
fields := strings.Fields(line)
|
|
|
- if len(fields) >= 5 {
|
|
|
- cur.Uptime = fields[4]
|
|
|
+ if len(fields) >= 2 && fields[1] == "BGP" {
|
|
|
+ cur = &livePeerData{}
|
|
|
+ var sinceStr string
|
|
|
+ if len(fields) >= 6 && strings.Contains(fields[4], "-") && strings.Contains(fields[5], ":") {
|
|
|
+ sinceStr = fields[4] + " " + fields[5]
|
|
|
+ } else if len(fields) >= 5 {
|
|
|
+ sinceStr = fields[4]
|
|
|
+ }
|
|
|
+ cur.Uptime = parseBirdSinceToUptime(sinceStr, now)
|
|
|
+ } else {
|
|
|
+ cur = nil
|
|
|
}
|
|
|
}
|
|
|
if cur == nil {
|
|
|
@@ -863,7 +876,11 @@ func (m *Manager) ListBGPPeers() []BGPPeer {
|
|
|
for i := range peers {
|
|
|
if live, ok := liveMap[peers[i].RemoteAddress]; ok {
|
|
|
peers[i].State = live.State
|
|
|
- peers[i].Uptime = live.Uptime
|
|
|
+ if live.State == "Established" {
|
|
|
+ peers[i].Uptime = live.Uptime
|
|
|
+ } else {
|
|
|
+ peers[i].Uptime = ""
|
|
|
+ }
|
|
|
peers[i].PrefixReceived = live.PfxRcd
|
|
|
peers[i].PrefixSent = live.PfxSnt
|
|
|
if peers[i].LocalAS == 0 && live.LocalAS > 0 {
|
|
|
@@ -871,8 +888,10 @@ func (m *Manager) ListBGPPeers() []BGPPeer {
|
|
|
}
|
|
|
} else if peers[i].Disabled {
|
|
|
peers[i].State = "Disabled"
|
|
|
+ peers[i].Uptime = ""
|
|
|
} else {
|
|
|
peers[i].State = "Idle"
|
|
|
+ peers[i].Uptime = ""
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -880,6 +899,53 @@ func (m *Manager) ListBGPPeers() []BGPPeer {
|
|
|
return peers
|
|
|
}
|
|
|
|
|
|
+func parseBirdSinceToUptime(sinceStr string, now time.Time) string {
|
|
|
+ sinceStr = strings.TrimSpace(sinceStr)
|
|
|
+ if sinceStr == "" {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ // Case 1: "2006-01-02 15:04:05" (ISO long)
|
|
|
+ if t, err := time.ParseInLocation("2006-01-02 15:04:05", sinceStr, time.Local); err == nil {
|
|
|
+ return formatDuration(now.Sub(t))
|
|
|
+ }
|
|
|
+ // Case 2: "2006-01-02 15:04:05.999" (ISO long with milliseconds)
|
|
|
+ if t, err := time.ParseInLocation("2006-01-02 15:04:05.999", sinceStr, time.Local); err == nil {
|
|
|
+ return formatDuration(now.Sub(t))
|
|
|
+ }
|
|
|
+ // Case 3: "2006-01-02" (date only)
|
|
|
+ if t, err := time.ParseInLocation("2006-01-02", sinceStr, time.Local); err == nil {
|
|
|
+ return formatDuration(now.Sub(t))
|
|
|
+ }
|
|
|
+ // Case 4: "15:04:05" or "15:04:05.123" (time only, happened today)
|
|
|
+ timeOnly := sinceStr
|
|
|
+ if idx := strings.IndexByte(timeOnly, '.'); idx != -1 {
|
|
|
+ timeOnly = timeOnly[:idx]
|
|
|
+ }
|
|
|
+ if t, err := time.ParseInLocation("15:04:05", timeOnly, time.Local); err == nil {
|
|
|
+ tToday := time.Date(now.Year(), now.Month(), now.Day(), t.Hour(), t.Minute(), t.Second(), 0, time.Local)
|
|
|
+ if tToday.After(now) {
|
|
|
+ tToday = tToday.AddDate(0, 0, -1)
|
|
|
+ }
|
|
|
+ return formatDuration(now.Sub(tToday))
|
|
|
+ }
|
|
|
+ return ""
|
|
|
+}
|
|
|
+
|
|
|
+func formatDuration(d time.Duration) string {
|
|
|
+ if d < 0 {
|
|
|
+ d = 0
|
|
|
+ }
|
|
|
+ sec := int64(d.Seconds())
|
|
|
+ days := sec / 86400
|
|
|
+ hours := (sec % 86400) / 3600
|
|
|
+ mins := (sec % 3600) / 60
|
|
|
+ secs := sec % 60
|
|
|
+ if days > 0 {
|
|
|
+ return fmt.Sprintf("%dd %02dh %02dm %02ds", days, hours, mins, secs)
|
|
|
+ }
|
|
|
+ return fmt.Sprintf("%02dh %02dm %02ds", hours, mins, secs)
|
|
|
+}
|
|
|
+
|
|
|
func (m *Manager) SaveBGPPeer(peer *BGPPeer) error {
|
|
|
m.mu.Lock()
|
|
|
defer m.mu.Unlock()
|