ATC_SIMPLE/BACKEND/app/services/physical_test_service.ts

163 lines
4.9 KiB
TypeScript

import moment from 'moment'
import { normalizeInterface } from '../ultils/helper.js'
import { PhysicalTestReport, PhysicalTestResult, PortState } from '../ultils/types.js'
const LINK_UPDOWN_REGEX =
/Interface\s+((?:FastEthernet|GigabitEthernet|TenGigabitEthernet|TwentyFiveGigE|FortyGigabitEthernet|HundredGigE|Ethernet|Port-channel|Fa|Gi|Te|Hu|Eth)[\w\/.-]+),\s+changed state to\s+(up|down)/i
export class PhysicalPortTest {
public ports = new Map<string, PortState>()
private expectedPorts: string[]
public done = false
private startTime: Date
public inventory: any
constructor(expectedPorts: string[]) {
this.expectedPorts = expectedPorts
this.startTime = new Date()
this.inventory = ''
expectedPorts.forEach((p) => {
this.ports.set(normalizeInterface(p), {
name: normalizeInterface(p),
tested: false,
})
})
}
start(expectedPorts: string[], inventory: any) {
this.ports.clear()
this.startTime = new Date()
this.expectedPorts = expectedPorts
this.inventory = inventory
this.done = false
expectedPorts.forEach((p) => {
this.ports.set(normalizeInterface(p), {
name: normalizeInterface(p),
tested: false,
})
})
// this.connection.writeCommand('terminal length 0')
// this.connection.writeCommand('terminal monitor')
// this.connection.onLog((line) => {
// this.handleLog(line);
// });
}
handleLog(line: string) {
const match = line.match(LINK_UPDOWN_REGEX)
if (!match) return
const rawIface = match[1]
const state = match[2] as 'up' | 'down'
const iface = normalizeInterface(rawIface)
const port = this.ports.get(iface)
if (!port) return
// tránh update trùng state liên tiếp
if (port.lastState === state) return
port.lastState = state
port.lastSeen = new Date()
// chỉ cần UP 1 lần là pass
if (state === 'up' && !port.tested) {
port.tested = true
this.checkDone()
}
return this.getTestedPorts()
}
getTestedPorts(): string[] {
return Array.from(this.ports.values())
.filter((p) => p.tested)
.map((p) => p.name)
.sort()
}
private checkDone() {
const testedCount = [...this.ports.values()].filter((p) => p.tested).length
if (testedCount === this.expectedPorts.length) {
this.done = true
this.onDone()
}
}
onDone() {
this.getFormReport()
// this.ports.clear()
console.log('✅ Physical Test DONE')
}
getFormReport() {
const report: PhysicalTestReport = {
device: {
model: this?.inventory?.pid || '',
serial: this?.inventory?.sn || '',
},
startTime: this.startTime,
endTime: new Date(),
durationMs: Date.now() - this.startTime.getTime(),
ports: Array.from(this.ports.values()),
}
return this.generateEmailReport(report)
// console.log('✅ Physical Test DONE')
}
getResult(): PhysicalTestResult {
const tested = [...this.ports.values()].filter((p) => p.tested)
const missing = [...this.ports.values()].filter((p) => !p.tested).map((p) => p.name)
return {
expected: this.expectedPorts.length,
tested: tested.length,
missingPorts: missing,
status: this.done ? 'DONE' : 'RUNNING',
}
}
generateEmailReport(report: PhysicalTestReport): string {
const tested = report.ports.filter((p) => p.tested)
const missing = report.ports.filter((p) => !p.tested)
const status = missing.length === 0 ? 'PASS' : 'WARNING'
return `
Physical Port Test Report<br/>
────────────────────────────────<br/>
Model : <b>${report.device.model ?? 'N/A'}</b><br/>
Serial Number : <b>${report.device.serial ?? 'N/A'}</b><br/>
Started At : ${moment(report.startTime).format('YYYY/MM/DD, HH:mm:ss')}<br/>
Finished At : ${moment(report.endTime).format('YYYY/MM/DD, HH:mm:ss')}<br/>
Duration : ${Math.floor(report.durationMs / 1000)} sec<br/>
Status : ${status === 'PASS' ? '✅ PASS' : '⚠️ WARNING'}<br/>
<br/>
────────────────────────────────<br/>
<b>Test Summary</b><br/>
────────────<br/>
Total Ports : ${report.ports.length}<br/>
Ports Tested (UP) : ${tested.length}<br/>
Ports Missing : ${missing.length}<br/>
<br/>
────────────────────────────────<br/>
<b>Passed Ports</b><br/>
────────────<br/>
${tested.map((p) => p.name).join('<br/>')}<br/>
<br/>
${
missing.length
? `
────────────────────────────────<br/>
<b>Missing Ports</b><br/>
─────────────<br/>
${missing.map((p) => p.name).join('<br/>')}
`
: ''
}<br/>
<br/>
`.trim()
}
}