Update line_connection.ts
This commit is contained in:
parent
8580f3c88a
commit
429c570688
|
|
@ -157,7 +157,9 @@ export default class LineConnection {
|
||||||
this.outputPhysicalTest = ''
|
this.outputPhysicalTest = ''
|
||||||
this.listDeviceIos = []
|
this.listDeviceIos = []
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Connect to line with socket
|
||||||
|
*/
|
||||||
connect(timeoutMs = 5000) {
|
connect(timeoutMs = 5000) {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const { ip, port, lineNumber, id, stationId } = this.config
|
const { ip, port, lineNumber, id, stationId } = this.config
|
||||||
|
|
@ -304,10 +306,16 @@ export default class LineConnection {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waiting with millisecond
|
||||||
|
*/
|
||||||
private sleep(ms: number) {
|
private sleep(ms: number) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a command with socket.write
|
||||||
|
*/
|
||||||
async writeCommand(cmd: string | Buffer<ArrayBuffer>, userName = '') {
|
async writeCommand(cmd: string | Buffer<ArrayBuffer>, userName = '') {
|
||||||
if (this.client.destroyed) {
|
if (this.client.destroyed) {
|
||||||
console.log(`⚠️ Cannot send, line ${this.config.lineNumber} is closed`)
|
console.log(`⚠️ Cannot send, line ${this.config.lineNumber} is closed`)
|
||||||
|
|
@ -326,6 +334,9 @@ export default class LineConnection {
|
||||||
this.client.write(cmd)
|
this.client.write(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect socket with line
|
||||||
|
*/
|
||||||
async disconnect() {
|
async disconnect() {
|
||||||
try {
|
try {
|
||||||
console.log('[DISCONNECT] Line', this.config.lineNumber)
|
console.log('[DISCONNECT] Line', this.config.lineNumber)
|
||||||
|
|
@ -342,6 +353,9 @@ export default class LineConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a scenario as DPELP, Breaking password, load ios,...
|
||||||
|
*/
|
||||||
async runScript(script: Scenario, userName: string) {
|
async runScript(script: Scenario, userName: string) {
|
||||||
if (!this.client || this.client.destroyed) {
|
if (!this.client || this.client.destroyed) {
|
||||||
console.log('Not connected')
|
console.log('Not connected')
|
||||||
|
|
@ -575,7 +589,7 @@ export default class LineConnection {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (typeof step.send !== 'undefined') {
|
if (typeof step.send !== 'undefined') {
|
||||||
// console.log(Date.now() - now, (step?.send ?? '[ENTER]').toString())
|
console.log(Date.now() - now, (step?.send ?? '[ENTER]').toString())
|
||||||
this.outputScenario += `\n---send-command---"${(step?.send ?? '[ENTER]').toString().replace(/\r/g, '\\r').replace(/\n/g, '\\n')}"---${now}---\n`
|
this.outputScenario += `\n---send-command---"${(step?.send ?? '[ENTER]').toString().replace(/\r/g, '\\r').replace(/\n/g, '\\n')}"---${now}---\n`
|
||||||
appendLog(
|
appendLog(
|
||||||
`\n---send-command---"${(step?.send ?? '[ENTER]').toString().replace(/\r/g, '\\r').replace(/\n/g, '\\n')}"---${now}---\n`,
|
`\n---send-command---"${(step?.send ?? '[ENTER]').toString().replace(/\r/g, '\\r').replace(/\n/g, '\\n')}"---${now}---\n`,
|
||||||
|
|
@ -620,6 +634,9 @@ export default class LineConnection {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconnect socket with line
|
||||||
|
*/
|
||||||
public async reconnect(): Promise<boolean> {
|
public async reconnect(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
this.disconnect()
|
this.disconnect()
|
||||||
|
|
@ -632,6 +649,9 @@ export default class LineConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User open CLI from front-end
|
||||||
|
*/
|
||||||
userOpenCLI(user: User) {
|
userOpenCLI(user: User) {
|
||||||
this.config.openCLI = true
|
this.config.openCLI = true
|
||||||
this.config.userEmailOpenCLI = user.userEmail
|
this.config.userEmailOpenCLI = user.userEmail
|
||||||
|
|
@ -651,6 +671,9 @@ export default class LineConnection {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User close CLI from front-end
|
||||||
|
*/
|
||||||
userCloseCLI() {
|
userCloseCLI() {
|
||||||
this.config.openCLI = false
|
this.config.openCLI = false
|
||||||
this.config.userEmailOpenCLI = ''
|
this.config.userEmailOpenCLI = ''
|
||||||
|
|
@ -662,6 +685,9 @@ export default class LineConnection {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear output buffer
|
||||||
|
*/
|
||||||
clearCLI() {
|
clearCLI() {
|
||||||
this.config.output = ''
|
this.config.output = ''
|
||||||
this.socketIO.emit('user_clear_terminal', {
|
this.socketIO.emit('user_clear_terminal', {
|
||||||
|
|
@ -671,6 +697,9 @@ export default class LineConnection {
|
||||||
setTimeout(() => this.writeCommand('\r\n'), 100)
|
setTimeout(() => this.writeCommand('\r\n'), 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waiting for a expect with until catch it from output
|
||||||
|
*/
|
||||||
waitForExpect = async (expect: string, timeout = 60000) => {
|
waitForExpect = async (expect: string, timeout = 60000) => {
|
||||||
const start = Date.now()
|
const start = Date.now()
|
||||||
// console.log('[EXPECT]', expect, timeout)
|
// console.log('[EXPECT]', expect, timeout)
|
||||||
|
|
@ -684,6 +713,9 @@ export default class LineConnection {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect inventory data from output
|
||||||
|
*/
|
||||||
getInventory = () => {
|
getInventory = () => {
|
||||||
const data = textfsmResults(this.outputInventory, 'show inventory')
|
const data = textfsmResults(this.outputInventory, 'show inventory')
|
||||||
try {
|
try {
|
||||||
|
|
@ -714,7 +746,9 @@ export default class LineConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gửi nhiều ký tự ESC để vào ROMMON
|
/**
|
||||||
|
* Gửi nhiều ký tự ESC để vào ROMMON
|
||||||
|
*/
|
||||||
breakSpam() {
|
breakSpam() {
|
||||||
console.log('SPAM Break to line:', this.config.lineNumber)
|
console.log('SPAM Break to line:', this.config.lineNumber)
|
||||||
let count = 0
|
let count = 0
|
||||||
|
|
@ -728,6 +762,9 @@ export default class LineConnection {
|
||||||
}, 1)
|
}, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Baud of line
|
||||||
|
*/
|
||||||
async setBaud(baud: number) {
|
async setBaud(baud: number) {
|
||||||
this.writeCommand('enable\r\n')
|
this.writeCommand('enable\r\n')
|
||||||
await sleep(500)
|
await sleep(500)
|
||||||
|
|
@ -743,6 +780,9 @@ export default class LineConnection {
|
||||||
this.writeCommand('\r\n')
|
this.writeCommand('\r\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get content's log of line with date
|
||||||
|
*/
|
||||||
async getLog(date: string) {
|
async getLog(date: string) {
|
||||||
const logDir = path.join('storage', 'system_logs')
|
const logDir = path.join('storage', 'system_logs')
|
||||||
const logFile = path
|
const logFile = path
|
||||||
|
|
@ -759,6 +799,9 @@ export default class LineConnection {
|
||||||
return await fs.promises.readFile(logFile, 'utf8')
|
return await fs.promises.readFile(logFile, 'utf8')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect log by call api gpt, return summary and issues
|
||||||
|
*/
|
||||||
async detectLogWithAI(log: string) {
|
async detectLogWithAI(log: string) {
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
|
|
@ -812,6 +855,9 @@ export default class LineConnection {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add cache to list history devices on this line
|
||||||
|
*/
|
||||||
async addHistory(stationId: number, lineId: number, item: HistoryItem) {
|
async addHistory(stationId: number, lineId: number, item: HistoryItem) {
|
||||||
if (!item.pid || !item.sn) return
|
if (!item.pid || !item.sn) return
|
||||||
const key = `station:${stationId}:line:${lineId}:history`
|
const key = `station:${stationId}:line:${lineId}:history`
|
||||||
|
|
@ -850,12 +896,18 @@ export default class LineConnection {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list history devices
|
||||||
|
*/
|
||||||
async getHistory(stationId: number, lineId: number) {
|
async getHistory(stationId: number, lineId: number) {
|
||||||
const key = `station:${stationId}:line:${lineId}:history`
|
const key = `station:${stationId}:line:${lineId}:history`
|
||||||
const items = await redis.zrange(key, 0, -1)
|
const items = await redis.zrange(key, 0, -1)
|
||||||
return items.map((i) => JSON.parse(i))
|
return items.map((i) => JSON.parse(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle raw log to regex error
|
||||||
|
*/
|
||||||
handleLogLine = (line: string) => {
|
handleLogLine = (line: string) => {
|
||||||
try {
|
try {
|
||||||
const parsed = classifyLog(line)
|
const parsed = classifyLog(line)
|
||||||
|
|
@ -865,6 +917,9 @@ export default class LineConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check raw log was regex each 5 minutes, if has error will send email report
|
||||||
|
*/
|
||||||
checkLog() {
|
checkLog() {
|
||||||
const interval = setInterval(async () => {
|
const interval = setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -902,6 +957,9 @@ export default class LineConnection {
|
||||||
}, 300000)
|
}, 300000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render table to view error
|
||||||
|
*/
|
||||||
renderErrorTable(rows: ErrorRow[]): string {
|
renderErrorTable(rows: ErrorRow[]): string {
|
||||||
if (!rows.length) {
|
if (!rows.length) {
|
||||||
return `<p style="color: green;">No errors detected</p>`
|
return `<p style="color: green;">No errors detected</p>`
|
||||||
|
|
@ -942,6 +1000,9 @@ export default class LineConnection {
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a body email
|
||||||
|
*/
|
||||||
buildEmailContent(result: TestResult): string {
|
buildEmailContent(result: TestResult): string {
|
||||||
const rows = mapErrorsToRows(result.errors)
|
const rows = mapErrorsToRows(result.errors)
|
||||||
const table = this.renderErrorTable(rows)
|
const table = this.renderErrorTable(rows)
|
||||||
|
|
@ -956,6 +1017,9 @@ export default class LineConnection {
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update note of SN to ERP after run DPELP
|
||||||
|
*/
|
||||||
async updateNote(sn: string, data: DataDPELP) {
|
async updateNote(sn: string, data: DataDPELP) {
|
||||||
const licenses = Array.isArray(data.license)
|
const licenses = Array.isArray(data.license)
|
||||||
? [...new Set(data.license)]
|
? [...new Set(data.license)]
|
||||||
|
|
@ -968,6 +1032,9 @@ export default class LineConnection {
|
||||||
await updateNoteToERP(sn, note)
|
await updateNoteToERP(sn, note)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starting physical test (PoE ports testing)
|
||||||
|
*/
|
||||||
async runPhysicalTest() {
|
async runPhysicalTest() {
|
||||||
if (this.config.runningPhysical) {
|
if (this.config.runningPhysical) {
|
||||||
console.log('Running physical test')
|
console.log('Running physical test')
|
||||||
|
|
@ -1002,6 +1069,9 @@ export default class LineConnection {
|
||||||
// }, 10000)
|
// }, 10000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End all testing
|
||||||
|
*/
|
||||||
endTesting() {
|
endTesting() {
|
||||||
this.physicalTest.done = true
|
this.physicalTest.done = true
|
||||||
this.physicalTest.resetTestedPorts()
|
this.physicalTest.resetTestedPorts()
|
||||||
|
|
@ -1018,6 +1088,9 @@ export default class LineConnection {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list PoE ports
|
||||||
|
*/
|
||||||
async getPorts(): Promise<string[]> {
|
async getPorts(): Promise<string[]> {
|
||||||
this.writeCommand(' show power inline\r\n')
|
this.writeCommand(' show power inline\r\n')
|
||||||
this.writeCommand(' \r\n')
|
this.writeCommand(' \r\n')
|
||||||
|
|
@ -1040,6 +1113,9 @@ export default class LineConnection {
|
||||||
return [...new Set(ports)]
|
return [...new Set(ports)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send report after done physical test
|
||||||
|
*/
|
||||||
async sendReportPhysicalTest() {
|
async sendReportPhysicalTest() {
|
||||||
const formReport = this.physicalTest.getFormReport()
|
const formReport = this.physicalTest.getFormReport()
|
||||||
await sendMessageToMail(
|
await sendMessageToMail(
|
||||||
|
|
@ -1048,6 +1124,9 @@ export default class LineConnection {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle load ios for router
|
||||||
|
*/
|
||||||
async loadIosRouter(nameIos: string, userName: string) {
|
async loadIosRouter(nameIos: string, userName: string) {
|
||||||
const station = await Station.find(this.config.stationId)
|
const station = await Station.find(this.config.stationId)
|
||||||
if (!station) return
|
if (!station) return
|
||||||
|
|
@ -1193,6 +1272,9 @@ export default class LineConnection {
|
||||||
await this.sendEmailLoadIos(nameIos, startTime)
|
await this.sendEmailLoadIos(nameIos, startTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle load ios for switch
|
||||||
|
*/
|
||||||
async loadIosSwitch(nameIos: string, userName: string) {
|
async loadIosSwitch(nameIos: string, userName: string) {
|
||||||
const station = await Station.find(this.config.stationId)
|
const station = await Station.find(this.config.stationId)
|
||||||
if (!station) return
|
if (!station) return
|
||||||
|
|
@ -1402,6 +1484,9 @@ export default class LineConnection {
|
||||||
await this.sendEmailLoadIos(nameIos, startTime)
|
await this.sendEmailLoadIos(nameIos, startTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send mail report after load ios
|
||||||
|
*/
|
||||||
async sendEmailLoadIos(nameIos: string, startTime: string) {
|
async sendEmailLoadIos(nameIos: string, startTime: string) {
|
||||||
const timeZone = process.env.TIME_ZONE || 'Australia/Sydney'
|
const timeZone = process.env.TIME_ZONE || 'Australia/Sydney'
|
||||||
const dataFormat = momentTZ().tz(timeZone).format('YYYY/MM/DD, HH:mm:ss')
|
const dataFormat = momentTZ().tz(timeZone).format('YYYY/MM/DD, HH:mm:ss')
|
||||||
|
|
@ -1422,20 +1507,38 @@ export default class LineConnection {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check list ios exist on flash
|
||||||
|
*/
|
||||||
async checkDeviceFlash() {
|
async checkDeviceFlash() {
|
||||||
this.writeCommand(' enable\r\n')
|
this.writeCommand(' enable\r\n')
|
||||||
this.writeCommand('show flash:\r\n')
|
this.writeCommand('show flash:\r\n')
|
||||||
await sleep(2000)
|
await sleep(2000)
|
||||||
const ios = []
|
|
||||||
const binRegex = /^\s*\d+\s+-rwx\s+\d+\s+.*?\s+([^\s]+\.bin)\s*$/gim
|
|
||||||
|
|
||||||
|
const output = this.outputBuffer
|
||||||
|
const ios: string[] = []
|
||||||
let match
|
let match
|
||||||
while ((match = binRegex.exec(this.outputBuffer)) !== null) {
|
|
||||||
ios.push(match[1])
|
const SWITCH_BIN_REGEX = /^\s*\d+\s+-rwx\s+\d+\s+.*?\s+([^\s]+\.bin)\s*$/gim
|
||||||
|
const ROUTER_BIN_REGEX = /^\s*\d+\s+(\d+)\s+.*?\s+([^\s]+\.bin)\s*$/gim
|
||||||
|
|
||||||
|
// 🔍 Detect device type
|
||||||
|
const isSwitch = output.includes('-rwx')
|
||||||
|
const regex = isSwitch ? SWITCH_BIN_REGEX : ROUTER_BIN_REGEX
|
||||||
|
|
||||||
|
// reset regex state
|
||||||
|
regex.lastIndex = 0
|
||||||
|
|
||||||
|
while ((match = regex.exec(output)) !== null) {
|
||||||
|
ios.push(isSwitch ? match[1] : match[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
return ios
|
return ios
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete File on Flash
|
||||||
|
*/
|
||||||
async deleteFileOnFlash(fileName: string) {
|
async deleteFileOnFlash(fileName: string) {
|
||||||
await this.writeCommand(`delete flash:${fileName}\r\n`)
|
await this.writeCommand(`delete flash:${fileName}\r\n`)
|
||||||
await this.writeCommand(`\r\n`)
|
await this.writeCommand(`\r\n`)
|
||||||
|
|
@ -1443,6 +1546,9 @@ export default class LineConnection {
|
||||||
await sleep(3000)
|
await sleep(3000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload file from flash to TFTP server
|
||||||
|
*/
|
||||||
async uploadFileToServerTFTP(fileName: string, server: string) {
|
async uploadFileToServerTFTP(fileName: string, server: string) {
|
||||||
this.config.runningScenario = 'Upload file'
|
this.config.runningScenario = 'Upload file'
|
||||||
await this.writeCommand(`copy flash: tftp:\r\n`)
|
await this.writeCommand(`copy flash: tftp:\r\n`)
|
||||||
|
|
@ -1460,7 +1566,9 @@ export default class LineConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// function get list ios
|
/**
|
||||||
|
* Get list ios from TFTP server
|
||||||
|
*/
|
||||||
async getListIos() {
|
async getListIos() {
|
||||||
try {
|
try {
|
||||||
const controller = new IosLicenseController()
|
const controller = new IosLicenseController()
|
||||||
|
|
@ -1472,6 +1580,9 @@ export default class LineConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current boot ios of device
|
||||||
|
*/
|
||||||
async getCurrentBootIos() {
|
async getCurrentBootIos() {
|
||||||
this.writeCommand('show version | include System image\r\n')
|
this.writeCommand('show version | include System image\r\n')
|
||||||
await sleep(2000)
|
await sleep(2000)
|
||||||
|
|
@ -1482,6 +1593,9 @@ export default class LineConnection {
|
||||||
return match ? match[1] : null
|
return match ? match[1] : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backup ios to TFTP, after that delete it on flash for free space
|
||||||
|
*/
|
||||||
async backupIos(nameIos: string) {
|
async backupIos(nameIos: string) {
|
||||||
const station = await Station.find(this.config.stationId)
|
const station = await Station.find(this.config.stationId)
|
||||||
if (!station) return
|
if (!station) return
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue