fix(gre): отклонять повреждённые и повторяющиеся native идентификаторы
This commit is contained in:
1 parent
51a579d370
commit
7a38bb36aa
19 files changed
+678
-19
No files matched your search
@@ -12,7 +12,7 @@ import {
|
||||
type GreSnapshot,
|
||||
} from "./entity-snapshots.js"
|
||||
import { captureAndAppendRevision } from "./config-revisions.js"
|
||||
import { readGreObservedState } from "./gre-observed-state.js"
|
||||
import { readGreObservedState, readGreNativeRows } from "./gre-observed-state.js"
|
||||
|
||||
type ServerRow = typeof servers.$inferSelect
|
||||
|
||||
@@ -114,8 +114,9 @@ export function mapGreLive(
|
||||
gre: GreLiveIface[]
|
||||
addrs: GreLiveAddr[]
|
||||
} {
|
||||
const observedGre = readGreNativeRows(greRaw)
|
||||
const addrsByIface = new Map<string, GreLiveAddr[]>()
|
||||
const greNames = new Set(greRaw.map(g => (g.name ?? "").trim()))
|
||||
const greNames = new Set(observedGre.map(g => g.name.trim()))
|
||||
for (const a of addrsRaw) {
|
||||
if (rosDisabled(a.dynamic)) continue
|
||||
const iface = (a.interface ?? "").trim()
|
||||
@@ -134,9 +135,9 @@ export function mapGreLive(
|
||||
const addrs: GreLiveAddr[] = []
|
||||
const tunnels: LiveGreTunnel[] = []
|
||||
|
||||
greRaw.forEach((g, idx) => {
|
||||
const rosId = String(g[".id"] ?? g.name ?? `gre-${idx}`)
|
||||
const name = (g.name ?? "").trim() || `gre-${idx + 1}`
|
||||
observedGre.forEach((g) => {
|
||||
const rosId = g[".id"]
|
||||
const name = g.name.trim()
|
||||
const keepalive = parseKeepalive(g.keepalive)
|
||||
const fromComment = parseInnerFromComment(g.comment)
|
||||
const ifaceAddrs = addrsByIface.get(name) ?? []
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import {readGreNativeRows} from './gre-observed-state.js'
|
||||
import {mapGreLive} from './gre-live.js'
|
||||
import {servers} from '../db/schema.js'
|
||||
const row={'.id':'*A',name:'GRE «тест» / peer',disabled:'false',running:'true'},server={id:1} as typeof servers.$inferSelect
|
||||
assert.deepEqual(readGreNativeRows([]),[]);assert.equal(mapGreLive(server,[row],[]).tunnels[0].id,'*A');assert.equal(mapGreLive(server,[row],[]).tunnels[0].name,row.name)
|
||||
const broken=[{},null,[null],[[]],[{...row,'.id':''}],[{...row,name:' '}],[{...row,'.id':undefined}],[{...row,name:undefined}],[{...row,mtu:1400}],[{...row,'.id':' *A'}],[row,row],[row,{...row,'.id':'*B'}],[row,{...row,'.id':'*B',name:' '+row.name+' '}]]
|
||||
for(const raw of broken){assert.throws(()=>readGreNativeRows(raw),/каталог GRE|native ID/);assert.throws(()=>mapGreLive(server,raw as never,[]),/каталог GRE|native ID/)}
|
||||
console.log('GRE identity: '+broken.length+' malformed/duplicate cases rejected by raw reader and catalog mapper; empty/native ID/special name preserved')
|
||||
@@ -1,5 +1,20 @@
|
||||
import { observedBoolean } from "./ospf-observed-values.js"
|
||||
|
||||
/** Native identity must exist before commands or configuration snapshots refer to it. */
|
||||
export function readGreNativeRows(raw: unknown): Array<Record<string, string>> {
|
||||
if (!Array.isArray(raw) || raw.some(row => !row || typeof row !== "object" || Array.isArray(row)
|
||||
|| Object.values(row).some(value => typeof value !== "string")
|
||||
|| [".id", "name"].some(key => typeof row[key] !== "string" || !row[key].trim())
|
||||
|| row[".id"] !== row[".id"].trim())) {
|
||||
throw new Error("Некорректный каталог GRE: отсутствует native ID/имя или повреждены поля")
|
||||
}
|
||||
if (new Set(raw.map(row => row[".id"])).size !== raw.length
|
||||
|| new Set(raw.map(row => row.name.trim())).size !== raw.length) {
|
||||
throw new Error("Повторяющиеся native ID или имена GRE")
|
||||
}
|
||||
return raw as Array<Record<string, string>>
|
||||
}
|
||||
|
||||
/** Disabled is authoritative; an enabled interface also needs a known running flag. */
|
||||
export function readGreObservedState(flags: { running?: string; disabled?: string }, name: string) {
|
||||
const disabled = observedBoolean(flags.disabled)
|
||||
|
||||
@@ -16,7 +16,7 @@ import {mapGreLive} from './gre-live.js'
|
||||
import {mapGreAlertRow} from './gre-tunnels-live.js'
|
||||
let combinations=0
|
||||
for(const disabled of ['true','yes','false','no',undefined,'','junk'])for(const running of ['true','yes','false','no',undefined,'','junk']){
|
||||
const row={'.id':'*1',name:'own-flags',disabled,running},off=['true','yes'].includes(disabled??''),on=['false','no'].includes(disabled??''),runningKnown=['true','yes','false','no'].includes(running??'')
|
||||
const row={'.id':'*1',name:'own-flags',...(disabled===undefined?{}:{disabled}),...(running===undefined?{}:{running})},off=['true','yes'].includes(disabled??''),on=['false','no'].includes(disabled??''),runningKnown=['true','yes','false','no'].includes(running??'')
|
||||
if(off||on&&runningKnown){const expected=off?'down':['true','yes'].includes(running??'')?'up':'degraded';assert.equal(mapGreAlertRow(devices[0],row).status,expected);assert.equal(mapGreLive(devices[0],[row],[]).tunnels[0].status,expected)}
|
||||
else {assert.throws(()=>mapGreAlertRow(devices[0],row),/Не подтверждено состояние GRE/);assert.throws(()=>mapGreLive(devices[0],[row],[]),/Не подтверждено состояние GRE/)}
|
||||
combinations++
|
||||
|
||||
@@ -2,8 +2,7 @@ import { eq } from "drizzle-orm"
|
||||
import { db } from "../db/index.js"
|
||||
import { servers } from "../db/schema.js"
|
||||
import { MikrotikClient } from "./mikrotik.js"
|
||||
import { readInternetPathRows } from "./internet-path-snapshot.js"
|
||||
import { readGreObservedState } from "./gre-observed-state.js"
|
||||
import { readGreObservedState, readGreNativeRows } from "./gre-observed-state.js"
|
||||
|
||||
type ServerRow = typeof servers.$inferSelect
|
||||
|
||||
@@ -40,8 +39,7 @@ export function mapGreAlertRow(server: ServerRow, g: RosGre, idx = 0): GreTunnel
|
||||
|
||||
async function fetchGreForServer(server: ServerRow): Promise<GreTunnelLiveRow[]> {
|
||||
const client = MikrotikClient.fromServer(server)
|
||||
const greRaw = readInternetPathRows(await client.get<unknown>("/interface/gre"), "GRE alert source", [".id", "name"])
|
||||
if (new Set(greRaw.map(row => row[".id"])).size !== greRaw.length) throw new Error("Повторяющиеся ID GRE")
|
||||
const greRaw = readGreNativeRows(await client.get<unknown>("/interface/gre"))
|
||||
return greRaw.map((g, idx) => mapGreAlertRow(server, g, idx))
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user