Aplikasi billing & manajemen ISP (fiber optic) — PWA pelanggan, portal teknisi & kolektor, admin panel lengkap, multi-vendor OLT, RADIUS, WhatsApp/Telegram, pembayaran QRIS. Kredit & lisensi (ISC): - Base kode & repo: alijayanet/billing-rtrw oleh Alijaya Net — terima kasih banyak atas base kode dan repo yang bermanfaat. - Add-on: GoWA, LibreSpeed/OpenSpeedTest, Uptime Kuma, notification-forwarder. - Dikembangkan menjadi OPEN-ISP oleh oxpoe — https://github.com/oxpoe/open-isp
1394 lines
42 KiB
JavaScript
1394 lines
42 KiB
JavaScript
const { Client } = require('ssh2');
|
|
const { logger } = require('../config/logger');
|
|
const { getSetting } = require('../config/settingsManager');
|
|
|
|
function sanitizeCliInput(val) {
|
|
if (val === undefined || val === null) return '';
|
|
return String(val).replace(/[\r\n]+/g, ' ').trim();
|
|
}
|
|
|
|
function sanitizeParams(params) {
|
|
const clean = {};
|
|
if (!params || typeof params !== 'object') return clean;
|
|
for (const k of Object.keys(params)) {
|
|
clean[k] = typeof params[k] === 'string'
|
|
? params[k].replace(/[\r\n]+/g, ' ').trim()
|
|
: params[k];
|
|
}
|
|
return clean;
|
|
}
|
|
|
|
/**
|
|
* ONU Provision Service
|
|
* Support untuk:
|
|
* - ZTE C300/C320
|
|
* - Huawei MA5800 Series
|
|
* - Fiberhome AN5516 Series
|
|
* - VSOL V1600/V2400 Series
|
|
* - C-Data FD1600/FD1800 Series
|
|
*/
|
|
|
|
class ONUProvisionService {
|
|
constructor() {
|
|
this.connections = new Map();
|
|
}
|
|
|
|
/**
|
|
* Connect ke OLT via SSH
|
|
*/
|
|
async connectSSH(oltConfig) {
|
|
return new Promise((resolve, reject) => {
|
|
const conn = new Client();
|
|
const timeout = setTimeout(() => {
|
|
conn.end();
|
|
reject(new Error('Connection timeout'));
|
|
}, 30000);
|
|
|
|
conn.on('ready', () => {
|
|
clearTimeout(timeout);
|
|
resolve(conn);
|
|
});
|
|
|
|
conn.on('error', (err) => {
|
|
clearTimeout(timeout);
|
|
reject(err);
|
|
});
|
|
|
|
conn.connect({
|
|
host: oltConfig.host,
|
|
port: oltConfig.port || 22,
|
|
username: oltConfig.username,
|
|
password: oltConfig.password,
|
|
readyTimeout: 30000,
|
|
keepaliveInterval: 10000
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Execute multiple commands sequentially on a single SSH interactive shell stream
|
|
*/
|
|
async executeCommands(conn, commands, promptPattern = /#|>/) {
|
|
return new Promise((resolve, reject) => {
|
|
conn.shell((err, stream) => {
|
|
if (err) return reject(err);
|
|
|
|
let output = '';
|
|
let cmdIndex = 0;
|
|
const cmdList = Array.isArray(commands) ? [...commands] : [commands];
|
|
|
|
const timeout = setTimeout(() => {
|
|
stream.end();
|
|
reject(new Error('SSH command sequence timeout'));
|
|
}, 60000);
|
|
|
|
const writeNext = () => {
|
|
if (cmdIndex < cmdList.length) {
|
|
const rawCmd = cmdList[cmdIndex++];
|
|
// Defense-in-depth: strip newlines from commands to make sure no multiline command gets injected
|
|
const cmd = sanitizeCliInput(rawCmd);
|
|
logger.info(`SSH command sent: ${cmd}`);
|
|
stream.write(cmd + '\n');
|
|
} else {
|
|
clearTimeout(timeout);
|
|
stream.end();
|
|
}
|
|
};
|
|
|
|
let buffer = '';
|
|
stream.on('data', (data) => {
|
|
const chunk = data.toString();
|
|
output += chunk;
|
|
buffer += chunk;
|
|
|
|
const lines = buffer.split('\n');
|
|
const lastLine = lines[lines.length - 1].trim();
|
|
|
|
// If the last line matches the OLT prompt pattern, send the next command
|
|
if (promptPattern.test(lastLine)) {
|
|
buffer = '';
|
|
setTimeout(writeNext, 50);
|
|
}
|
|
});
|
|
|
|
stream.on('close', () => {
|
|
clearTimeout(timeout);
|
|
resolve(output);
|
|
});
|
|
|
|
stream.stderr.on('data', (data) => {
|
|
logger.error('SSH stderr:', data.toString());
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* ZTE C300/C320 - Get unconfigured ONUs
|
|
*/
|
|
async zteGetUnconfiguredONUs(oltConfig, pon) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
const cleanPon = sanitizeCliInput(pon);
|
|
|
|
const commands = [
|
|
'enable',
|
|
`show gpon onu uncfg gpon-olt_${cleanPon}`
|
|
];
|
|
|
|
const output = await this.executeCommands(conn, commands);
|
|
const onus = this.parseZTEUnconfiguredONUs(output);
|
|
conn.end();
|
|
|
|
return onus;
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
logger.error('ZTE get unconfigured ONUs error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse ZTE unconfigured ONUs output robustly
|
|
*/
|
|
parseZTEUnconfiguredONUs(output) {
|
|
const onus = [];
|
|
const lines = output.split('\n');
|
|
|
|
for (const line of lines) {
|
|
const match = line.match(/gpon-onu_(\d+\/\d+\/\d+):(\d+)\s+(.+)/);
|
|
if (match) {
|
|
const pon = match[1];
|
|
const onuId = match[2];
|
|
const rest = match[3].trim().split(/\s+/);
|
|
|
|
let sn = '';
|
|
let model = 'Unknown';
|
|
|
|
if (rest.length >= 2) {
|
|
const isSn = (str) => /^[A-Z]{4}[0-9A-F]{8}$/i.test(str) || /^[0-9A-F]{12}$/i.test(str);
|
|
|
|
if (isSn(rest[0])) {
|
|
sn = rest[0];
|
|
model = rest[1] || 'Unknown';
|
|
} else if (isSn(rest[1])) {
|
|
sn = rest[1];
|
|
model = rest[0];
|
|
} else {
|
|
sn = rest[1] || rest[0];
|
|
model = rest[0];
|
|
}
|
|
} else if (rest.length === 1) {
|
|
sn = rest[0];
|
|
}
|
|
|
|
if (sn && sn.toLowerCase() !== 'sn-auth') {
|
|
onus.push({
|
|
pon,
|
|
onuId,
|
|
model,
|
|
sn,
|
|
vendor: 'ZTE'
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return onus;
|
|
}
|
|
|
|
/**
|
|
* ZTE C300/C320 - Provision ONU
|
|
*/
|
|
async zteProvisionONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
|
|
const { pon, onuId, sn, name, vlan, bandwidth, wifiSsid, wifiPassword, lanMode, pppoeUsername, pppoePassword, tr069AcsUrl, tr069AcsUsername, tr069AcsPassword, tr069PeriodicInform } = params;
|
|
|
|
const cmds = [
|
|
'enable',
|
|
'configure terminal',
|
|
`interface gpon-olt_${pon}`,
|
|
`onu ${onuId} type ${params.onuType || 'F660'} sn ${sn}`,
|
|
'exit',
|
|
`interface gpon-onu_${pon}:${onuId}`,
|
|
`name ${name}`,
|
|
`tcont 1 profile ${bandwidth || 'default'}`,
|
|
'gemport 1 tcont 1'
|
|
];
|
|
|
|
if (wifiSsid && wifiPassword) {
|
|
cmds.push(`ssid 1 ${wifiSsid}`);
|
|
cmds.push(`security 1 wpa2-psk AES ${wifiPassword}`);
|
|
cmds.push('wifi enable 1');
|
|
}
|
|
|
|
if (lanMode) {
|
|
cmds.push(`lan-mode ${lanMode}`);
|
|
}
|
|
|
|
if (lanMode === 'router' && pppoeUsername && pppoePassword) {
|
|
cmds.push(`wan-ip pppoe username ${pppoeUsername} password ${pppoePassword}`);
|
|
}
|
|
|
|
if (tr069AcsUrl) {
|
|
cmds.push('tr069 enable');
|
|
cmds.push(`tr069 acs url ${tr069AcsUrl}`);
|
|
if (tr069AcsUsername && tr069AcsPassword) {
|
|
cmds.push(`tr069 acs username ${tr069AcsUsername}`);
|
|
cmds.push(`tr069 acs password ${tr069AcsPassword}`);
|
|
}
|
|
if (tr069PeriodicInform) {
|
|
cmds.push(`tr069 periodic-inform interval ${tr069PeriodicInform}`);
|
|
cmds.push('tr069 periodic-inform enable');
|
|
}
|
|
}
|
|
|
|
cmds.push('exit');
|
|
cmds.push(`pon-onu-mng gpon-onu_${pon}:${onuId}`);
|
|
cmds.push(`service 1 gemport 1 vlan ${vlan}`);
|
|
cmds.push(`vlan port eth_0/1 mode tag vlan ${vlan}`);
|
|
cmds.push('exit');
|
|
cmds.push('write');
|
|
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
|
|
const features = [];
|
|
if (wifiSsid) features.push('WiFi');
|
|
if (lanMode) features.push(`LAN Mode: ${lanMode}`);
|
|
if (tr069AcsUrl) features.push('TR069');
|
|
|
|
return {
|
|
success: true,
|
|
message: `ONU provisioned successfully${features.length > 0 ? ' with ' + features.join(', ') : ''}`
|
|
};
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
logger.error('ZTE provision ONU error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* HSGQ / HIOSO - Get unconfigured ONUs
|
|
*/
|
|
async hsgqGetUnconfiguredONUs(oltConfig, pon) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
const cleanPon = sanitizeCliInput(pon);
|
|
const ponLower = String(cleanPon || '').toLowerCase();
|
|
const isEpon = ponLower.includes('epon');
|
|
|
|
const commands = ['enable'];
|
|
if (isEpon) {
|
|
commands.push(`show onu unregister`);
|
|
} else {
|
|
const ponInterface = ponLower.includes('gpon') ? cleanPon : `gpon-olt_${cleanPon}`;
|
|
commands.push(`show gpon onu uncfg ${ponInterface}`);
|
|
}
|
|
|
|
const output = await this.executeCommands(conn, commands);
|
|
const onus = this.parseHSGQUnconfiguredONUs(output, cleanPon, oltConfig.vendor);
|
|
conn.end();
|
|
|
|
return onus;
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
logger.error(`${oltConfig.vendor} get unconfigured ONUs error:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse HSGQ/HIOSO unconfigured ONUs output
|
|
*/
|
|
parseHSGQUnconfiguredONUs(output, pon, vendor) {
|
|
const onus = [];
|
|
const lines = output.split('\n');
|
|
|
|
for (const line of lines) {
|
|
const macMatch = line.match(/([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})|([0-9A-Fa-f]{12})/);
|
|
const snMatch = line.match(/([A-Z]{4}[0-9A-F]{8})/i);
|
|
|
|
if (macMatch || snMatch) {
|
|
const sn = snMatch ? snMatch[1] : macMatch[0];
|
|
const onuIdMatch = line.match(/(?:onu|index)\s*(\d+)/i) || line.match(/^\s*(\d+)/);
|
|
const onuId = onuIdMatch ? onuIdMatch[1] : (onus.length + 1).toString();
|
|
|
|
onus.push({
|
|
pon,
|
|
onuId,
|
|
model: 'Unknown',
|
|
sn,
|
|
vendor: vendor || 'HSGQ'
|
|
});
|
|
}
|
|
}
|
|
|
|
return onus;
|
|
}
|
|
|
|
/**
|
|
* HSGQ OLT - Provision ONU (Supports EPON & GPON)
|
|
*/
|
|
async hsgqProvisionONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
|
|
const { pon, onuId, sn, name, vlan, bandwidth, wifiSsid, wifiPassword, lanMode, pppoeUsername, pppoePassword, tr069AcsUrl, tr069AcsUsername, tr069AcsPassword, tr069PeriodicInform } = params;
|
|
|
|
const ponLower = String(pon || '').toLowerCase();
|
|
const isEpon = ponLower.includes('epon');
|
|
|
|
const cmds = [
|
|
'enable',
|
|
'config'
|
|
];
|
|
|
|
if (isEpon) {
|
|
cmds.push(`interface ${pon}`);
|
|
cmds.push(`onu add ${onuId} mac ${sn}`);
|
|
cmds.push(`onu ${onuId} name ${name}`);
|
|
if (vlan) {
|
|
cmds.push(`onu ${onuId} vlan mode tag vlan ${vlan}`);
|
|
}
|
|
} else {
|
|
const ponInterface = ponLower.includes('gpon') ? pon : `gpon-olt_${pon}`;
|
|
cmds.push(`interface ${ponInterface}`);
|
|
cmds.push(`onu ${onuId} type ${params.onuType || 'HSGQ-ONU'} sn ${sn}`);
|
|
cmds.push('exit');
|
|
const onuInterface = ponLower.includes('gpon') ? `${pon}:${onuId}` : `gpon-onu_${pon}:${onuId}`;
|
|
cmds.push(`interface ${onuInterface}`);
|
|
cmds.push(`name ${name}`);
|
|
cmds.push(`tcont 1 profile ${bandwidth || 'default'}`);
|
|
cmds.push('gemport 1 tcont 1');
|
|
cmds.push('exit');
|
|
cmds.push(`pon-onu-mng ${onuInterface}`);
|
|
cmds.push(`service 1 gemport 1 vlan ${vlan}`);
|
|
cmds.push(`vlan port eth_0/1 mode tag vlan ${vlan}`);
|
|
cmds.push('exit');
|
|
}
|
|
|
|
cmds.push('write');
|
|
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
|
|
const features = [];
|
|
if (wifiSsid) features.push('WiFi');
|
|
if (lanMode) features.push(`LAN Mode: ${lanMode}`);
|
|
if (tr069AcsUrl) features.push('TR069');
|
|
|
|
return {
|
|
success: true,
|
|
message: `ONU provisioned successfully on HSGQ${features.length > 0 ? ' with ' + features.join(', ') : ''}`
|
|
};
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
logger.error('HSGQ provision ONU error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* HIOSO OLT - Provision ONU (Supports EPON & GPON)
|
|
*/
|
|
async hiosoProvisionONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
|
|
const { pon, onuId, sn, name, vlan, bandwidth, wifiSsid, wifiPassword, lanMode, pppoeUsername, pppoePassword, tr069AcsUrl, tr069AcsUsername, tr069AcsPassword, tr069PeriodicInform } = params;
|
|
|
|
const ponLower = String(pon || '').toLowerCase();
|
|
const isEpon = ponLower.includes('epon') || (!ponLower.includes('gpon'));
|
|
|
|
const cmds = [
|
|
'enable',
|
|
'config'
|
|
];
|
|
|
|
if (isEpon) {
|
|
const epInterface = ponLower.includes('epon') ? pon : `epon 0/${pon}`;
|
|
cmds.push(`interface ${epInterface}`);
|
|
cmds.push(`onu add ${onuId} mac ${sn}`);
|
|
cmds.push(`onu ${onuId} name ${name}`);
|
|
if (vlan) {
|
|
cmds.push(`onu ${onuId} vlan mode tag vlan ${vlan}`);
|
|
}
|
|
} else {
|
|
const gpInterface = ponLower.includes('gpon') ? pon : `gpon-olt_1/${pon}`;
|
|
cmds.push(`interface ${gpInterface}`);
|
|
cmds.push(`onu ${onuId} type ${params.onuType || 'HIOSO-ONU'} sn ${sn}`);
|
|
cmds.push('exit');
|
|
const onuInterface = ponLower.includes('gpon') ? `${pon}:${onuId}` : `gpon-onu_1/${pon}:${onuId}`;
|
|
cmds.push(`interface ${onuInterface}`);
|
|
cmds.push(`name ${name}`);
|
|
cmds.push(`tcont 1 profile ${bandwidth || 'default'}`);
|
|
cmds.push('gemport 1 tcont 1');
|
|
cmds.push('exit');
|
|
cmds.push(`pon-onu-mng ${onuInterface}`);
|
|
cmds.push(`service 1 gemport 1 vlan ${vlan}`);
|
|
cmds.push(`vlan port eth_0/1 mode tag vlan ${vlan}`);
|
|
cmds.push('exit');
|
|
}
|
|
|
|
cmds.push('write');
|
|
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
|
|
const features = [];
|
|
if (wifiSsid) features.push('WiFi');
|
|
if (lanMode) features.push(`LAN Mode: ${lanMode}`);
|
|
if (tr069AcsUrl) features.push('TR069');
|
|
|
|
return {
|
|
success: true,
|
|
message: `ONU provisioned successfully on HIOSO${features.length > 0 ? ' with ' + features.join(', ') : ''}`
|
|
};
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
logger.error('HIOSO provision ONU error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Huawei MA5800 - Get unconfigured ONUs
|
|
*/
|
|
async huaweiGetUnconfiguredONUs(oltConfig, frame, slot, pon) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
const cleanFrame = sanitizeCliInput(frame);
|
|
const cleanSlot = sanitizeCliInput(slot);
|
|
const cleanPon = sanitizeCliInput(pon);
|
|
|
|
const commands = [
|
|
'enable',
|
|
`display ont autofind ${cleanFrame}/${cleanSlot}/${cleanPon}`
|
|
];
|
|
|
|
const output = await this.executeCommands(conn, commands);
|
|
const onus = this.parseHuaweiUnconfiguredONUs(output, cleanFrame, cleanSlot, cleanPon);
|
|
conn.end();
|
|
|
|
return onus;
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
logger.error('Huawei get unconfigured ONUs error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse Huawei unconfigured ONUs output robustly (handles both block and table formats)
|
|
*/
|
|
parseHuaweiUnconfiguredONUs(output, frame, slot, pon) {
|
|
const onus = [];
|
|
|
|
if (output.includes('Ont SN') || output.includes('Slot/Port')) {
|
|
const blocks = output.split(/----------------------------------------------------------------------/);
|
|
for (const block of blocks) {
|
|
const snMatch = block.match(/Ont\s+SN\s*:\s*(\S+)/i);
|
|
if (snMatch) {
|
|
let sn = snMatch[1];
|
|
sn = sn.split('(')[0].trim();
|
|
|
|
const portMatch = block.match(/Slot\/Port\s*:\s*(\d+\/\d+\/\d+)/i) || block.match(/Slot\/Port\s*:\s*(\d+\/\d+)/i);
|
|
let blockPon = pon;
|
|
let blockSlot = slot;
|
|
let blockFrame = frame;
|
|
|
|
if (portMatch) {
|
|
const parts = portMatch[1].split('/');
|
|
if (parts.length === 3) {
|
|
blockFrame = parts[0];
|
|
blockSlot = parts[1];
|
|
blockPon = parts[2];
|
|
} else if (parts.length === 2) {
|
|
blockSlot = parts[0];
|
|
blockPon = parts[1];
|
|
}
|
|
}
|
|
|
|
const modelMatch = block.match(/Ont\s+EquipmentID\s*:\s*(\S+)/i) || block.match(/Ont\s+Type\s*:\s*(\S+)/i);
|
|
const model = modelMatch ? modelMatch[1] : 'Unknown';
|
|
|
|
onus.push({
|
|
frame: blockFrame,
|
|
slot: blockSlot,
|
|
pon: blockPon,
|
|
onuId: onus.length.toString(),
|
|
sn,
|
|
model,
|
|
vendor: 'Huawei'
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
const lines = output.split('\n');
|
|
for (const line of lines) {
|
|
const match = line.match(/(?:^|\s)([0-9A-F]{16}|[A-Z]{4}[0-9A-F]{8}|HWTC[0-9A-F]{8})(?:\s|$)/i);
|
|
if (match) {
|
|
const sn = match[1];
|
|
const parts = line.trim().split(/\s+/);
|
|
let blockFrame = frame, blockSlot = slot, blockPon = pon;
|
|
const portPart = parts.find(p => /^\d+\/\d+\/\d+$/.test(p) || /^\d+\/\d+$/.test(p));
|
|
if (portPart) {
|
|
const p = portPart.split('/');
|
|
if (p.length === 3) {
|
|
blockFrame = p[0];
|
|
blockSlot = p[1];
|
|
blockPon = p[2];
|
|
} else if (p.length === 2) {
|
|
blockSlot = p[0];
|
|
blockPon = p[1];
|
|
}
|
|
}
|
|
|
|
onus.push({
|
|
frame: blockFrame,
|
|
slot: blockSlot,
|
|
pon: blockPon,
|
|
onuId: parts[0] || onus.length.toString(),
|
|
sn,
|
|
model: 'Unknown',
|
|
vendor: 'Huawei'
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return onus;
|
|
}
|
|
|
|
/**
|
|
* Huawei MA5800 - Provision ONU
|
|
*/
|
|
async huaweiProvisionONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
|
|
const { frame, slot, pon, onuId, sn, name, vlan, bandwidth, lineProfile, srvProfile, wifiSsid, wifiPassword, lanMode, pppoeUsername, pppoePassword, tr069AcsUrl, tr069AcsUsername, tr069AcsPassword, tr069PeriodicInform } = params;
|
|
|
|
const cmds = [
|
|
'enable',
|
|
'config',
|
|
`interface gpon ${frame}/${slot}`,
|
|
`ont add ${pon} ${onuId} sn-auth ${sn} omci ont-lineprofile-id ${lineProfile || 1} ont-srvprofile-id ${srvProfile || 1} desc ${name}`
|
|
];
|
|
|
|
if (wifiSsid && wifiPassword) {
|
|
cmds.push(`ont wifi-config ${pon} ${onuId} ssid ${wifiSsid} wpa-psk ${wifiPassword}`);
|
|
}
|
|
|
|
if (lanMode) {
|
|
cmds.push(`ont port native-vlan ${pon} ${onuId} eth 1 vlan ${vlan} priority 0`);
|
|
}
|
|
|
|
if (lanMode === 'router' && pppoeUsername && pppoePassword) {
|
|
cmds.push(`ont wan-config ${pon} ${onuId} pppoe username ${pppoeUsername} password ${pppoePassword}`);
|
|
}
|
|
|
|
if (tr069AcsUrl) {
|
|
cmds.push(`ont tr069-server ${pon} ${onuId} url ${tr069AcsUrl}`);
|
|
if (tr069AcsUsername && tr069AcsPassword) {
|
|
cmds.push(`ont tr069-server ${pon} ${onuId} username ${tr069AcsUsername} password ${tr069AcsPassword}`);
|
|
}
|
|
if (tr069PeriodicInform) {
|
|
cmds.push(`ont tr069-server ${pon} ${onuId} periodic-inform interval ${tr069PeriodicInform}`);
|
|
cmds.push(`ont tr069-server ${pon} ${onuId} periodic-inform enable`);
|
|
}
|
|
cmds.push(`ont tr069-server ${pon} ${onuId} enable`);
|
|
}
|
|
|
|
cmds.push('quit');
|
|
cmds.push(`service-port vlan ${vlan} gpon ${frame}/${slot}/${pon} ont ${onuId} gemport 1 multi-service user-vlan ${vlan}`);
|
|
cmds.push('save');
|
|
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
|
|
const features = [];
|
|
if (wifiSsid) features.push('WiFi');
|
|
if (lanMode) features.push(`LAN Mode: ${lanMode}`);
|
|
if (tr069AcsUrl) features.push('TR069');
|
|
|
|
return {
|
|
success: true,
|
|
message: `ONU provisioned successfully${features.length > 0 ? ' with ' + features.join(', ') : ''}`
|
|
};
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
logger.error('Huawei provision ONU error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fiberhome AN5516 - Provision ONU
|
|
*/
|
|
async fiberhomeProvisionONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
|
|
const { frame, slot, pon, onuId, sn, name, vlan, lineProfile, srvProfile, wifiSsid, wifiPassword, lanMode, pppoeUsername, pppoePassword, tr069AcsUrl, tr069AcsUsername, tr069AcsPassword, tr069PeriodicInform } = params;
|
|
|
|
const cmds = [
|
|
'enable',
|
|
'config',
|
|
`interface gpon ${frame}/${slot}`,
|
|
`ont add ${pon} ${onuId} sn-auth ${sn} ont-lineprofile-id ${lineProfile || 1} ont-srvprofile-id ${srvProfile || 1} desc ${name}`
|
|
];
|
|
|
|
if (wifiSsid && wifiPassword) {
|
|
cmds.push(`ont wlan ssid ${pon} ${onuId} 1 ${wifiSsid}`);
|
|
cmds.push(`ont wlan security ${pon} ${onuId} 1 wpa2-psk aes ${wifiPassword}`);
|
|
cmds.push(`ont wlan enable ${pon} ${onuId} 1`);
|
|
}
|
|
|
|
if (lanMode) {
|
|
cmds.push(`ont port vlan ${pon} ${onuId} eth 1 mode tag vlan ${vlan}`);
|
|
}
|
|
|
|
if (lanMode === 'router' && pppoeUsername && pppoePassword) {
|
|
cmds.push(`ont wan ${pon} ${onuId} pppoe username ${pppoeUsername} password ${pppoePassword}`);
|
|
}
|
|
|
|
if (tr069AcsUrl) {
|
|
cmds.push(`ont cwmp ${pon} ${onuId} acs-url ${tr069AcsUrl}`);
|
|
if (tr069AcsUsername && tr069AcsPassword) {
|
|
cmds.push(`ont cwmp ${pon} ${onuId} acs-username ${tr069AcsUsername}`);
|
|
cmds.push(`ont cwmp ${pon} ${onuId} acs-password ${tr069AcsPassword}`);
|
|
}
|
|
if (tr069PeriodicInform) {
|
|
cmds.push(`ont cwmp ${pon} ${onuId} periodic-inform-interval ${tr069PeriodicInform}`);
|
|
cmds.push(`ont cwmp ${pon} ${onuId} periodic-inform enable`);
|
|
}
|
|
cmds.push(`ont cwmp ${pon} ${onuId} enable`);
|
|
}
|
|
|
|
cmds.push('quit');
|
|
cmds.push(`service-port ${vlan} vlan ${vlan} gpon ${frame}/${slot}/${pon} ont ${onuId} gemport 1 multi-service user-vlan ${vlan}`);
|
|
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
|
|
const features = [];
|
|
if (wifiSsid) features.push('WiFi');
|
|
if (lanMode) features.push(`LAN Mode: ${lanMode}`);
|
|
if (tr069AcsUrl) features.push('TR069');
|
|
|
|
return {
|
|
success: true,
|
|
message: `ONU provisioned successfully${features.length > 0 ? ' with ' + features.join(', ') : ''}`
|
|
};
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
logger.error('Fiberhome provision ONU error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* VSOL V1600/V2400 - Provision ONU
|
|
*/
|
|
async vsolProvisionONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
|
|
const { pon, onuId, sn, name, vlan, bandwidth, wifiSsid, wifiPassword, lanMode, pppoeUsername, pppoePassword, tr069AcsUrl, tr069AcsUsername, tr069AcsPassword, tr069PeriodicInform } = params;
|
|
|
|
const cmds = [
|
|
'enable',
|
|
'configure terminal',
|
|
`interface gpon-olt_${pon}`,
|
|
`onu ${onuId} type ${params.onuType || 'V2802RGW'} sn ${sn}`,
|
|
'exit',
|
|
`interface gpon-onu_${pon}:${onuId}`,
|
|
`name ${name}`,
|
|
`tcont 1 profile ${bandwidth || 'default'}`,
|
|
'gemport 1 tcont 1'
|
|
];
|
|
|
|
if (wifiSsid && wifiPassword) {
|
|
cmds.push(`ssid 1 ${wifiSsid}`);
|
|
cmds.push(`security 1 wpa2-psk AES ${wifiPassword}`);
|
|
cmds.push('wifi enable 1');
|
|
}
|
|
|
|
if (lanMode) {
|
|
cmds.push(`lan-mode ${lanMode}`);
|
|
}
|
|
|
|
if (lanMode === 'router' && pppoeUsername && pppoePassword) {
|
|
cmds.push(`wan-ip pppoe username ${pppoeUsername} password ${pppoePassword}`);
|
|
}
|
|
|
|
if (tr069AcsUrl) {
|
|
cmds.push('tr069 enable');
|
|
cmds.push(`tr069 acs url ${tr069AcsUrl}`);
|
|
if (tr069AcsUsername && tr069AcsPassword) {
|
|
cmds.push(`tr069 acs username ${tr069AcsUsername}`);
|
|
cmds.push(`tr069 acs password ${tr069AcsPassword}`);
|
|
}
|
|
if (tr069PeriodicInform) {
|
|
cmds.push(`tr069 periodic-inform interval ${tr069PeriodicInform}`);
|
|
cmds.push('tr069 periodic-inform enable');
|
|
}
|
|
}
|
|
|
|
cmds.push('exit');
|
|
cmds.push(`pon-onu-mng gpon-onu_${pon}:${onuId}`);
|
|
cmds.push(`service 1 gemport 1 vlan ${vlan}`);
|
|
cmds.push(`vlan port eth_0/1 mode tag vlan ${vlan}`);
|
|
cmds.push('exit');
|
|
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
|
|
const features = [];
|
|
if (wifiSsid) features.push('WiFi');
|
|
if (lanMode) features.push(`LAN Mode: ${lanMode}`);
|
|
if (tr069AcsUrl) features.push('TR069');
|
|
|
|
return {
|
|
success: true,
|
|
message: `ONU provisioned successfully${features.length > 0 ? ' with ' + features.join(', ') : ''}`
|
|
};
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
logger.error('VSOL provision ONU error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* C-Data FD1600/FD1800 - Provision ONU
|
|
*/
|
|
async cdataProvisionONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
|
|
const { pon, onuId, sn, name, vlan, bandwidth, wifiSsid, wifiPassword, lanMode, pppoeUsername, pppoePassword, tr069AcsUrl, tr069AcsUsername, tr069AcsPassword, tr069PeriodicInform } = params;
|
|
|
|
const cmds = [
|
|
'enable',
|
|
'configure terminal',
|
|
`interface gpon-olt_${pon}`,
|
|
`onu ${onuId} type ${params.onuType || 'FD1104S'} sn ${sn}`,
|
|
'exit',
|
|
`interface gpon-onu_${pon}:${onuId}`,
|
|
`name ${name}`,
|
|
`tcont 1 profile ${bandwidth || 'default'}`,
|
|
'gemport 1 tcont 1'
|
|
];
|
|
|
|
if (wifiSsid && wifiPassword) {
|
|
cmds.push(`ssid 1 ${wifiSsid}`);
|
|
cmds.push(`security 1 wpa2-psk AES ${wifiPassword}`);
|
|
cmds.push('wifi enable 1');
|
|
}
|
|
|
|
if (lanMode) {
|
|
cmds.push(`lan-mode ${lanMode}`);
|
|
}
|
|
|
|
if (lanMode === 'router' && pppoeUsername && pppoePassword) {
|
|
cmds.push(`wan-ip pppoe username ${pppoeUsername} password ${pppoePassword}`);
|
|
}
|
|
|
|
if (tr069AcsUrl) {
|
|
cmds.push('cwmp enable');
|
|
cmds.push(`cwmp acs url ${tr069AcsUrl}`);
|
|
if (tr069AcsUsername && tr069AcsPassword) {
|
|
cmds.push(`cwmp acs username ${tr069AcsUsername}`);
|
|
cmds.push(`cwmp acs password ${tr069AcsPassword}`);
|
|
}
|
|
if (tr069PeriodicInform) {
|
|
cmds.push(`cwmp periodic-inform interval ${tr069PeriodicInform}`);
|
|
cmds.push('cwmp periodic-inform enable');
|
|
}
|
|
}
|
|
|
|
cmds.push('exit');
|
|
cmds.push(`pon-onu-mng gpon-onu_${pon}:${onuId}`);
|
|
cmds.push(`service 1 gemport 1 vlan ${vlan}`);
|
|
cmds.push(`vlan port eth_0/1 mode tag vlan ${vlan}`);
|
|
cmds.push('exit');
|
|
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
|
|
const features = [];
|
|
if (wifiSsid) features.push('WiFi');
|
|
if (lanMode) features.push(`LAN Mode: ${lanMode}`);
|
|
if (tr069AcsUrl) features.push('TR069');
|
|
|
|
return {
|
|
success: true,
|
|
message: `ONU provisioned successfully${features.length > 0 ? ' with ' + features.join(', ') : ''}`
|
|
};
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
logger.error('C-Data provision ONU error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get ONU details (works for both ZTE and Huawei)
|
|
*/
|
|
async getONUDetails(oltConfig, vendor, params) {
|
|
if (vendor === 'ZTE') {
|
|
return this.zteGetONUDetails(oltConfig, params);
|
|
} else if (vendor === 'Huawei') {
|
|
return this.huaweiGetONUDetails(oltConfig, params);
|
|
}
|
|
throw new Error('Unsupported vendor');
|
|
}
|
|
|
|
/**
|
|
* ZTE - Get ONU details
|
|
*/
|
|
async zteGetONUDetails(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
const { pon, onuId } = params;
|
|
const cmds = [
|
|
'enable',
|
|
`show gpon onu detail-info gpon-onu_${pon}:${onuId}`
|
|
];
|
|
const output = await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
|
|
return this.parseZTEONUDetails(output);
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse ZTE ONU details
|
|
*/
|
|
parseZTEONUDetails(output) {
|
|
const details = {};
|
|
const lines = output.split('\n');
|
|
|
|
for (const line of lines) {
|
|
if (line.includes('Name:')) {
|
|
details.name = line.split(':')[1]?.trim();
|
|
}
|
|
if (line.includes('SN:')) {
|
|
details.sn = line.split(':')[1]?.trim();
|
|
}
|
|
if (line.includes('Status:')) {
|
|
details.status = line.split(':')[1]?.trim();
|
|
}
|
|
if (line.includes('RX Power:')) {
|
|
details.rxPower = line.split(':')[1]?.trim();
|
|
}
|
|
}
|
|
|
|
return details;
|
|
}
|
|
|
|
/**
|
|
* Huawei - Get ONU details
|
|
*/
|
|
async huaweiGetONUDetails(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
const { frame, slot, pon, onuId } = params;
|
|
const cmds = [
|
|
'enable',
|
|
`display ont info ${frame} ${slot} ${pon} ${onuId}`
|
|
];
|
|
const output = await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
|
|
return this.parseHuaweiONUDetails(output);
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse Huawei ONU details
|
|
*/
|
|
parseHuaweiONUDetails(output) {
|
|
const details = {};
|
|
const lines = output.split('\n');
|
|
|
|
for (const line of lines) {
|
|
if (line.includes('Description')) {
|
|
details.name = line.split(':')[1]?.trim();
|
|
}
|
|
if (line.includes('SN')) {
|
|
details.sn = line.split(':')[1]?.trim();
|
|
}
|
|
if (line.includes('Run state')) {
|
|
details.status = line.split(':')[1]?.trim();
|
|
}
|
|
if (line.includes('Rx optical power')) {
|
|
details.rxPower = line.split(':')[1]?.trim();
|
|
}
|
|
}
|
|
|
|
return details;
|
|
}
|
|
|
|
/**
|
|
* Delete ONU
|
|
*/
|
|
async deleteONU(oltConfig, vendor, params) {
|
|
params = sanitizeParams(params);
|
|
const v = String(vendor || '').toUpperCase();
|
|
if (v === 'ZTE') {
|
|
return this.zteDeleteONU(oltConfig, params);
|
|
} else if (v === 'HUAWEI') {
|
|
return this.huaweiDeleteONU(oltConfig, params);
|
|
} else if (v === 'FIBERHOME') {
|
|
return this.fiberhomeDeleteONU(oltConfig, params);
|
|
} else if (v === 'VSOL') {
|
|
return this.vsolDeleteONU(oltConfig, params);
|
|
} else if (v === 'CDATA') {
|
|
return this.cdataDeleteONU(oltConfig, params);
|
|
} else if (v === 'HSGQ') {
|
|
return this.hsgqDeleteONU(oltConfig, params);
|
|
} else if (v === 'HIOSO') {
|
|
return this.hiosoDeleteONU(oltConfig, params);
|
|
}
|
|
throw new Error(`Unsupported vendor: ${vendor}`);
|
|
}
|
|
|
|
/**
|
|
* ZTE - Delete ONU
|
|
*/
|
|
async zteDeleteONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
const { pon, onuId } = params;
|
|
const cmds = [
|
|
'enable',
|
|
'configure terminal',
|
|
`interface gpon-olt_${pon}`,
|
|
`no onu ${onuId}`,
|
|
'exit',
|
|
'write'
|
|
];
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
return { success: true, message: 'ONU deleted successfully' };
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Huawei - Delete ONU
|
|
*/
|
|
async huaweiDeleteONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
const { frame, slot, pon, onuId } = params;
|
|
const cmds = [
|
|
'enable',
|
|
'config',
|
|
`interface gpon ${frame}/${slot}`,
|
|
`ont delete ${pon} ${onuId}`,
|
|
'quit',
|
|
'save'
|
|
];
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
return { success: true, message: 'ONU deleted successfully' };
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fiberhome - Delete ONU
|
|
*/
|
|
async fiberhomeDeleteONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
const { frame, slot, pon, onuId } = params;
|
|
const cmds = [
|
|
'enable',
|
|
'config',
|
|
`interface gpon ${frame}/${slot}`,
|
|
`ont delete ${pon} ${onuId}`,
|
|
'quit'
|
|
];
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
return { success: true, message: 'ONU deleted successfully on Fiberhome' };
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* VSOL - Delete ONU
|
|
*/
|
|
async vsolDeleteONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
const { pon, onuId } = params;
|
|
const cmds = [
|
|
'enable',
|
|
'configure terminal',
|
|
`interface gpon-olt_${pon}`,
|
|
`no onu ${onuId}`,
|
|
'exit'
|
|
];
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
return { success: true, message: 'ONU deleted successfully on VSOL' };
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* C-Data - Delete ONU
|
|
*/
|
|
async cdataDeleteONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
const { pon, onuId } = params;
|
|
const cmds = [
|
|
'enable',
|
|
'configure terminal',
|
|
`interface gpon-olt_${pon}`,
|
|
`no onu ${onuId}`,
|
|
'exit'
|
|
];
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
return { success: true, message: 'ONU deleted successfully on C-Data' };
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* HSGQ - Delete ONU (Supports EPON & GPON)
|
|
*/
|
|
async hsgqDeleteONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
const { pon, onuId } = params;
|
|
const ponLower = String(pon || '').toLowerCase();
|
|
const isEpon = ponLower.includes('epon');
|
|
|
|
const cmds = [
|
|
'enable',
|
|
'config'
|
|
];
|
|
|
|
if (isEpon) {
|
|
cmds.push(`interface ${pon}`);
|
|
cmds.push(`no onu ${onuId}`);
|
|
} else {
|
|
const ponInterface = ponLower.includes('gpon') ? pon : `gpon-olt_${pon}`;
|
|
cmds.push(`interface ${ponInterface}`);
|
|
cmds.push(`no onu ${onuId}`);
|
|
}
|
|
cmds.push('exit');
|
|
cmds.push('write');
|
|
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
return { success: true, message: 'ONU deleted successfully on HSGQ' };
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* HIOSO - Delete ONU (Supports EPON & GPON)
|
|
*/
|
|
async hiosoDeleteONU(oltConfig, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
const { pon, onuId } = params;
|
|
const ponLower = String(pon || '').toLowerCase();
|
|
const isEpon = ponLower.includes('epon') || (!ponLower.includes('gpon'));
|
|
|
|
const cmds = [
|
|
'enable',
|
|
'config'
|
|
];
|
|
|
|
if (isEpon) {
|
|
const epInterface = ponLower.includes('epon') ? pon : `epon 0/${pon}`;
|
|
cmds.push(`interface ${epInterface}`);
|
|
cmds.push(`no onu ${onuId}`);
|
|
} else {
|
|
const gpInterface = ponLower.includes('gpon') ? pon : `gpon-olt_1/${pon}`;
|
|
cmds.push(`interface ${gpInterface}`);
|
|
cmds.push(`no onu ${onuId}`);
|
|
}
|
|
cmds.push('exit');
|
|
cmds.push('write');
|
|
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
return { success: true, message: 'ONU deleted successfully on HIOSO' };
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create PPPoE Secret in MikroTik
|
|
*/
|
|
async createMikrotikPPPoE(mikrotikConfig, params) {
|
|
const mikrotikService = require('./mikrotikService');
|
|
|
|
try {
|
|
const { username, password, profile, comment, localAddress, remoteAddress } = params;
|
|
|
|
const secretData = {
|
|
name: username,
|
|
password: password,
|
|
service: 'pppoe',
|
|
profile: profile || 'default'
|
|
};
|
|
|
|
if (comment) {
|
|
secretData.comment = comment;
|
|
}
|
|
|
|
if (localAddress) {
|
|
secretData['local-address'] = localAddress;
|
|
}
|
|
|
|
if (remoteAddress) {
|
|
secretData['remote-address'] = remoteAddress;
|
|
}
|
|
|
|
await mikrotikService.addPppoeSecret(secretData, null);
|
|
|
|
logger.info(`PPPoE secret created in MikroTik: ${username}`);
|
|
return { success: true, message: 'PPPoE secret created successfully' };
|
|
} catch (error) {
|
|
logger.error('Create MikroTik PPPoE error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete PPPoE Secret from MikroTik
|
|
*/
|
|
async deleteMikrotikPPPoE(mikrotikConfig, username) {
|
|
const mikrotikService = require('./mikrotikService');
|
|
|
|
try {
|
|
const conn = await mikrotikService.connect(mikrotikConfig);
|
|
|
|
const secrets = await conn.write('/ppp/secret/print', [`?name=${username}`]);
|
|
|
|
if (secrets && secrets.length > 0) {
|
|
const secretId = secrets[0]['.id'];
|
|
await conn.write('/ppp/secret/remove', [`=.id=${secretId}`]);
|
|
logger.info(`PPPoE secret deleted from MikroTik: ${username}`);
|
|
}
|
|
|
|
conn.close();
|
|
|
|
return { success: true, message: 'PPPoE secret deleted successfully' };
|
|
} catch (error) {
|
|
logger.error('Delete MikroTik PPPoE error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Full Provision: ONU + MikroTik PPPoE
|
|
*/
|
|
async fullProvision(oltConfig, mikrotikConfig, params) {
|
|
const results = {
|
|
onu: null,
|
|
pppoe: null,
|
|
errors: []
|
|
};
|
|
|
|
try {
|
|
const { vendor } = params;
|
|
|
|
if (vendor === 'ZTE') {
|
|
results.onu = await this.zteProvisionONU(oltConfig, params);
|
|
} else if (vendor === 'Huawei') {
|
|
results.onu = await this.huaweiProvisionONU(oltConfig, params);
|
|
} else if (vendor === 'Fiberhome') {
|
|
results.onu = await this.fiberhomeProvisionONU(oltConfig, params);
|
|
} else if (vendor === 'VSOL') {
|
|
results.onu = await this.vsolProvisionONU(oltConfig, params);
|
|
} else if (vendor === 'CData') {
|
|
results.onu = await this.cdataProvisionONU(oltConfig, params);
|
|
} else if (vendor === 'HSGQ') {
|
|
results.onu = await this.hsgqProvisionONU(oltConfig, params);
|
|
} else if (vendor === 'Hioso') {
|
|
results.onu = await this.hiosoProvisionONU(oltConfig, params);
|
|
} else {
|
|
throw new Error(`Unsupported vendor: ${vendor}`);
|
|
}
|
|
|
|
logger.info(`ONU provisioned: ${params.name} (${vendor})`);
|
|
|
|
if (params.pppoeUsername && params.pppoePassword && mikrotikConfig) {
|
|
try {
|
|
results.pppoe = await this.createMikrotikPPPoE(mikrotikConfig, {
|
|
username: params.pppoeUsername,
|
|
password: params.pppoePassword,
|
|
profile: params.bandwidth || 'default',
|
|
comment: `${params.name} - Auto-provisioned`,
|
|
remoteAddress: params.remoteAddress
|
|
});
|
|
|
|
logger.info(`PPPoE created: ${params.pppoeUsername}`);
|
|
} catch (pppoeError) {
|
|
results.errors.push(`PPPoE creation failed: ${pppoeError.message}`);
|
|
logger.error('PPPoE creation failed:', pppoeError);
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
message: 'Full provisioning completed',
|
|
results
|
|
};
|
|
} catch (error) {
|
|
results.errors.push(`Provisioning failed: ${error.message}`);
|
|
logger.error('Full provision error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reboot ONU via SSH CLI (for ZTE and Huawei)
|
|
*/
|
|
async rebootONU(oltConfig, vendor, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
const { frame, board, port, onuId } = params;
|
|
const cmds = [];
|
|
|
|
if (vendor === 'ZTE') {
|
|
cmds.push('enable');
|
|
cmds.push('configure terminal');
|
|
cmds.push(`pon-onu-mng gpon-onu_1/${board}/${port}:${onuId}`);
|
|
cmds.push('reboot');
|
|
} else if (vendor === 'Huawei') {
|
|
cmds.push('enable');
|
|
cmds.push('config');
|
|
cmds.push(`interface gpon ${frame}/${board}`);
|
|
cmds.push(`ont reset ${port} ${onuId}`);
|
|
cmds.push('quit');
|
|
cmds.push('save');
|
|
} else {
|
|
throw new Error('Unsupported vendor for SSH reboot');
|
|
}
|
|
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
return { success: true, message: 'ONU rebooted successfully' };
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Rename ONU via SSH CLI (for ZTE and Huawei)
|
|
*/
|
|
async renameONU(oltConfig, vendor, params) {
|
|
let conn;
|
|
try {
|
|
conn = await this.connectSSH(oltConfig);
|
|
params = sanitizeParams(params);
|
|
const { frame, board, port, onuId, newName } = params;
|
|
const cmds = [];
|
|
|
|
if (vendor === 'ZTE') {
|
|
cmds.push('enable');
|
|
cmds.push('configure terminal');
|
|
cmds.push(`interface gpon-onu_1/${board}/${port}:${onuId}`);
|
|
cmds.push(`name ${newName}`);
|
|
cmds.push('exit');
|
|
cmds.push('write');
|
|
} else if (vendor === 'Huawei') {
|
|
cmds.push('enable');
|
|
cmds.push('config');
|
|
cmds.push(`interface gpon ${frame}/${board}`);
|
|
cmds.push(`ont name ${port} ${onuId} "${newName}"`);
|
|
cmds.push('quit');
|
|
cmds.push('save');
|
|
} else {
|
|
throw new Error('Unsupported vendor for SSH rename');
|
|
}
|
|
|
|
await this.executeCommands(conn, cmds);
|
|
conn.end();
|
|
return { success: true, message: 'ONU renamed successfully' };
|
|
} catch (error) {
|
|
if (conn) conn.end();
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = new ONUProvisionService();
|
|
|
|
// Made with Bob
|