| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package snmp |
| 4 | |
| 5 | import ( |
| 6 | "context" |
| 7 | "sort" |
| 8 | "strconv" |
| 9 | "time" |
| 10 | |
| 11 | "github.com/netdata/netdata/go/plugins/pkg/funcapi" |
| 12 | ) |
| 13 | |
| 14 | const ( |
| 15 | bgpPeersMethodID = "bgp-peers" |
| 16 | bgpPeersParamView = "view" |
| 17 | bgpPeersViewPeers = "peers" |
| 18 | bgpPeersViewFamilies = "peer_families" |
| 19 | bgpPeersViewAll = "all" |
| 20 | bgpPeersDefaultView = bgpPeersViewPeers |
| 21 | ) |
| 22 | |
| 23 | type bgpPeerColumn struct { |
| 24 | funcapi.ColumnMeta |
| 25 | Value func(*bgpPeerEntry) any |
| 26 | } |
| 27 | |
| 28 | type funcBGPPeers struct { |
| 29 | cache *bgpPeerCache |
| 30 | } |
| 31 | |
| 32 | func newFuncBGPPeers(cache *bgpPeerCache) *funcBGPPeers { |
| 33 | return &funcBGPPeers{cache: cache} |
| 34 | } |
| 35 | |
| 36 | func bgpPeerColumnSet(cols []bgpPeerColumn) funcapi.ColumnSet[bgpPeerColumn] { |
| 37 | return funcapi.Columns(cols, func(c bgpPeerColumn) funcapi.ColumnMeta { return c.ColumnMeta }) |
| 38 | } |
| 39 | |
| 40 | var _ funcapi.MethodHandler = (*funcBGPPeers)(nil) |
| 41 | |
| 42 | func bgpPeersMethodConfig() funcapi.MethodConfig { |
| 43 | return funcapi.MethodConfig{ |
| 44 | ID: bgpPeersMethodID, |
| 45 | Name: "BGP Peers", |
| 46 | UpdateEvery: 10, |
| 47 | Help: "Detailed current BGP peer and peer-family state from cached SNMP data", |
| 48 | RequiredParams: []funcapi.ParamConfig{{ |
| 49 | ID: bgpPeersParamView, |
| 50 | Name: "View", |
| 51 | Help: "Choose whether to show peer rows, peer-family rows, or both", |
| 52 | Selection: funcapi.ParamSelect, |
| 53 | Options: []funcapi.ParamOption{ |
| 54 | {ID: bgpPeersViewPeers, Name: "Peers", Default: true}, |
| 55 | {ID: bgpPeersViewFamilies, Name: "Peer Families"}, |
| 56 | {ID: bgpPeersViewAll, Name: "All"}, |
| 57 | }, |
| 58 | }}, |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | func (f *funcBGPPeers) MethodParams(_ context.Context, method string) ([]funcapi.ParamConfig, error) { |
| 63 | if method != bgpPeersMethodID { |
| 64 | return nil, nil |
| 65 | } |
| 66 | return bgpPeersMethodConfig().RequiredParams, nil |
| 67 | } |
| 68 | |
| 69 | func (f *funcBGPPeers) Cleanup(_ context.Context) {} |
| 70 | |
| 71 | func (f *funcBGPPeers) Handle(_ context.Context, method string, params funcapi.ResolvedParams) *funcapi.FunctionResponse { |
| 72 | if method != bgpPeersMethodID { |
| 73 | return funcapi.NotFoundResponse(method) |
| 74 | } |
| 75 | if f.cache == nil { |
| 76 | return funcapi.UnavailableResponse("BGP peer data is not available for this device") |
| 77 | } |
| 78 | |
| 79 | view := params.GetOne(bgpPeersParamView) |
| 80 | if view == "" { |
| 81 | view = bgpPeersDefaultView |
| 82 | } |
| 83 | |
| 84 | snapshot := f.cache.snapshot(time.Now()) |
| 85 | if snapshot.expired || len(snapshot.entries) == 0 { |
| 86 | if snapshot.message == "" { |
| 87 | snapshot.message = "no BGP peer data is available for this device" |
| 88 | } |
| 89 | return funcapi.UnavailableResponse(snapshot.message) |
| 90 | } |
| 91 | |
| 92 | rows := make([][]any, 0, len(snapshot.entries)) |
| 93 | for _, entry := range snapshot.entries { |
| 94 | if !matchesBGPPeerView(entry.scope, view) { |
| 95 | continue |
| 96 | } |
| 97 | rows = append(rows, buildBGPPeerRow(entry)) |
| 98 | } |
| 99 | |
| 100 | sortBGPPeerRows(rows) |
| 101 | |
| 102 | cs := bgpPeerColumnSet(bgpPeerColumns) |
| 103 | help := "Detailed current BGP peer and peer-family state from cached SNMP data" |
| 104 | if snapshot.stale && snapshot.message != "" { |
| 105 | help += ". " + snapshot.message |
| 106 | } |
| 107 | return &funcapi.FunctionResponse{ |
| 108 | Status: 200, |
| 109 | Help: help, |
| 110 | Columns: cs.BuildColumns(), |
| 111 | Data: rows, |
| 112 | DefaultSortColumn: "Neighbor", |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | func matchesBGPPeerView(scope, view string) bool { |
| 117 | switch view { |
| 118 | case bgpPeersViewAll: |
| 119 | return true |
| 120 | case bgpPeersViewFamilies: |
| 121 | return scope == bgpPeersViewFamilies |
| 122 | default: |
| 123 | return scope == bgpPeersViewPeers |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | func buildBGPPeerRow(entry *bgpPeerEntry) []any { |
| 128 | row := make([]any, len(bgpPeerColumns)) |
| 129 | for i, col := range bgpPeerColumns { |
| 130 | row[i] = col.Value(entry) |
| 131 | } |
| 132 | return row |
| 133 | } |
| 134 | |
| 135 | func sortBGPPeerRows(rows [][]any) { |
| 136 | if len(rows) == 0 { |
| 137 | return |
| 138 | } |
| 139 | |
| 140 | colIdx := map[string]int{} |
| 141 | for i, col := range bgpPeerColumns { |
| 142 | colIdx[col.Name] = i |
| 143 | } |
| 144 | |
| 145 | sort.Slice(rows, func(i, j int) bool { |
| 146 | a := rows[i] |
| 147 | b := rows[j] |
| 148 | |
| 149 | for _, name := range []string{"Scope", "Routing Instance", "Neighbor", "Family"} { |
| 150 | av, _ := a[colIdx[name]].(string) |
| 151 | bv, _ := b[colIdx[name]].(string) |
| 152 | if av == bv { |
| 153 | continue |
| 154 | } |
| 155 | return av < bv |
| 156 | } |
| 157 | return false |
| 158 | }) |
| 159 | } |
| 160 | |
| 161 | func bgpScopeLabel(scope string) string { |
| 162 | switch scope { |
| 163 | case bgpPeersViewFamilies: |
| 164 | return "peer family" |
| 165 | default: |
| 166 | return "peer" |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | func bgpFamilyLabel(tags map[string]string) string { |
| 171 | if name := tags["address_family_name"]; name != "" && name != "all all" { |
| 172 | return name |
| 173 | } |
| 174 | af := tags["address_family"] |
| 175 | safi := tags["subsequent_address_family"] |
| 176 | switch { |
| 177 | case af == "" && safi == "": |
| 178 | return "" |
| 179 | case af == "all" && safi == "all": |
| 180 | return "all" |
| 181 | case safi == "": |
| 182 | return af |
| 183 | default: |
| 184 | return af + " " + safi |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | func bgpLastErrorDisplay(entry *bgpPeerEntry) string { |
| 189 | if entry.lastErrorText != "" { |
| 190 | return entry.lastErrorText |
| 191 | } |
| 192 | if entry.lastErrorCode == nil { |
| 193 | return "" |
| 194 | } |
| 195 | if entry.lastErrorSubcode == nil { |
| 196 | return "code " + int64ToString(entry.lastErrorCode) |
| 197 | } |
| 198 | return "code " + int64ToString(entry.lastErrorCode) + " / subcode " + int64ToString(entry.lastErrorSubcode) |
| 199 | } |
| 200 | |
| 201 | func int64ToString(v *int64) string { |
| 202 | if v == nil { |
| 203 | return "" |
| 204 | } |
| 205 | return strconv.FormatInt(*v, 10) |
| 206 | } |
| 207 | |
| 208 | func int64Value(v *int64) any { |
| 209 | if v == nil { |
| 210 | return nil |
| 211 | } |
| 212 | return *v |
| 213 | } |
| 214 | |
| 215 | var bgpPeerColumns = []bgpPeerColumn{ |
| 216 | {ColumnMeta: funcapi.ColumnMeta{Name: "rowId", Tooltip: "Row ID", Type: funcapi.FieldTypeString, Visible: false, Sortable: true, Filter: funcapi.FieldFilterMultiselect, UniqueKey: true, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.key }}, |
| 217 | {ColumnMeta: funcapi.ColumnMeta{Name: "Stale", Tooltip: "Whether this row is from a previous successful collection after a newer BGP collection failure", Type: funcapi.FieldTypeBoolean, Visible: false, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.stale }}, |
| 218 | {ColumnMeta: funcapi.ColumnMeta{Name: "Scope", Tooltip: "Scope", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return bgpScopeLabel(e.scope) }}, |
| 219 | {ColumnMeta: funcapi.ColumnMeta{Name: "Routing Instance", Tooltip: "Routing Instance", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.tags["routing_instance"] }}, |
| 220 | {ColumnMeta: funcapi.ColumnMeta{Name: "Neighbor", Tooltip: "Neighbor", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Sticky: true, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.tags["neighbor"] }}, |
| 221 | {ColumnMeta: funcapi.ColumnMeta{Name: "Local Address", Tooltip: "Local Address", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.tags["local_address"] }}, |
| 222 | {ColumnMeta: funcapi.ColumnMeta{Name: "Remote AS", Tooltip: "Remote AS", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.tags["remote_as"] }}, |
| 223 | {ColumnMeta: funcapi.ColumnMeta{Name: "Peer Description", Tooltip: "Peer Description", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, FullWidth: true, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.tags["peer_description"] }}, |
| 224 | {ColumnMeta: funcapi.ColumnMeta{Name: "Family", Tooltip: "Family", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return bgpFamilyLabel(e.tags) }}, |
| 225 | {ColumnMeta: funcapi.ColumnMeta{Name: "Admin Status", Tooltip: "Admin Status", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Visualization: funcapi.FieldVisualPill, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.adminStatus }}, |
| 226 | {ColumnMeta: funcapi.ColumnMeta{Name: "Connection State", Tooltip: "Connection State", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Visualization: funcapi.FieldVisualPill, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return humanizeBGPLabel(e.state) }}, |
| 227 | {ColumnMeta: funcapi.ColumnMeta{Name: "Previous State", Tooltip: "Previous State", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Visualization: funcapi.FieldVisualPill, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return humanizeBGPLabel(e.previousState) }}, |
| 228 | {ColumnMeta: funcapi.ColumnMeta{Name: "Established Uptime", Tooltip: "Established Uptime", Type: funcapi.FieldTypeInteger, Units: "seconds", Visible: true, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(e.establishedUptime) }}, |
| 229 | {ColumnMeta: funcapi.ColumnMeta{Name: "Last Update Age", Tooltip: "Last Update Age", Type: funcapi.FieldTypeInteger, Units: "seconds", Visible: true, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(e.lastReceivedUpdate) }}, |
| 230 | {ColumnMeta: funcapi.ColumnMeta{Name: "Updates Received", Tooltip: "Updates Received", Type: funcapi.FieldTypeInteger, Visible: true, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.updateCounts, "received")) }}, |
| 231 | {ColumnMeta: funcapi.ColumnMeta{Name: "Updates Sent", Tooltip: "Updates Sent", Type: funcapi.FieldTypeInteger, Visible: true, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.updateCounts, "sent")) }}, |
| 232 | {ColumnMeta: funcapi.ColumnMeta{Name: "Prefixes Accepted", Tooltip: "Prefixes Accepted", Type: funcapi.FieldTypeInteger, Visible: true, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeCounts, "accepted")) }}, |
| 233 | {ColumnMeta: funcapi.ColumnMeta{Name: "Prefixes Advertised", Tooltip: "Prefixes Advertised", Type: funcapi.FieldTypeInteger, Visible: true, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeCounts, "advertised")) }}, |
| 234 | {ColumnMeta: funcapi.ColumnMeta{Name: "Last Error", Tooltip: "Last Error", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, FullWidth: true, Wrap: true, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return bgpLastErrorDisplay(e) }}, |
| 235 | {ColumnMeta: funcapi.ColumnMeta{Name: "Down Reason", Tooltip: "Down Reason", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, FullWidth: true, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.lastDownReason }}, |
| 236 | {ColumnMeta: funcapi.ColumnMeta{Name: "GR State", Tooltip: "GR State", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.gracefulRestart }}, |
| 237 | {ColumnMeta: funcapi.ColumnMeta{Name: "Unavailability Reason", Tooltip: "Unavailability Reason", Type: funcapi.FieldTypeString, Visible: true, Sortable: true, Filter: funcapi.FieldFilterMultiselect, FullWidth: true, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.unavailabilityReason }}, |
| 238 | {ColumnMeta: funcapi.ColumnMeta{Name: "Neighbor Address Type", Tooltip: "Neighbor Address Type", Type: funcapi.FieldTypeString, Visible: false, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.tags["neighbor_address_type"] }}, |
| 239 | {ColumnMeta: funcapi.ColumnMeta{Name: "Local AS", Tooltip: "Local AS", Type: funcapi.FieldTypeString, Visible: false, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.tags["local_as"] }}, |
| 240 | {ColumnMeta: funcapi.ColumnMeta{Name: "Local Identifier", Tooltip: "Local Identifier", Type: funcapi.FieldTypeString, Visible: false, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.tags["local_identifier"] }}, |
| 241 | {ColumnMeta: funcapi.ColumnMeta{Name: "Peer Identifier", Tooltip: "Peer Identifier", Type: funcapi.FieldTypeString, Visible: false, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.tags["peer_identifier"] }}, |
| 242 | {ColumnMeta: funcapi.ColumnMeta{Name: "Peer Type", Tooltip: "Peer Type", Type: funcapi.FieldTypeString, Visible: false, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.tags["peer_type"] }}, |
| 243 | {ColumnMeta: funcapi.ColumnMeta{Name: "BGP Version", Tooltip: "BGP Version", Type: funcapi.FieldTypeString, Visible: false, Sortable: true, Filter: funcapi.FieldFilterMultiselect, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.tags["bgp_version"] }}, |
| 244 | {ColumnMeta: funcapi.ColumnMeta{Name: "Messages Received", Tooltip: "Messages Received", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.messageCounts, "received")) }}, |
| 245 | {ColumnMeta: funcapi.ColumnMeta{Name: "Messages Sent", Tooltip: "Messages Sent", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.messageCounts, "sent")) }}, |
| 246 | {ColumnMeta: funcapi.ColumnMeta{Name: "Notifications Received", Tooltip: "Notifications Received", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.notificationCounts, "received")) }}, |
| 247 | {ColumnMeta: funcapi.ColumnMeta{Name: "Notifications Sent", Tooltip: "Notifications Sent", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.notificationCounts, "sent")) }}, |
| 248 | {ColumnMeta: funcapi.ColumnMeta{Name: "Last Received Notification Reason", Tooltip: "Last Received Notification Reason", Type: funcapi.FieldTypeString, Visible: false, Sortable: true, Filter: funcapi.FieldFilterMultiselect, FullWidth: true, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.lastRecvNotify }}, |
| 249 | {ColumnMeta: funcapi.ColumnMeta{Name: "Last Sent Notification Reason", Tooltip: "Last Sent Notification Reason", Type: funcapi.FieldTypeString, Visible: false, Sortable: true, Filter: funcapi.FieldFilterMultiselect, FullWidth: true, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return e.lastSentNotify }}, |
| 250 | {ColumnMeta: funcapi.ColumnMeta{Name: "Open Received", Tooltip: "Open Received", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.openCounts, "received")) }}, |
| 251 | {ColumnMeta: funcapi.ColumnMeta{Name: "Open Sent", Tooltip: "Open Sent", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.openCounts, "sent")) }}, |
| 252 | {ColumnMeta: funcapi.ColumnMeta{Name: "Keepalive Received", Tooltip: "Keepalive Received", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.keepaliveCounts, "received")) }}, |
| 253 | {ColumnMeta: funcapi.ColumnMeta{Name: "Keepalive Sent", Tooltip: "Keepalive Sent", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.keepaliveCounts, "sent")) }}, |
| 254 | {ColumnMeta: funcapi.ColumnMeta{Name: "Route Refresh Received", Tooltip: "Route Refresh Received", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeRefreshCounts, "received")) }}, |
| 255 | {ColumnMeta: funcapi.ColumnMeta{Name: "Route Refresh Sent", Tooltip: "Route Refresh Sent", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeRefreshCounts, "sent")) }}, |
| 256 | {ColumnMeta: funcapi.ColumnMeta{Name: "Established Transitions", Tooltip: "Established Transitions", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(e.establishedCount) }}, |
| 257 | {ColumnMeta: funcapi.ColumnMeta{Name: "Down Transitions", Tooltip: "Down Transitions", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(e.downTransitions) }}, |
| 258 | {ColumnMeta: funcapi.ColumnMeta{Name: "Up Transitions", Tooltip: "Up Transitions", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(e.upTransitions) }}, |
| 259 | {ColumnMeta: funcapi.ColumnMeta{Name: "Flaps", Tooltip: "Flaps", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummarySum}, Value: func(e *bgpPeerEntry) any { return int64Value(e.flaps) }}, |
| 260 | {ColumnMeta: funcapi.ColumnMeta{Name: "Prefixes Received", Tooltip: "Prefixes Received", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeCounts, "received")) }}, |
| 261 | {ColumnMeta: funcapi.ColumnMeta{Name: "Prefixes Active", Tooltip: "Prefixes Active", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeCounts, "active")) }}, |
| 262 | {ColumnMeta: funcapi.ColumnMeta{Name: "Prefixes Rejected", Tooltip: "Prefixes Rejected", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeCounts, "rejected")) }}, |
| 263 | {ColumnMeta: funcapi.ColumnMeta{Name: "Prefixes Suppressed", Tooltip: "Prefixes Suppressed", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeCounts, "suppressed")) }}, |
| 264 | {ColumnMeta: funcapi.ColumnMeta{Name: "Prefixes Withdrawn", Tooltip: "Prefixes Withdrawn", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeCounts, "withdrawn")) }}, |
| 265 | {ColumnMeta: funcapi.ColumnMeta{Name: "Routes Received Total", Tooltip: "Routes Received Total", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeTotals, "received")) }}, |
| 266 | {ColumnMeta: funcapi.ColumnMeta{Name: "Routes Advertised Total", Tooltip: "Routes Advertised Total", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeTotals, "advertised")) }}, |
| 267 | {ColumnMeta: funcapi.ColumnMeta{Name: "Routes Rejected Total", Tooltip: "Routes Rejected Total", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeTotals, "rejected")) }}, |
| 268 | {ColumnMeta: funcapi.ColumnMeta{Name: "Routes Active Total", Tooltip: "Routes Active Total", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeTotals, "active")) }}, |
| 269 | {ColumnMeta: funcapi.ColumnMeta{Name: "Prefix Limit", Tooltip: "Prefix Limit", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeLimits, "admin_limit")) }}, |
| 270 | {ColumnMeta: funcapi.ColumnMeta{Name: "Prefix Threshold", Tooltip: "Prefix Threshold", Type: funcapi.FieldTypeInteger, Units: "%", Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeLimitThresholds, "threshold")) }}, |
| 271 | {ColumnMeta: funcapi.ColumnMeta{Name: "Prefix Clear Threshold", Tooltip: "Prefix Clear Threshold", Type: funcapi.FieldTypeInteger, Units: "%", Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryMax}, Value: func(e *bgpPeerEntry) any { return int64Value(mapValuePtr(e.routeLimitThresholds, "clear_threshold")) }}, |
| 272 | {ColumnMeta: funcapi.ColumnMeta{Name: "Last Error Code", Tooltip: "Last Error Code", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return int64Value(e.lastErrorCode) }}, |
| 273 | {ColumnMeta: funcapi.ColumnMeta{Name: "Last Error Subcode", Tooltip: "Last Error Subcode", Type: funcapi.FieldTypeInteger, Visible: false, Sortable: true, Filter: funcapi.FieldFilterRange, Transform: funcapi.FieldTransformNumber, Summary: funcapi.FieldSummaryCount}, Value: func(e *bgpPeerEntry) any { return int64Value(e.lastErrorSubcode) }}, |
| 274 | } |