Display of online state for nodes and hostname for monitoring info in admin panel.
This commit is contained in:
parent
c0ae97e298
commit
cd746a41ea
|
@ -47,11 +47,11 @@
|
||||||
color: lightgrey;
|
color: lightgrey;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monitoring-state-online {
|
.node-online, .monitoring-state-online {
|
||||||
color: green;
|
color: green;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monitoring-state-offline {
|
.node-offline, .monitoring-state-offline {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,10 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Rest
|
||||||
return { params: params };
|
return { params: params };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function nullable(value) {
|
||||||
|
return value ? value : 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
function formatMoment(unix) {
|
function formatMoment(unix) {
|
||||||
return unix ? moment.unix(unix).fromNow() : 'N/A';
|
return unix ? moment.unix(unix).fromNow() : 'N/A';
|
||||||
}
|
}
|
||||||
|
@ -41,9 +45,20 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Rest
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
|
switch (node.values.onlineState) {
|
||||||
|
case 'ONLINE':
|
||||||
|
return 'node-online';
|
||||||
|
|
||||||
|
case 'OFFLINE':
|
||||||
|
return 'node-offline';
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var nodes = nga.entity('nodes').label('Nodes').identifier(nga.field('token'));
|
var nodes = nga.entity('nodes').label('Nodes').identifier(nga.field('token'));
|
||||||
nodes
|
nodes
|
||||||
.listView()
|
.listView()
|
||||||
|
@ -70,6 +85,7 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Rest
|
||||||
? '<i class="fa fa-map-marker coords-set" aria-hidden="true" title="coordinates set"></i>'
|
? '<i class="fa fa-map-marker coords-set" aria-hidden="true" title="coordinates set"></i>'
|
||||||
: '<i class="fa fa-times coords-unset" aria-hidden="true" title="no coordinates"></i>';
|
: '<i class="fa fa-times coords-unset" aria-hidden="true" title="no coordinates"></i>';
|
||||||
}),
|
}),
|
||||||
|
nga.field('onlineState').cssClasses(nodeClasses),
|
||||||
nga.field('monitoringState').cssClasses(nodeClasses).template(function (node) {
|
nga.field('monitoringState').cssClasses(nodeClasses).template(function (node) {
|
||||||
switch (node.values.monitoringState) {
|
switch (node.values.monitoringState) {
|
||||||
case 'active':
|
case 'active':
|
||||||
|
@ -158,11 +174,24 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Rest
|
||||||
.exportFields([])
|
.exportFields([])
|
||||||
.fields([
|
.fields([
|
||||||
nga.field('id').cssClasses(monitoringStateClasses),
|
nga.field('id').cssClasses(monitoringStateClasses),
|
||||||
|
nga.field('hostname').cssClasses(monitoringStateClasses),
|
||||||
nga.field('mac').cssClasses(monitoringStateClasses),
|
nga.field('mac').cssClasses(monitoringStateClasses),
|
||||||
|
nga.field('monitoring_state').cssClasses(monitoringStateClasses).template(function (monitoringState) {
|
||||||
|
switch (monitoringState.values.monitoring_state) {
|
||||||
|
case 'active':
|
||||||
|
return '<i class="fa fa-heartbeat monitoring-active" title="active"></i>';
|
||||||
|
|
||||||
|
case 'pending':
|
||||||
|
return '<i class="fa fa-envelope monitoring-confirmation-pending" title="confirmation pending"></i>';
|
||||||
|
|
||||||
|
default:
|
||||||
|
return '<i class="fa fa-times monitoring-disabled" title="disabled"></i>';
|
||||||
|
}
|
||||||
|
}),
|
||||||
nga.field('state').cssClasses(monitoringStateClasses),
|
nga.field('state').cssClasses(monitoringStateClasses),
|
||||||
nga.field('last_seen').map(formatMoment).cssClasses(monitoringStateClasses),
|
nga.field('last_seen').map(formatMoment).cssClasses(monitoringStateClasses),
|
||||||
nga.field('import_timestamp').label('Imported').map(formatMoment).cssClasses(monitoringStateClasses),
|
nga.field('import_timestamp').label('Imported').map(formatMoment).cssClasses(monitoringStateClasses),
|
||||||
nga.field('last_status_mail_type').cssClasses(monitoringStateClasses),
|
nga.field('last_status_mail_type').map(nullable).cssClasses(monitoringStateClasses),
|
||||||
nga.field('last_status_mail_sent').map(formatMoment).cssClasses(monitoringStateClasses),
|
nga.field('last_status_mail_sent').map(formatMoment).cssClasses(monitoringStateClasses),
|
||||||
nga.field('created_at').map(formatMoment).cssClasses(monitoringStateClasses),
|
nga.field('created_at').map(formatMoment).cssClasses(monitoringStateClasses),
|
||||||
nga.field('modified_at').map(formatMoment).cssClasses(monitoringStateClasses)
|
nga.field('modified_at').map(formatMoment).cssClasses(monitoringStateClasses)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE node_state ADD COLUMN hostname VARCHAR(32);
|
||||||
|
ALTER TABLE node_state ADD COLUMN monitoring_state VARCHAR(10);
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
angular.module('ffffng').factory('NodeInformationRetrievalJob', function (MonitoringService, Logger) {
|
angular.module('ffffng').factory('NodeInformationRetrievalJob', function (MonitoringService, Logger) {
|
||||||
return {
|
return {
|
||||||
description: 'Fetches the nodes.json and calculates and stores the monitoring status for nodes with active monitoring.',
|
description: 'Fetches the nodes.json and calculates and stores the monitoring / online status for registered nodes.',
|
||||||
|
|
||||||
run: function (callback) {
|
run: function (callback) {
|
||||||
MonitoringService.retrieveNodeInformation(function (err) {
|
MonitoringService.retrieveNodeInformation(function (err) {
|
||||||
|
|
|
@ -22,6 +22,7 @@ require('./app');
|
||||||
require('./router');
|
require('./router');
|
||||||
require('./libs');
|
require('./libs');
|
||||||
|
|
||||||
|
require('./utils/databaseUtil');
|
||||||
require('./utils/errorTypes');
|
require('./utils/errorTypes');
|
||||||
require('./utils/resources');
|
require('./utils/resources');
|
||||||
require('./utils/strings');
|
require('./utils/strings');
|
||||||
|
|
|
@ -3,8 +3,11 @@
|
||||||
angular.module('ffffng').factory('NodeResource', function (
|
angular.module('ffffng').factory('NodeResource', function (
|
||||||
Constraints,
|
Constraints,
|
||||||
Validator,
|
Validator,
|
||||||
|
Logger,
|
||||||
|
MonitoringService,
|
||||||
NodeService,
|
NodeService,
|
||||||
_,
|
_,
|
||||||
|
deepExtend,
|
||||||
Strings,
|
Strings,
|
||||||
Resources,
|
Resources,
|
||||||
ErrorTypes
|
ErrorTypes
|
||||||
|
@ -110,22 +113,62 @@ angular.module('ffffng').factory('NodeResource', function (
|
||||||
return node.token;
|
return node.token;
|
||||||
});
|
});
|
||||||
|
|
||||||
var filteredNodes = Resources.filter(
|
var macs = _.map(realNodes, function (node) {
|
||||||
realNodes,
|
return node.mac;
|
||||||
['hostname', 'nickname', 'email', 'token', 'mac', 'key'],
|
});
|
||||||
restParams
|
|
||||||
);
|
|
||||||
var total = filteredNodes.length;
|
|
||||||
|
|
||||||
var sortedNodes = Resources.sort(
|
MonitoringService.getByMacs(macs, function (err, nodeStateByMac) {
|
||||||
filteredNodes,
|
if (err) {
|
||||||
['hostname', 'nickname', 'email', 'token', 'mac', 'key', 'coords', 'monitoringState'],
|
Logger.tag('nodes', 'admin').error('Error getting nodes by MACs:', err);
|
||||||
restParams
|
return Resources.error(res, {data: 'Internal error.', type: ErrorTypes.internalError});
|
||||||
);
|
}
|
||||||
var pageNodes = Resources.getPageEntities(sortedNodes, restParams);
|
|
||||||
|
|
||||||
res.set('X-Total-Count', total);
|
var enhancedNodes = _.map(realNodes, function (node) {
|
||||||
return Resources.success(res, pageNodes);
|
var nodeState = nodeStateByMac[node.mac];
|
||||||
|
if (nodeState) {
|
||||||
|
return deepExtend({}, node, {
|
||||||
|
onlineState: nodeState.state
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
|
||||||
|
var filteredNodes = Resources.filter(
|
||||||
|
enhancedNodes,
|
||||||
|
[
|
||||||
|
'hostname',
|
||||||
|
'nickname',
|
||||||
|
'email',
|
||||||
|
'token',
|
||||||
|
'mac',
|
||||||
|
'key',
|
||||||
|
'onlineState'
|
||||||
|
],
|
||||||
|
restParams
|
||||||
|
);
|
||||||
|
var total = filteredNodes.length;
|
||||||
|
|
||||||
|
var sortedNodes = Resources.sort(
|
||||||
|
filteredNodes,
|
||||||
|
[
|
||||||
|
'hostname',
|
||||||
|
'nickname',
|
||||||
|
'email',
|
||||||
|
'token',
|
||||||
|
'mac',
|
||||||
|
'key',
|
||||||
|
'coords',
|
||||||
|
'onlineState',
|
||||||
|
'monitoringState'
|
||||||
|
],
|
||||||
|
restParams
|
||||||
|
);
|
||||||
|
var pageNodes = Resources.getPageEntities(sortedNodes, restParams);
|
||||||
|
|
||||||
|
res.set('X-Total-Count', total);
|
||||||
|
return Resources.success(res, pageNodes);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@ angular.module('ffffng')
|
||||||
_,
|
_,
|
||||||
async,
|
async,
|
||||||
config,
|
config,
|
||||||
|
deepExtend,
|
||||||
Database,
|
Database,
|
||||||
|
DatabaseUtil,
|
||||||
ErrorTypes,
|
ErrorTypes,
|
||||||
Logger,
|
Logger,
|
||||||
moment,
|
moment,
|
||||||
|
@ -34,10 +36,12 @@ angular.module('ffffng')
|
||||||
|
|
||||||
return Database.run(
|
return Database.run(
|
||||||
'INSERT INTO node_state ' +
|
'INSERT INTO node_state ' +
|
||||||
'(mac, state, last_seen, import_timestamp, (last_status_mail_sent, last_status_mail_type) OR last_status_mail_type IS NULL) ' +
|
'(hostname, mac, monitoring_state, state, last_seen, import_timestamp, last_status_mail_sent, last_status_mail_type) ' +
|
||||||
'VALUES (?, ?, ?, ?, ?, ?)',
|
'VALUES (?, ?, ?, ?, ?, ?)',
|
||||||
[
|
[
|
||||||
|
node.hostname,
|
||||||
node.mac,
|
node.mac,
|
||||||
|
node.monitoringState,
|
||||||
nodeData.state,
|
nodeData.state,
|
||||||
nodeData.lastSeen.unix(),
|
nodeData.lastSeen.unix(),
|
||||||
nodeData.importTimestamp.unix(),
|
nodeData.importTimestamp.unix(),
|
||||||
|
@ -66,45 +70,37 @@ angular.module('ffffng')
|
||||||
|
|
||||||
return Database.run(
|
return Database.run(
|
||||||
'UPDATE node_state ' +
|
'UPDATE node_state ' +
|
||||||
'SET state = ?, last_seen = ?, import_timestamp = ?, modified_at = ?' +
|
'SET hostname = ?, monitoring_state = ?, state = ?, last_seen = ?, import_timestamp = ?, modified_at = ?' +
|
||||||
'WHERE id = ? AND mac = ?',
|
'WHERE id = ? AND mac = ?',
|
||||||
[
|
[
|
||||||
nodeData.state, nodeData.lastSeen.unix(), nodeData.importTimestamp.unix(), moment().unix(),
|
node.hostname,
|
||||||
row.id, node.mac
|
node.monitoringState,
|
||||||
|
nodeData.state,
|
||||||
|
nodeData.lastSeen.unix(),
|
||||||
|
nodeData.importTimestamp.unix(),
|
||||||
|
moment().unix(),
|
||||||
|
|
||||||
|
row.id,
|
||||||
|
node.mac
|
||||||
],
|
],
|
||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteNodeInformation(nodeData, node, callback) {
|
|
||||||
Logger
|
|
||||||
.tag('monitoring', 'information-retrieval')
|
|
||||||
.debug('Node is not being monitored, deleting monitoring data: %s', nodeData.mac);
|
|
||||||
return Database.run(
|
|
||||||
'DELETE FROM node_state WHERE mac = ? AND import_timestamp < ?',
|
|
||||||
[node.mac, nodeData.importTimestamp.unix()],
|
|
||||||
callback
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function storeNodeInformation(nodeData, node, callback) {
|
function storeNodeInformation(nodeData, node, callback) {
|
||||||
if (node.monitoring && node.monitoringConfirmed) {
|
Logger.tag('monitoring', 'information-retrieval').debug('Storing status for node: %s', nodeData.mac);
|
||||||
Logger.tag('monitoring', 'information-retrieval').debug('Node is being monitored: %s', nodeData.mac);
|
|
||||||
|
|
||||||
return Database.get('SELECT * FROM node_state WHERE mac = ?', [node.mac], function (err, row) {
|
return Database.get('SELECT * FROM node_state WHERE mac = ?', [node.mac], function (err, row) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_.isUndefined(row)) {
|
if (_.isUndefined(row)) {
|
||||||
return insertNodeInformation(nodeData, node, callback);
|
return insertNodeInformation(nodeData, node, callback);
|
||||||
} else {
|
} else {
|
||||||
return updateNodeInformation(nodeData, node, row, callback);
|
return updateNodeInformation(nodeData, node, row, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
return deleteNodeInformation(nodeData, node, callback);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var isValidMac = Validator.forConstraint(Constraints.node.mac);
|
var isValidMac = Validator.forConstraint(Constraints.node.mac);
|
||||||
|
@ -221,7 +217,7 @@ angular.module('ffffng')
|
||||||
function (nodeState, mailCallback) {
|
function (nodeState, mailCallback) {
|
||||||
var mac = nodeState.mac;
|
var mac = nodeState.mac;
|
||||||
Logger.tag('monitoring', 'mail-sending').debug('Loading node data for: %s', mac);
|
Logger.tag('monitoring', 'mail-sending').debug('Loading node data for: %s', mac);
|
||||||
NodeService.findNodeDataByMac(mac, function (err, node, nodeSecrets) {
|
NodeService.getNodeDataByMac(mac, function (err, node, nodeSecrets) {
|
||||||
if (err) {
|
if (err) {
|
||||||
Logger
|
Logger
|
||||||
.tag('monitoring', 'mail-sending')
|
.tag('monitoring', 'mail-sending')
|
||||||
|
@ -267,10 +263,10 @@ angular.module('ffffng')
|
||||||
var now = moment().unix();
|
var now = moment().unix();
|
||||||
Database.run(
|
Database.run(
|
||||||
'UPDATE node_state ' +
|
'UPDATE node_state ' +
|
||||||
'SET modified_at = ?, last_status_mail_sent = ?, last_status_mail_type = ?' +
|
'SET hostname = ?, monitoring_state = ?, modified_at = ?, last_status_mail_sent = ?, last_status_mail_type = ?' +
|
||||||
'WHERE id = ?',
|
'WHERE id = ?',
|
||||||
[
|
[
|
||||||
now, now, mailType,
|
node.hostname, node.monitoringState, now, now, mailType,
|
||||||
nodeState.id
|
nodeState.id
|
||||||
],
|
],
|
||||||
mailCallback
|
mailCallback
|
||||||
|
@ -357,7 +353,9 @@ angular.module('ffffng')
|
||||||
getAll: function (restParams, callback) {
|
getAll: function (restParams, callback) {
|
||||||
var sortFields = [
|
var sortFields = [
|
||||||
'id',
|
'id',
|
||||||
|
'hostname',
|
||||||
'mac',
|
'mac',
|
||||||
|
'monitoring_state',
|
||||||
'state',
|
'state',
|
||||||
'last_seen',
|
'last_seen',
|
||||||
'import_timestamp',
|
'import_timestamp',
|
||||||
|
@ -367,7 +365,9 @@ angular.module('ffffng')
|
||||||
'modified_at'
|
'modified_at'
|
||||||
];
|
];
|
||||||
var filterFields = [
|
var filterFields = [
|
||||||
|
'hostname',
|
||||||
'mac',
|
'mac',
|
||||||
|
'monitoring_state',
|
||||||
'state',
|
'state',
|
||||||
'last_status_mail_type'
|
'last_status_mail_type'
|
||||||
];
|
];
|
||||||
|
@ -406,6 +406,31 @@ angular.module('ffffng')
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getByMacs: function (macs, callback) {
|
||||||
|
if (_.isEmpty(macs)) {
|
||||||
|
return callback(null, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
var inCondition = DatabaseUtil.inCondition('mac', macs);
|
||||||
|
|
||||||
|
Database.all(
|
||||||
|
'SELECT * FROM node_state WHERE ' + inCondition.query,
|
||||||
|
_.concat([], inCondition.params),
|
||||||
|
function (err, rows) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodeStateByMac = {};
|
||||||
|
_.each(rows, function (row) {
|
||||||
|
nodeStateByMac[row.mac] = row;
|
||||||
|
});
|
||||||
|
|
||||||
|
callback(null, nodeStateByMac);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
confirm: function (token, callback) {
|
confirm: function (token, callback) {
|
||||||
NodeService.getNodeDataByMonitoringToken(token, function (err, node, nodeSecrets) {
|
NodeService.getNodeDataByMonitoringToken(token, function (err, node, nodeSecrets) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -490,7 +515,7 @@ angular.module('ffffng')
|
||||||
function (nodeData, nodeCallback) {
|
function (nodeData, nodeCallback) {
|
||||||
Logger.tag('monitoring', 'information-retrieval').debug('Importing: %s', nodeData.mac);
|
Logger.tag('monitoring', 'information-retrieval').debug('Importing: %s', nodeData.mac);
|
||||||
|
|
||||||
NodeService.findNodeDataByMac(nodeData.mac, function (err, node) {
|
NodeService.getNodeDataByMac(nodeData.mac, function (err, node) {
|
||||||
if (err) {
|
if (err) {
|
||||||
Logger
|
Logger
|
||||||
.tag('monitoring', 'information-retrieval')
|
.tag('monitoring', 'information-retrieval')
|
||||||
|
|
|
@ -424,7 +424,7 @@ angular.module('ffffng')
|
||||||
getAllNodes: function (callback) {
|
getAllNodes: function (callback) {
|
||||||
findNodeFiles({}, function (err, files) {
|
findNodeFiles({}, function (err, files) {
|
||||||
if (err) {
|
if (err) {
|
||||||
Logger.tag('nodes').error('Error getting all nodes:', error);
|
Logger.tag('nodes').error('Error getting all nodes:', err);
|
||||||
return callback({data: 'Internal error.', type: ErrorTypes.internalError});
|
return callback({data: 'Internal error.', type: ErrorTypes.internalError});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,7 +434,7 @@ angular.module('ffffng')
|
||||||
parseNodeFile,
|
parseNodeFile,
|
||||||
function (err, nodes) {
|
function (err, nodes) {
|
||||||
if (err) {
|
if (err) {
|
||||||
Logger.tag('nodes').error('Error getting all nodes:', error);
|
Logger.tag('nodes').error('Error getting all nodes:', err);
|
||||||
return callback({data: 'Internal error.', type: ErrorTypes.internalError});
|
return callback({data: 'Internal error.', type: ErrorTypes.internalError});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +444,7 @@ angular.module('ffffng')
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
findNodeDataByMac: function (mac, callback) {
|
getNodeDataByMac: function (mac, callback) {
|
||||||
return findNodeDataByFilePattern({ mac: mac }, callback);
|
return findNodeDataByFilePattern({ mac: mac }, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
12
server/utils/databaseUtil.js
Normal file
12
server/utils/databaseUtil.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('ffffng').factory('DatabaseUtil', function (_) {
|
||||||
|
return {
|
||||||
|
inCondition: function (field, list) {
|
||||||
|
return {
|
||||||
|
query: '(' + field + ' IN (' + _.join(_.times(list.length, _.constant('?')), ', ') + '))',
|
||||||
|
params: list
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
Loading…
Reference in a new issue