diff --git a/admin/index.html b/admin/index.html
index 4f6cf63..44a32fe 100644
--- a/admin/index.html
+++ b/admin/index.html
@@ -47,11 +47,11 @@
color: lightgrey;
}
- .monitoring-state-online {
+ .node-online, .monitoring-state-online {
color: green;
}
- .monitoring-state-offline {
+ .node-offline, .monitoring-state-offline {
color: red;
}
diff --git a/admin/js/main.js b/admin/js/main.js
index 9ca19d5..846ceed 100644
--- a/admin/js/main.js
+++ b/admin/js/main.js
@@ -15,6 +15,10 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Rest
return { params: params };
});
+ function nullable(value) {
+ return value ? value : 'N/A';
+ }
+
function formatMoment(unix) {
return unix ? moment.unix(unix).fromNow() : 'N/A';
}
@@ -41,9 +45,20 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Rest
if (!node) {
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'));
nodes
.listView()
@@ -70,6 +85,7 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Rest
? ''
: '';
}),
+ nga.field('onlineState').cssClasses(nodeClasses),
nga.field('monitoringState').cssClasses(nodeClasses).template(function (node) {
switch (node.values.monitoringState) {
case 'active':
@@ -158,11 +174,24 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Rest
.exportFields([])
.fields([
nga.field('id').cssClasses(monitoringStateClasses),
+ nga.field('hostname').cssClasses(monitoringStateClasses),
nga.field('mac').cssClasses(monitoringStateClasses),
+ nga.field('monitoring_state').cssClasses(monitoringStateClasses).template(function (monitoringState) {
+ switch (monitoringState.values.monitoring_state) {
+ case 'active':
+ return '';
+
+ case 'pending':
+ return '';
+
+ default:
+ return '';
+ }
+ }),
nga.field('state').cssClasses(monitoringStateClasses),
nga.field('last_seen').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('created_at').map(formatMoment).cssClasses(monitoringStateClasses),
nga.field('modified_at').map(formatMoment).cssClasses(monitoringStateClasses)
diff --git a/server/db/patches/005_add_hostname_and_monitoring_state.sql b/server/db/patches/005_add_hostname_and_monitoring_state.sql
new file mode 100644
index 0000000..243b1ef
--- /dev/null
+++ b/server/db/patches/005_add_hostname_and_monitoring_state.sql
@@ -0,0 +1,2 @@
+ALTER TABLE node_state ADD COLUMN hostname VARCHAR(32);
+ALTER TABLE node_state ADD COLUMN monitoring_state VARCHAR(10);
diff --git a/server/jobs/nodeInformationRetrievalJob.js b/server/jobs/nodeInformationRetrievalJob.js
index 133e65b..0248f30 100644
--- a/server/jobs/nodeInformationRetrievalJob.js
+++ b/server/jobs/nodeInformationRetrievalJob.js
@@ -2,7 +2,7 @@
angular.module('ffffng').factory('NodeInformationRetrievalJob', function (MonitoringService, Logger) {
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) {
MonitoringService.retrieveNodeInformation(function (err) {
diff --git a/server/main.js b/server/main.js
index 45fe71e..3c18306 100755
--- a/server/main.js
+++ b/server/main.js
@@ -22,6 +22,7 @@ require('./app');
require('./router');
require('./libs');
+require('./utils/databaseUtil');
require('./utils/errorTypes');
require('./utils/resources');
require('./utils/strings');
diff --git a/server/resources/nodeResource.js b/server/resources/nodeResource.js
index dc63d56..01fdd52 100644
--- a/server/resources/nodeResource.js
+++ b/server/resources/nodeResource.js
@@ -3,8 +3,11 @@
angular.module('ffffng').factory('NodeResource', function (
Constraints,
Validator,
+ Logger,
+ MonitoringService,
NodeService,
_,
+ deepExtend,
Strings,
Resources,
ErrorTypes
@@ -110,22 +113,62 @@ angular.module('ffffng').factory('NodeResource', function (
return node.token;
});
- var filteredNodes = Resources.filter(
- realNodes,
- ['hostname', 'nickname', 'email', 'token', 'mac', 'key'],
- restParams
- );
- var total = filteredNodes.length;
+ var macs = _.map(realNodes, function (node) {
+ return node.mac;
+ });
- var sortedNodes = Resources.sort(
- filteredNodes,
- ['hostname', 'nickname', 'email', 'token', 'mac', 'key', 'coords', 'monitoringState'],
- restParams
- );
- var pageNodes = Resources.getPageEntities(sortedNodes, restParams);
+ MonitoringService.getByMacs(macs, function (err, nodeStateByMac) {
+ if (err) {
+ Logger.tag('nodes', 'admin').error('Error getting nodes by MACs:', err);
+ return Resources.error(res, {data: 'Internal error.', type: ErrorTypes.internalError});
+ }
- res.set('X-Total-Count', total);
- return Resources.success(res, pageNodes);
+ var enhancedNodes = _.map(realNodes, function (node) {
+ 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);
+ });
});
});
}
diff --git a/server/services/monitoringService.js b/server/services/monitoringService.js
index a18eda3..63e48f9 100644
--- a/server/services/monitoringService.js
+++ b/server/services/monitoringService.js
@@ -5,7 +5,9 @@ angular.module('ffffng')
_,
async,
config,
+ deepExtend,
Database,
+ DatabaseUtil,
ErrorTypes,
Logger,
moment,
@@ -34,10 +36,12 @@ angular.module('ffffng')
return Database.run(
'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 (?, ?, ?, ?, ?, ?)',
[
+ node.hostname,
node.mac,
+ node.monitoringState,
nodeData.state,
nodeData.lastSeen.unix(),
nodeData.importTimestamp.unix(),
@@ -66,45 +70,37 @@ angular.module('ffffng')
return Database.run(
'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 = ?',
[
- nodeData.state, nodeData.lastSeen.unix(), nodeData.importTimestamp.unix(), moment().unix(),
- row.id, node.mac
+ node.hostname,
+ node.monitoringState,
+ nodeData.state,
+ nodeData.lastSeen.unix(),
+ nodeData.importTimestamp.unix(),
+ moment().unix(),
+
+ row.id,
+ node.mac
],
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) {
- if (node.monitoring && node.monitoringConfirmed) {
- Logger.tag('monitoring', 'information-retrieval').debug('Node is being monitored: %s', nodeData.mac);
+ Logger.tag('monitoring', 'information-retrieval').debug('Storing status for node: %s', nodeData.mac);
- return Database.get('SELECT * FROM node_state WHERE mac = ?', [node.mac], function (err, row) {
- if (err) {
- return callback(err);
- }
+ return Database.get('SELECT * FROM node_state WHERE mac = ?', [node.mac], function (err, row) {
+ if (err) {
+ return callback(err);
+ }
- if (_.isUndefined(row)) {
- return insertNodeInformation(nodeData, node, callback);
- } else {
- return updateNodeInformation(nodeData, node, row, callback);
- }
- });
- } else {
- return deleteNodeInformation(nodeData, node, callback);
- }
+ if (_.isUndefined(row)) {
+ return insertNodeInformation(nodeData, node, callback);
+ } else {
+ return updateNodeInformation(nodeData, node, row, callback);
+ }
+ });
}
var isValidMac = Validator.forConstraint(Constraints.node.mac);
@@ -221,7 +217,7 @@ angular.module('ffffng')
function (nodeState, mailCallback) {
var mac = nodeState.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) {
Logger
.tag('monitoring', 'mail-sending')
@@ -267,10 +263,10 @@ angular.module('ffffng')
var now = moment().unix();
Database.run(
'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 = ?',
[
- now, now, mailType,
+ node.hostname, node.monitoringState, now, now, mailType,
nodeState.id
],
mailCallback
@@ -357,7 +353,9 @@ angular.module('ffffng')
getAll: function (restParams, callback) {
var sortFields = [
'id',
+ 'hostname',
'mac',
+ 'monitoring_state',
'state',
'last_seen',
'import_timestamp',
@@ -367,7 +365,9 @@ angular.module('ffffng')
'modified_at'
];
var filterFields = [
+ 'hostname',
'mac',
+ 'monitoring_state',
'state',
'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) {
NodeService.getNodeDataByMonitoringToken(token, function (err, node, nodeSecrets) {
if (err) {
@@ -490,7 +515,7 @@ angular.module('ffffng')
function (nodeData, nodeCallback) {
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) {
Logger
.tag('monitoring', 'information-retrieval')
diff --git a/server/services/nodeService.js b/server/services/nodeService.js
index 6c8558a..3d8a21e 100644
--- a/server/services/nodeService.js
+++ b/server/services/nodeService.js
@@ -424,7 +424,7 @@ angular.module('ffffng')
getAllNodes: function (callback) {
findNodeFiles({}, function (err, files) {
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});
}
@@ -434,7 +434,7 @@ angular.module('ffffng')
parseNodeFile,
function (err, nodes) {
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});
}
@@ -444,7 +444,7 @@ angular.module('ffffng')
});
},
- findNodeDataByMac: function (mac, callback) {
+ getNodeDataByMac: function (mac, callback) {
return findNodeDataByFilePattern({ mac: mac }, callback);
},
diff --git a/server/utils/databaseUtil.js b/server/utils/databaseUtil.js
new file mode 100644
index 0000000..5f1ea96
--- /dev/null
+++ b/server/utils/databaseUtil.js
@@ -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
+ };
+ }
+ };
+});