fix(ospf): показать конфликт состояний OSPF и BFD
This commit is contained in:
1 parent
4d3cb659c1
commit
12ead0b6c2
15 files changed
+6321
-18
No files matched your search
+15
-1
@@ -1,12 +1,26 @@
|
||||
import assert from "node:assert/strict"
|
||||
import { test } from "node:test"
|
||||
import { normalizeOspfState, normalizeBfdState, ospfNodeState } from "./ospf-state"
|
||||
import { normalizeOspfState, normalizeBfdState, ospfNodeState, ospfBfdConflicts } from "./ospf-state"
|
||||
test("RouterOS states keep transitional adjacency separate from failure", () => {
|
||||
assert.deepEqual(["full", "2-way", "ExStart", "exchange", "loading", "attempt", "init", "down"].map(normalizeOspfState), ["Full", "2-Way", "ExStart", "Exchange", "Loading", "Attempt", "Init", "Down"])
|
||||
assert.equal(normalizeOspfState("future-state"), "Unknown")
|
||||
assert.equal(normalizeBfdState(""), "Unknown")
|
||||
assert.equal(normalizeBfdState(" UP "), "Up")
|
||||
})
|
||||
|
||||
test("Full/BFD conflict requires fresh exact device, interface, peer, explicit active BFD and unique direct session", () => {
|
||||
const neighbor = { localRouter: "1", localIface: "gre-own", neighborAddress: "198.18.153.2", state: "Full" as const }
|
||||
const iface = { serverId: "1", interfaceName: "gre-own", active: true, useBfd: true }
|
||||
const session = { serverId: "1", iface: "gre-own", remoteAddr: "198.18.153.2", state: "Down" as const, multihop: false }
|
||||
assert.deepEqual(ospfBfdConflicts([neighbor], [iface], [session]), [neighbor])
|
||||
assert.deepEqual(ospfBfdConflicts([neighbor], [iface], [{ ...session, state: "AdminDown" }]), [neighbor])
|
||||
for (const s of [{ ...session, serverId: "2" }, { ...session, iface: "ether1" }, { ...session, remoteAddr: "198.18.153.3" }, { ...session, multihop: true }, { ...session, multihop: null }, { ...session, state: "Unknown" as const }, { ...session, state: "Up" as const }, { ...session, dataUnknown: true }]) assert.deepEqual(ospfBfdConflicts([neighbor], [iface], [s]), [])
|
||||
assert.deepEqual(ospfBfdConflicts([neighbor], [iface], [session, { ...session, state: "Up" }]), [])
|
||||
for (const i of [{ ...iface, useBfd: null }, { ...iface, useBfd: false }, { ...iface, active: false }, { ...iface, active: null }, { ...iface, dataUnknown: true }]) assert.deepEqual(ospfBfdConflicts([neighbor], [i], [session]), [])
|
||||
assert.deepEqual(ospfBfdConflicts([{ ...neighbor, dataUnknown: true }], [iface], [session]), [])
|
||||
assert.deepEqual(ospfBfdConflicts([{ ...neighbor, state: "Down" }], [iface], [session]), [])
|
||||
assert.deepEqual(ospfBfdConflicts([{ ...neighbor, neighborAddress: undefined }], [iface], [session]), [])
|
||||
})
|
||||
test("topology does not infer Down from missing or transitional evidence", () => {
|
||||
assert.equal(ospfNodeState([]), "none")
|
||||
assert.equal(ospfNodeState(["Down"]), "Down")
|
||||
|
||||
@@ -12,3 +12,18 @@ export function ospfNodeState(states: OspfNeighborState[]): "Full" | "2-Way" | "
|
||||
if (states.some(state => state !== "Down")) return "Pending"
|
||||
return "Down"
|
||||
}
|
||||
|
||||
/** Correlate only a single observed direct BFD session on an explicitly configured interface. */
|
||||
export function ospfBfdConflicts<T extends { localRouter: string; localIface: string; neighborAddress?: string; state: OspfNeighborState; dataUnknown?: boolean }>(
|
||||
neighbors: T[],
|
||||
interfaces: Array<{ serverId: string; interfaceName: string; useBfd?: boolean | null; active?: boolean | null; dataUnknown?: boolean }>,
|
||||
sessions: Array<{ serverId: string; iface: string; remoteAddr: string; state: BfdState; multihop?: boolean | null; dataUnknown?: boolean }>,
|
||||
): T[] {
|
||||
const ip = (value: string) => value.split("%")[0]?.toLowerCase() ?? ""
|
||||
return neighbors.filter(n => {
|
||||
if (n.dataUnknown || n.state !== "Full" || !n.neighborAddress || !n.localIface) return false
|
||||
if (!interfaces.some(i => !i.dataUnknown && i.active === true && i.useBfd === true && i.serverId === n.localRouter && i.interfaceName === n.localIface)) return false
|
||||
const matching = sessions.filter(s => !s.dataUnknown && s.multihop === false && s.serverId === n.localRouter && s.iface === n.localIface && ip(s.remoteAddr) === ip(n.neighborAddress!))
|
||||
return matching.length === 1 && (matching[0]!.state === "Down" || matching[0]!.state === "AdminDown")
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user