fix(ospf): показать конфликт состояний OSPF и BFD
This commit is contained in:
1 parent
4d3cb659c1
commit
12ead0b6c2
15 files changed
+6321
-18
No files matched your search
@@ -5,7 +5,7 @@ import { OspfTopologyScope } from "@/components/ospf-topology-scope"
|
||||
import { usePrefersReducedMotion } from "@/hooks/use-prefers-reduced-motion"
|
||||
import { deriveOspfRouterIdentity } from "@/lib/ospf-router-identity"
|
||||
import { buildOspfCostIndex, ospfCostKey } from "@/lib/ospf-cost"
|
||||
import { normalizeOspfState, normalizeBfdState, ospfNodeState, type OspfNeighborState, type BfdState } from "@/lib/ospf-state"
|
||||
import { normalizeOspfState, normalizeBfdState, ospfNodeState, ospfBfdConflicts, type OspfNeighborState, type BfdState } from "@/lib/ospf-state"
|
||||
import { useMemo, useState, useEffect, useRef, Suspense } from "react"
|
||||
import { useSearchParams } from "next/navigation"
|
||||
import { readOspfUrl, patchOspfUrl, type OspfTab } from "@/lib/ospf-url-state"
|
||||
@@ -62,6 +62,7 @@ interface OspfItem {
|
||||
}
|
||||
|
||||
interface OspfNeighbor {
|
||||
neighborAddress?: string
|
||||
id: string
|
||||
localRouter: string
|
||||
localLabel: string
|
||||
@@ -136,6 +137,7 @@ function backendToNeighbor(b: BackendNeighbor, ifaceMap: Map<string, number | nu
|
||||
remoteRouter: serverByRouterId[b.routerId] ?? b.routerId,
|
||||
remoteLabel: labels[serverByRouterId[b.routerId]] ?? b.routerId,
|
||||
remoteRouterId: b.routerId,
|
||||
neighborAddress: b.address,
|
||||
area: b.areaId !== b.area ? `${b.area} (${b.areaId})` : b.area,
|
||||
state,
|
||||
cost: ifaceMap.get(costKey) ?? null,
|
||||
@@ -1144,9 +1146,9 @@ function OspfRoute() {
|
||||
const href = patchOspfUrl(new URLSearchParams(window.location.search), values)
|
||||
if (href !== window.location.pathname + window.location.search) (replace ? window.history.replaceState : window.history.pushState).call(window.history, null, "", href)
|
||||
}
|
||||
return <OspfPageContent key={JSON.stringify([mode, backendUrl])} filterServerId={state.server} activeTab={state.tab} contextError={state.error} search={state.search} selectedNodeId={state.node} onSearch={value => navigate({ q: value }, true)} onSelectNode={node => navigate({ node })} onSelectServer={id => navigate({ server: id === ALL_SERVERS_ID ? null : id, node: null })} onSelectTab={tab => navigate({ tab: tab === "interfaces" ? null : tab })} onResetContext={() => navigate({ server: null, tab: null, q: null, node: null })} />
|
||||
return <OspfPageContent key={JSON.stringify([mode, backendUrl])} filterServerId={state.server} activeTab={state.tab} contextError={state.error} search={state.search} selectedNodeId={state.node} onSearch={value => navigate({ q: value }, true)} onSelectNode={node => navigate({ node })} onSelectServer={id => navigate({ server: id === ALL_SERVERS_ID ? null : id, node: null })} onSelectTab={tab => navigate({ tab: tab === "interfaces" ? null : tab })} onInspectBfd={() => navigate({ tab: "bfd", q: null })} onResetContext={() => navigate({ server: null, tab: null, q: null, node: null })} />
|
||||
}
|
||||
function OspfPageContent({ search, selectedNodeId, onSearch, onSelectNode, filterServerId, activeTab, contextError, onSelectServer, onSelectTab, onResetContext }: { search: string; selectedNodeId: string | null; onSearch: (value: string) => void; onSelectNode: (id: string | null) => void; filterServerId: string; activeTab: OspfTab; contextError: string | null; onSelectServer: (id: string) => void; onSelectTab: (tab: OspfTab) => void; onResetContext: () => void }) {
|
||||
function OspfPageContent({ search, selectedNodeId, onSearch, onSelectNode, filterServerId, activeTab, contextError, onSelectServer, onSelectTab, onInspectBfd, onResetContext }: { search: string; selectedNodeId: string | null; onSearch: (value: string) => void; onSelectNode: (id: string | null) => void; filterServerId: string; activeTab: OspfTab; contextError: string | null; onSelectServer: (id: string) => void; onSelectTab: (tab: OspfTab) => void; onInspectBfd: () => void; onResetContext: () => void }) {
|
||||
const { mode, backendUrl } = useDataSource()
|
||||
const isLive = mode === "live"
|
||||
|
||||
@@ -1272,6 +1274,7 @@ function OspfPageContent({ search, selectedNodeId, onSearch, onSelectNode, filte
|
||||
const scopedNeighbors = unavailable ? [] : filterServerId === ALL_SERVERS_ID ? neighbors : neighbors.filter(n => n.localRouter === filterServerId)
|
||||
const scopedBfdSessions = unavailable ? [] : filterServerId === ALL_SERVERS_ID ? bfdSessions : bfdSessions.filter(b => b.serverId === filterServerId)
|
||||
const scopedRoutes = unavailable ? [] : filterServerId === ALL_SERVERS_ID ? routes : routes.filter(r => r.serverId === filterServerId)
|
||||
const bfdConflicts = ospfBfdConflicts(scopedNeighbors, scopedItems, scopedBfdSessions)
|
||||
const query = search.trim().toLocaleLowerCase("ru")
|
||||
const matches = (...fields: (string | number | null | undefined)[]) => !query || fields.some(field => String(field ?? "").toLocaleLowerCase("ru").includes(query))
|
||||
const displayItems = scopedItems.filter(i => matches(i.routerLabel, i.interfaceName, i.area, i.type))
|
||||
@@ -1350,6 +1353,7 @@ function OspfPageContent({ search, selectedNodeId, onSearch, onSelectNode, filte
|
||||
<div className="flex flex-col gap-5">
|
||||
|
||||
{unavailable && <Alert variant="warning"><AlertCircleIcon /><AlertTitle>Контекст OSPF недоступен</AlertTitle><AlertDescription>{contextError ?? `Сервер ${filterServerId} отсутствует среди доступных устройств.`}<Button type="button" variant="outline" size="sm" disabled={writeBusy} onClick={onResetContext}>Сбросить контекст OSPF</Button></AlertDescription></Alert>}
|
||||
{bfdConflicts.length > 0 && <Alert variant="warning"><AlertCircleIcon /><AlertTitle>OSPF Full при недоступном BFD</AlertTitle><AlertDescription>Последний опрос: {bfdConflicts.map(n => `${n.localLabel} · ${n.localIface}`).join("; ")} — OSPF Full, BFD не Up. Проверьте канал: Full не подтверждает работу BFD.<Button type="button" variant="outline" size="sm" disabled={writeBusy} onClick={() => { if (!operationBusy.current) onInspectBfd() }}>Проверить BFD</Button></AlertDescription></Alert>}
|
||||
<DataPageToolbar search={search} onSearchChange={onSearch} searchPlaceholder="Поиск в текущем разделе OSPF…" countLabel={viewIncomplete ? "Данные неизвестны" : `${visibleCount} из ${scopeCount}`} actions={search ? <Button type="button" variant="ghost" size="sm" onClick={() => onSearch("")}>Сбросить поиск</Button> : undefined} />
|
||||
{/* data source banner */}
|
||||
{isLive && loading && (
|
||||
|
||||
Reference in new issue
Block a user