diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 0cd4b64fb..2a52013d4 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -2,4 +2,5 @@ - FIX Change Device Not Found to 404 instead of 500 (#161). - ADD First version of the stats registry (#160). - FIX Reestructure files and folders (#164). -- FIX Use Express Methods to check content type (#170). \ No newline at end of file +- ADD Mongodb action to persist stats. +- FIX Use Express Methods to check content type (#170). diff --git a/lib/model/dbConn.js b/lib/model/dbConn.js index 8152c936a..f605a1380 100644 --- a/lib/model/dbConn.js +++ b/lib/model/dbConn.js @@ -28,7 +28,8 @@ */ var mongoose = require('mongoose'), - defaultDb; + defaultDb, + DEFAULT_DB_NAME = 'iotagent'; function loadModels() { require('./Device').load(defaultDb); @@ -38,9 +39,10 @@ function loadModels() { /** * Creates a new connection to the Mongo DB. * + * @this Reference to the dbConn module itself. */ function init(host, db, port, options, callback) { - /*jshint camelcase:false */ + /*jshint camelcase:false, validthis:true */ defaultDb = mongoose.createConnection(host, db, port, options); @@ -48,9 +50,13 @@ function init(host, db, port, options, callback) { throw new Error(error); }); + this.db = defaultDb; + loadModels(); callback(null); } + exports.init = init; exports.db = defaultDb; +exports.DEFAULT_DB_NAME = DEFAULT_DB_NAME; diff --git a/lib/services/devices/deviceRegistryMongoDB.js b/lib/services/devices/deviceRegistryMongoDB.js index 65ebaba3b..a08760b9b 100644 --- a/lib/services/devices/deviceRegistryMongoDB.js +++ b/lib/services/devices/deviceRegistryMongoDB.js @@ -25,7 +25,6 @@ var logger = require('logops'), dbService = require('../../model/dbConn'), errors = require('../../errors'), Device = require('../../model/Device'), - DEFAULT_DB_NAME = 'iotagent', context = { op: 'IoTAgentNGSI.MongoDBDeviceRegister' }; @@ -223,7 +222,7 @@ function init(newConfig, callback) { port = newConfig.deviceRegistry.port || 27017; if (!newConfig.deviceRegistry.db) { - dbName = DEFAULT_DB_NAME; + dbName = dbService.DEFAULT_DB_NAME; } dbService.init(newConfig.deviceRegistry.host, dbName, port, {}, callback); diff --git a/lib/services/stats/statsRegistry.js b/lib/services/stats/statsRegistry.js index 714e38b43..28192bcbf 100644 --- a/lib/services/stats/statsRegistry.js +++ b/lib/services/stats/statsRegistry.js @@ -23,8 +23,11 @@ 'use strict'; var async = require('async'), + _ = require('underscore'), apply = async.apply, logger = require('logops'), + errors = require('../../errors'), + dbService = require('../../model/dbConn'), config, globalStats = {}, currentStats = {}, @@ -107,19 +110,6 @@ function globalLoad(values, callback) { callback(null); } -/** - * Changes the configuration of the Stats Registry service. - * - * @param {Object} newConfig New configuration of the registry. - */ -function init(newConfig, callback) { - config = newConfig; - - if (callback) { - callback(); - } -} - /** * Reset each of the current stats to value zero. */ @@ -160,6 +150,7 @@ function addTimerAction(handler, callback) { function clearTimers(callback) { if (timerHandler) { clearInterval(timerHandler); + timerHandler = undefined; } timerActions = []; @@ -179,6 +170,52 @@ function logStats(currentValues, globalValues, callback) { resetCurrent(callback); } +/** + * Predefined action that persists the current value of the stats in the MongoDb instance. + * + * @param {Object} currentValues Current stat values. + * @param {Object} globalValues Global stat values. + */ +function mongodbPersistence(currentValues, globalValues, callback) { + var statStamp = _.clone(globalValues); + + statStamp.timestamp = new Date().toISOString(); + dbService.db.collection('kpis').insert(statStamp); + + callback(); +} + +function configureDb(newConfig, callback) { + if (!newConfig.mongodb || !newConfig.mongodb.host) { + logger.fatal('No host found for MongoDB driver.'); + callback(new errors.BadConfiguration('No host found for MongoDB driver')); + } else { + var dbName = newConfig.mongodb.db, + port = newConfig.mongodb.port || 27017; + + if (!newConfig.mongodb.db) { + dbName = dbService.DEFAULT_DB_NAME; + } + + dbService.init(newConfig.mongodb.host, dbName, port, {}, callback); + } +} + +/** + * Changes the configuration of the Stats Registry service. + * + * @param {Object} newConfig New configuration of the registry. + */ +function init(newConfig, callback) { + config = newConfig; + + if (config.stats && config.stats.persistence) { + configureDb(newConfig, callback); + } else { + callback(); + } +} + exports.init = init; exports.add = add; exports.getCurrent = getCurrent; @@ -187,6 +224,7 @@ exports.getAllGlobal = getAllGlobal; exports.getAllCurrent = getAllCurrent; exports.globalLoad = globalLoad; exports.resetCurrent = resetCurrent; -exports.addTimerAction = addTimerAction; exports.clearTimers = clearTimers; +exports.addTimerAction = addTimerAction; exports.logStats = logStats; +exports.mongodbPersistence = mongodbPersistence; diff --git a/test/unit/statistics-persistence_test.js b/test/unit/statistics-persistence_test.js new file mode 100644 index 000000000..028bd6203 --- /dev/null +++ b/test/unit/statistics-persistence_test.js @@ -0,0 +1,101 @@ +/* + * Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U + * + * This file is part of fiware-iotagent-lib + * + * fiware-iotagent-lib is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * fiware-iotagent-lib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with fiware-iotagent-lib. + * If not, seehttp://www.gnu.org/licenses/. + * + * For those usages not covered by the GNU Affero General Public License + * please contact with::[contacto@tid.es] + */ +'use strict'; + +var statsService = require('../../lib/services/stats/statsRegistry'), + should = require('should'), + mongo = require('mongodb').MongoClient, + mongoUtils = require('./mongoDBUtils'), + iotAgentConfig = { + logLevel: 'FATAL', + contextBroker: { + host: '10.11.128.16', + port: '1026' + }, + server: { + port: 4041, + baseRoot: '/' + }, + stats: { + interval: 100, + persistence: true + }, + mongodb: { + host: 'localhost', + port: '27017', + db: 'iotagent' + }, + types: {}, + service: 'smartGondor', + subservice: 'gardens', + providerUrl: 'http://smartGondor.com', + deviceRegistrationDuration: 'P1M', + throttling: 'PT5S' + }, + iotAgentDb; + +describe('Statistics persistence service', function() { + beforeEach(function(done) { + statsService.init(iotAgentConfig, function() { + statsService.globalLoad({}, function() { + mongo.connect('mongodb://localhost:27017/iotagent', function(err, db) { + iotAgentDb = db; + done(); + }); + }); + }); + }); + + afterEach(function(done) { + statsService.globalLoad({}, function() { + iotAgentDb.close(function(error) { + mongoUtils.cleanDbs(done); + }); + }); + }); + + describe('When a periodic persitence action is set', function() { + beforeEach(function(done) { + statsService.globalLoad({ + stat1: 10 + }, function() { + statsService.add('stat1', 5, done); + }); + }); + + it('should store all the records in the database', function(done) { + statsService.addTimerAction(statsService.mongodbPersistence, function() { + setTimeout(function() { + statsService.clearTimers(function() { + iotAgentDb.collection('kpis').find({}).toArray(function(err, docs) { + should.not.exist(err); + should.exist(docs); + docs.length.should.be.above(2); + done(); + }); + }); + }, 480); + }); + }); + }); +}); diff --git a/test/unit/statistics-service_test.js b/test/unit/statistics-service_test.js index 7decf9537..a8f389fb7 100644 --- a/test/unit/statistics-service_test.js +++ b/test/unit/statistics-service_test.js @@ -47,9 +47,11 @@ var statsService = require('../../lib/services/stats/statsRegistry'), describe('Statistics service', function() { beforeEach(function(done) { - statsService.init(iotAgentConfig); - - statsService.globalLoad({}, done); + statsService.init(iotAgentConfig, function() { + statsService.globalLoad({}, function() { + statsService.clearTimers(done); + }); + }); }); afterEach(function(done) { @@ -152,7 +154,7 @@ describe('Statistics service', function() { it('should be triggered with the periodicity stated in the config.stats.interval parameter', function(done) { statsService.addTimerAction(mockedAction, function() { - setInterval(function() { + setTimeout(function() { statsService.clearTimers(function() { valueCurrent.should.equal(5); valueGlobal.should.equal(15);