2017-08-09 22:18:28 +02:00
var express = require ( 'express' ) ;
var path = require ( 'path' ) ;
var favicon = require ( 'serve-favicon' ) ;
var logger = require ( 'morgan' ) ;
var cookieParser = require ( 'cookie-parser' ) ;
var bodyParser = require ( 'body-parser' ) ;
2017-09-06 08:05:34 +02:00
const execSync = require ( 'child_process' ) . execSync ;
2017-08-09 22:18:28 +02:00
var app = express ( ) ;
2017-09-02 08:04:47 +02:00
/* Read Config */
var json _file = require ( 'jsonfile' ) ;
2017-09-02 11:44:39 +02:00
var glass _config = json _file . readFileSync ( 'config/glass_config.json' ) ;
2017-09-02 08:04:47 +02:00
2017-08-09 22:18:28 +02:00
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app . use ( logger ( 'dev' ) ) ;
app . use ( bodyParser . json ( ) ) ;
app . use ( bodyParser . urlencoded ( { extended : false } ) ) ;
app . use ( cookieParser ( ) ) ;
app . use ( express . static ( path . join ( _ _dirname , 'public' ) ) ) ;
/* Normal Web Routes */
app . use ( '/' , require ( './routes/index' ) ) ;
app . use ( '/users' , require ( './routes/users' ) ) ;
app . use ( '/get_stats' , require ( './routes/get_stats' ) ) ;
app . use ( '/dhcp_leases' , require ( './routes/dhcp_leases' ) ) ;
2017-09-02 11:44:39 +02:00
app . use ( '/dhcp_log' , require ( './routes/dhcp_log' ) ) ;
2017-09-02 12:58:59 +02:00
app . use ( '/dhcp_config' , require ( './routes/dhcp_config' ) ) ;
2017-09-02 14:07:30 +02:00
app . use ( '/dhcp_config_snapshots' , require ( './routes/dhcp_config_snapshots' ) ) ;
app . use ( '/dhcp_config_snapshot_view' , require ( './routes/dhcp_config_snapshot_view' ) ) ;
2017-09-02 12:58:59 +02:00
app . use ( '/dhcp_config_save' , require ( './routes/dhcp_config_save' ) ) ;
2017-09-02 15:45:55 +02:00
app . use ( '/dhcp_start_stop_restart' , require ( './routes/dhcp_start_stop_restart' ) ) ;
2017-08-09 22:18:28 +02:00
app . use ( '/api_examples' , require ( './routes/api_examples' ) ) ;
2017-09-02 08:04:47 +02:00
app . use ( '/glass_settings' , require ( './routes/glass_settings' ) ) ;
2017-09-02 16:59:36 +02:00
app . use ( '/glass_alerts' , require ( './routes/glass_alerts' ) ) ;
app . use ( '/glass_alert_settings_save' , require ( './routes/glass_alert_settings_save' ) ) ;
2017-09-02 08:04:47 +02:00
app . use ( '/glass_settings_save' , require ( './routes/glass_settings_save' ) ) ;
2017-08-09 22:18:28 +02:00
/* API Routes */
app . use ( '/api/get_active_leases/' , require ( './api/get_active_leases' ) ) ;
2017-09-02 08:04:47 +02:00
app . use ( '/api/get_subnet_details/' , require ( './api/get_subnet_details' ) ) ;
2017-08-09 22:18:28 +02:00
app . set ( 'view engine' , 'html' ) ;
// catch 404 and forward to error handler
app . use ( function ( req , res , next ) {
2017-09-02 11:44:39 +02:00
var err = new Error ( 'Not Found' ) ;
err . status = 404 ;
next ( err ) ;
2017-08-09 22:18:28 +02:00
} ) ;
// error handler
app . use ( function ( err , req , res , next ) {
2017-09-02 11:44:39 +02:00
// set locals, only providing error in development
res . locals . message = err . message ;
res . locals . error = req . app . get ( 'env' ) === 'development' ? err : { } ;
2017-08-09 22:18:28 +02:00
2017-09-02 11:44:39 +02:00
// render the error page
res . status ( err . status || 500 ) ;
res . send ( err . message ) ;
2017-08-09 22:18:28 +02:00
} ) ;
module . exports = app ;
2017-09-02 11:44:39 +02:00
/ * *
* Global Variables
* /
2017-09-06 08:05:34 +02:00
leases _per _minute = 0 ;
2017-09-02 11:44:39 +02:00
cpu _utilization = 0 ;
total _leases = 0 ;
2017-08-09 22:18:28 +02:00
current _time = 0 ;
leases _per _second = 0 ;
current _leases _per _second = 0 ;
leases _last _update _time = 0 ;
2017-09-02 11:44:39 +02:00
listening _to _log _file = 0 ;
2017-08-09 22:18:28 +02:00
options = { } ;
options . interval = 1000 ;
2017-09-02 11:44:39 +02:00
/ * *
* Ingest Current Lease File
* /
2017-08-09 22:18:28 +02:00
var lease _parser = require ( './lib/lease_parser.js' ) ;
dhcp _lease _data = { } ;
lease _read _buffer = "" ;
fs = require ( 'fs' ) ;
2017-09-02 08:04:47 +02:00
fs . readFile ( glass _config . leases _file , 'utf8' , function ( err , data ) {
2017-08-09 22:18:28 +02:00
if ( err ) {
return console . log ( err ) ;
}
2017-09-02 11:44:39 +02:00
else {
lease _parser . parse ( data ) ;
}
2017-08-09 22:18:28 +02:00
} ) ;
2017-09-02 11:44:39 +02:00
/ * *
* Leases File Listener
* /
2017-08-09 22:18:28 +02:00
var tail _module = require ( 'always-tail' ) ;
tail = new tail _module (
2017-09-02 08:04:47 +02:00
glass _config . leases _file ,
2017-08-09 22:18:28 +02:00
"\n" ,
options
) ;
tail . on ( "line" , function ( data ) {
unix _time = Math . floor ( new Date ( ) / 1000 ) ;
/* Buffering lines until we get full lease data */
lease _read _buffer = lease _read _buffer + data + "\n" ;
/* End of lease - cut off and parse the buffer */
if ( /}/i . test ( data ) ) {
lease _parser . parse ( lease _read _buffer ) ;
lease _read _buffer = "" ;
}
/* Count leases per second */
if ( /lease/ . test ( data ) ) {
leases _per _second ++ ;
}
if ( current _time != unix _time ) {
current _time = unix _time ;
current _leases _per _second = leases _per _second ;
leases _last _update _time = unix _time ;
leases _per _second = 0 ;
}
} ) ;
2017-09-02 11:44:39 +02:00
/ * *
* Watch DHCP Log File
* /
var json _file = require ( 'jsonfile' ) ;
var glass _config = json _file . readFileSync ( 'config/glass_config.json' ) ;
var options = { } ;
options . interval = 1000 ;
2017-08-09 22:18:28 +02:00
2017-09-06 08:05:34 +02:00
var dashboard _timer = setInterval ( function ( ) {
2017-08-09 22:18:28 +02:00
// console.log("Checking timers...");
unix _time = Math . floor ( new Date ( ) / 1000 ) ;
if ( ( unix _time - 5 ) > leases _last _update _time ) {
current _leases _per _second = 0 ;
}
// console.log(JSON.stringify(dhcp_lease_data, null, 2));
} , 5000 ) ;
2017-09-06 08:05:34 +02:00
/ * *
* Calculate leases per minute
* /
var leases _per _minute _data = [ ] ;
var leases _per _minute _counter = 0 ;
leases _per _minute _counter _timer = setInterval ( function ( ) {
// console.log("leases per minute counter %i", leases_per_minute_counter);
leases _per _minute _data [ leases _per _minute _counter ] = current _leases _per _second ;
leases _per _minute _counter ++ ;
/* Count how many actual data sets we walked that have values */
leases _per _minute = 0 ;
for ( i = 0 ; i < 59 ; i ++ ) {
if ( leases _per _minute _data [ i ] > 0 ) {
leases _per _minute += leases _per _minute _data [ i ] ;
// console.log("iteration " + i + " val: " + leases_per_minute_data[i] + " lpm: " + leases_per_minute);
}
else {
// console.log("no data " + i);
}
}
if ( leases _per _minute _counter == 60 )
leases _per _minute _counter = 0 ;
} , 1000 ) ;
/ * *
* Poll : CPU Utilization
* /
cpu _utilization _poll = setInterval ( function ( ) {
cpu _utilization = parseFloat ( execSync ( "top -bn 1 | awk 'NR>7{s+=$9} END {print s/4}'" ) )
} , ( 15 * 1000 ) ) ;
/ * *
* Clean Expired Leases
* /
2017-08-09 22:18:28 +02:00
lease _clean _timer = setInterval ( function ( ) {
lease _parser . clean ( ) ;
2017-09-02 11:44:39 +02:00
} , ( 60 * 1000 ) ) ;
function get _socket _clients _connected _count ( ) {
wss . clients . forEach ( function each ( client ) {
if ( client . readyState === WebSocket . OPEN ) {
socket _clients ++ ;
}
} ) ;
return socket _clients ;
}
/ * *
* Websocker Server
* /
const WebSocket = require ( 'ws' ) ;
const wss = new WebSocket . Server ( { port : 8080 } ) ;
options . interval = 100 ;
var tail _dhcp _log = new tail _module (
glass _config . log _file ,
"\n" ,
options
) ;
tail _dhcp _log . on ( "line" , function ( data ) {
if ( listening _to _log _file ) {
wss . broadcast _event ( data , 'dhcp_log_subscription' ) ;
}
} ) ;
wss . on ( 'connection' , function connection ( ws ) {
socket _clients ++ ;
console . log ( "[WS] CLIENT_CONNECT: Socket clients (" + socket _clients + ")" ) ;
if ( ! listening _to _log _file ) {
/* Watch log file for new information */
var tail _module = require ( 'always-tail' ) ;
listening _to _log _file = 1 ;
}
} ) ;
wss . on ( 'close' , function close ( ) {
socket _clients -- ;
console . log ( "[WS] CLIENT_DISCONNECT: Socket clients (" + socket _clients + ")" ) ;
} ) ;
function heartbeat ( ) {
this . isAlive = true ;
}
function isJson ( str ) {
try {
JSON . parse ( str ) ;
} catch ( e ) {
return false ;
}
return true ;
}
wss . on ( 'connection' , function connection ( ws ) {
ws . isAlive = true ;
ws . on ( 'pong' , heartbeat ) ;
ws . event _subscription = [ ] ;
ws . on ( 'message' , function incoming ( data ) {
if ( data != "" && isJson ( data ) ) {
var json = JSON . parse ( data ) ;
if ( typeof json [ "event_subscription" ] !== "undefined" ) {
console . log ( "[WS] Incoming Subscription '%s'" , json [ 'event_subscription' ] ) ;
ws . event _subscription [ json [ "event_subscription" ] ] = 1 ;
}
if ( typeof json [ "event_unsubscribe" ] !== "undefined" ) {
console . log ( "[WS] event_unsubscribe '%s'" , json [ 'event_unsubscribe' ] ) ;
delete ws . event _subscription [ json [ "event_unsubscribe" ] ] ;
}
}
} ) ;
stale _connections _audit ( ) ;
} ) ;
wss . broadcast = function broadcast ( data ) {
wss . clients . forEach ( function each ( client ) {
if ( client . readyState === WebSocket . OPEN ) {
client . send ( data ) ;
}
} ) ;
} ;
wss . broadcast _event = function broadcast ( data , event ) {
wss . clients . forEach ( function each ( client ) {
if ( client . readyState === WebSocket . OPEN ) {
if ( client . event _subscription [ event ] )
client . send ( data ) ;
}
} ) ;
} ;
function stale _connections _audit ( ) {
socket _clients = 0 ;
wss . clients . forEach ( function each ( ws ) {
if ( ws . isAlive === false ) return ws . terminate ( ) ;
ws . isAlive = false ;
ws . ping ( '' , false , true ) ;
socket _clients ++ ;
} ) ;
console . log ( "[WS] STATUS: Socket clients (" + socket _clients + ")" ) ;
}
/* Keepalive - kill stale connections (30s poll) */
const interval = setInterval ( function ping ( ) {
stale _connections _audit ( ) ;
} , 30000 ) ;
2017-09-06 08:05:34 +02:00
var socket _clients = 0 ;
/ * *
* Slack Hooks
* /
var Slack = require ( 'slack-node' ) ;
webhookUri = glass _config . slack _webhook _url ;
slack = new Slack ( ) ;
slack . setWebhook ( webhookUri ) ;
function slack _message ( message ) {
slack . webhook ( {
channel : glass _config . slack _alert _channel ,
username : "Glass" ,
icon _emoji : "https://i.imgur.com/VDzAuAq.png" ,
text : message
} , function ( err , response ) {
console . log ( response ) ;
} ) ;
}
/ * *
* Alert Checks
* /
alert _status = [ ] ;
alert _status [ 'leases_per_second' ] = 0 ;
alert _check _timer = setInterval ( function ( ) {
if ( glass _config . lease _per _second _threshold > 0 ) {
if ( current _leases _per _second <= glass _config . lease _per _second _threshold && alert _status [ 'leases_per_second' ] == 0 ) {
alert _status [ 'leases_per_second' ] = 1 ;
// slack_message(":warning: WARNING: DHCP leases per second have dropped below critical threshold (" + glass_config.lease_per_second_threshold + ") Current LP/s (" + current_leases_per_second + ")");
}
else if ( current _leases _per _second >= glass _config . lease _per _second _threshold && alert _status [ 'leases_per_second' ] == 1 ) {
alert _status [ 'leases_per_second' ] = 0 ;
// slack_message(":white_check_mark: CLEAR: DHCP leases per second have returned to above the critical threshold (" + glass_config.lease_per_second_threshold + ") Current LP/s (" + current_leases_per_second + ")");
}
}
} , ( 5 * 1000 ) ) ;