diff --git a/Shovel/controllers/Shovel.js b/Shovel/controllers/Shovel.js index b80a36e..dc3f83d 100644 --- a/Shovel/controllers/Shovel.js +++ b/Shovel/controllers/Shovel.js @@ -9,8 +9,8 @@ var keystone = require('./../lib/api/openstack/keystone'); var logger = require('./../lib/services/logger').Logger; var encryption = require('./../lib/services/encryption'); var jsonfile = require('jsonfile'); -var Promise = require('bluebird'); var _ = require('underscore'); +var Promise = require('bluebird'); var ironicConfig = config.ironic; var glanceConfig = config.glance; @@ -51,8 +51,8 @@ module.exports.driversGet = function driversGet(req, res, next) { else res.end(); }) - .catch(function(err){ - logger.error({message:err,path:req.url}); + .catch(function (err) { + logger.error({ message: err, path: req.url }); res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(err)); }); @@ -78,8 +78,8 @@ module.exports.ironicnodesGet = function ironicnodesGet(req, res, next) { else res.end(); }) - .catch(function(err){ - logger.error({message:err,path:req.url}); + .catch(function (err) { + logger.error({ message: err, path: req.url }); res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(err)); }); @@ -105,8 +105,8 @@ module.exports.ironicchassisGet = function ironicchassisGet(req, res, next) { else res.end(); }) - .catch(function(err){ - logger.error({message:err,path:req.url}); + .catch(function (err) { + logger.error({ message: err, path: req.url }); res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(err)); }); @@ -122,7 +122,7 @@ module.exports.ironicnodeGet = function ironicnodeGet(req, res, next) { ironicConfig.os_password). then(function (token) { token = JSON.parse(token).access.token.id; - return ironic.get_nodeAsync(token, req.swagger.params.identifier.value); + return ironic.get_node(token, req.swagger.params.identifier.value); }). then(function (result) { if (typeof result !== 'undefined') { @@ -132,8 +132,8 @@ module.exports.ironicnodeGet = function ironicnodeGet(req, res, next) { else res.end(); }) - .catch(function(err){ - logger.error({message:err,path:req.url}); + .catch(function (err) { + logger.error({ message: err, path: req.url }); res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(err)); }); @@ -158,8 +158,8 @@ module.exports.ironicnodePatch = function ironicnodePatch(req, res, next) { res.end(result); } }) - .catch(function(err){ - logger.error({message:err,path:req.url}); + .catch(function (err) { + logger.error({ message: err, path: req.url }); res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(err)); }); @@ -180,8 +180,8 @@ module.exports.catalogsGet = function catalogsGet(req, res, next) { else res.end(); }) - .catch(function(err){ - logger.error({message:err,path:req.url}); + .catch(function (err) { + logger.error({ message: err, path: req.url }); res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(err)); }); @@ -203,8 +203,8 @@ module.exports.catalogsbysourceGet = function catalogsbysourceGet(req, res, next else res.end(); }) - .catch(function(err){ - logger.error({message:err,path:req.url}); + .catch(function (err) { + logger.error({ message: err, path: req.url }); res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(err)); }); @@ -225,8 +225,8 @@ module.exports.nodeGet = function nodeGet(req, res, next) { else res.end(); }) - .catch(function(err){ - logger.error({message:err,path:req.url}); + .catch(function (err) { + logger.error({ message: err, path: req.url }); res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(err)); }); @@ -241,7 +241,7 @@ module.exports.nodesGet = function nodesGet(req, res, next) { return monorail.request_nodes_get(). then(function (nodes) { Promise.filter(JSON.parse(nodes), function (node) { - return lookupCatalog(node); + return monorail.lookupCatalog(node); }) .then(function (discoveredNodes) { if (typeof discoveredNodes !== 'undefined') { @@ -259,38 +259,6 @@ module.exports.nodesGet = function nodesGet(req, res, next) { }); }; -function lookupCatalog(node) { - return monorail.get_catalog_data_by_source(node.id, 'dmi') - .then(function (dmi) { - if (!_.has(JSON.parse(dmi), 'data')) { - return false; - } - }) - .then(function () { - return monorail.get_catalog_data_by_source(node.id, 'lsscsi') - }) - .then(function (lsscsi) { - if (!_.has(JSON.parse(lsscsi), 'data')) { - return false; - } - }) - .then(function () { - return monorail.get_catalog_data_by_source(node.id, 'bmc') - }) - .then(function (bmc) { - if (!_.has(JSON.parse(bmc), 'data')) { - return false; - } - else { - return true; - } - }) - .catch(function (err) { - logger.error(err); - return false; - }) -} - /* * @api {get} /api/1.1/nodes/identifier/sel / GET / * @apiDescription get specific node by id @@ -315,8 +283,8 @@ module.exports.getSeldata = function getSeldata(req, res, next) { res.end(); } }) - .catch(function(err){ - logger.error({message:err,path:req.url}); + .catch(function (err) { + logger.error({ message: err, path: req.url }); res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(err)); }); @@ -361,57 +329,43 @@ module.exports.registerpost = function registerpost(req, res, next) { return monorail.request_node_get(user_entry.uuid). then(function (result) { if (!JSON.parse(result).name) { - res.setHeader('Content-Type', 'application/json'); - res.end(result); + var error = { error_message: { message: 'failed to find required node in RackHD' } }; + logger.error(err); + throw error; } onrack_node = JSON.parse(result); - }).then(function () { - return monorail.get_catalog_data_by_source(user_entry.uuid, 'lsscsi'). - then(function (scsi) { - scsi = JSON.parse(scsi); - if (scsi.data) { - for (var elem in scsi.data) { - var item = (scsi.data[elem]); - if (item['peripheralType'] == 'disk') { - local_gb += parseFloat(item['size'].replace('GB', '').trim()); - } - } - } + return monorail.nodeDiskSize(onrack_node) + .catch(function (err) { + var error = { error_message: { message: 'failed to get compute node Disk Size' } }; + logger.error(err); + throw error; + }); - }).then(function () { - monorail.get_catalog_data_by_source(user_entry.uuid, 'dmi'). - then(function (dmi) { - dmi = JSON.parse(dmi); - if (dmi.data) { - var dmi_total = 0; - if (dmi.data['Memory Device']) { - var memory_device = dmi.data['Memory Device']; - for (var elem in memory_device) { - var item = memory_device[elem]; - //logger.info(item['Size']); - if (item['Size'].indexOf('GB') > -1) { - dmi_total += parseFloat(item['Size'].replace('GB', '').trim()) * 1000; - } - if (item['Size'].indexOf('MB') > -1) { - dmi_total += parseFloat(item['Size'].replace('MB', '').trim()); - } - } - } - propreties = { - 'cpus': dmi['data']['Processor Information'].length, - 'memory_mb': dmi_total, - 'local_gb': local_gb - }; - } - node = { - 'name': user_entry.uuid, - 'driver': user_entry.driver, - 'driver_info': info, - 'properties': propreties, - 'extra': extra - }; + }).then(function (localDisk) { + local_gb = localDisk; + return monorail.get_node_memory_cpu(onrack_node) + .catch(function (err) { + var error = { error_message: { message: 'failed to get compute node memory size' } }; + logger.error(err); + throw error; }); - }).then(function () { + }).then(function (dmiData) { + if (local_gb == 0 || dmiData.cpus == 0 || dmiData.memory == 0) { + var error = { error_message: { message: 'failed to get compute node data', nodeDisk: local_gb, memorySize: dmiData.memory, cpuCount: dmiData.cpus } }; + throw (error); + } + propreties = { + 'cpus': dmiData.cpus, + 'memory_mb': dmiData.memory, + 'local_gb': local_gb + }; + node = { + 'name': user_entry.uuid, + 'driver': user_entry.driver, + 'driver_info': info, + 'properties': propreties, + 'extra': extra + }; return (keystone.authenticatePassword(ironicConfig.os_tenant_name, ironicConfig.os_username, ironicConfig.os_password)); }). @@ -420,10 +374,9 @@ module.exports.registerpost = function registerpost(req, res, next) { return ironic.create_node(ironicToken, JSON.stringify(node)); }). then(function (ret) { - logger.info('\r\ncreate node:\r\n' + ret); + logger.debug('\r\ncreate node:\r\n' + ret); if (ret && JSON.parse(ret).error_message) { - res.setHeader('Content-Type', 'application/json'); - res.end(ret); + throw (JSON.parse(ret)); } ironic_node = JSON.parse(ret); port = { 'address': user_entry.port, 'node_uuid': ironic_node.uuid }; @@ -436,9 +389,7 @@ module.exports.registerpost = function registerpost(req, res, next) { then(function (pwr_state) { logger.info('\r\npwr_state: on'); if (pwr_state && JSON.parse(pwr_state).error_message) { - console.error(JSON.parse(pwr_state).error_message); - res.setHeader('Content-Type', 'application/json'); - res.end(pwr_state); + throw (JSON.parse(pwr_state)); } }).then(function () { var timer = {}; @@ -452,7 +403,8 @@ module.exports.registerpost = function registerpost(req, res, next) { }). then(function (result) { logger.info('\r\patched node:\r\n' + result); - }).then(function () { + }). + then(function () { return monorail.request_whitelist_set(user_entry.port) }). then(function (whitelist) { @@ -460,8 +412,8 @@ module.exports.registerpost = function registerpost(req, res, next) { res.setHeader('Content-Type', 'application/json'); res.end(whitelist); }) - .catch(function(err){ - logger.error({message:err,path:req.url}); + .catch(function (err) { + logger.error({ message: err, path: req.url }); res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(err)); }); @@ -472,36 +424,36 @@ module.exports.registerpost = function registerpost(req, res, next) { * @apiVersion 1.1.0 */ module.exports.unregisterdel = function unregisterdel(req, res, next) { + var ironicToken; return keystone.authenticatePassword(ironicConfig.os_tenant_name, ironicConfig.os_username, ironicConfig.os_password). then(function (token) { - token = JSON.parse(token).access.token.id;; - return ironic.delete_node(token, req.swagger.params.identifier.value); + ironicToken = JSON.parse(token).access.token.id; + return ironic.get_node(ironicToken, req.swagger.params.identifier.value); + }). + then(function (ironicNode) { + if (JSON.parse(ironicNode).hasOwnProperty('extra')) { + return monorail.request_whitelist_del(JSON.parse(ironicNode).extra.nodeid); + } + else { + throw { error_message: 'Node is not registered with Shovel' }; + } + }). + then(function () { + return ironic.delete_node(ironicToken, req.swagger.params.identifier.value); }). then(function (del_node) { if (del_node && JSON.parse(del_node).error_message) { - logger.info(del_node); - res.setHeader('Content-Type', 'application/json'); - res.end(del_node); - return; + throw (del_node); } - return monorail.request_node_get(req.swagger.params.identifier.value); - }). - then(function (onrack_node) { - if (onrack_node && !JSON.parse(onrack_node).name) { - logger.info(onrack_node); + else { + logger.debug('ironicNode: ' + req.swagger.params.identifier.value + ' is been deleted susccessfully'); res.setHeader('Content-Type', 'application/json'); - res.end(onrack_node); - return; + var success = { + result: 'success' + }; + res.end(JSON.stringify(success)); } - return monorail.request_whitelist_del(JSON.parse(onrack_node).name); - }). - then(function (whitelist) { - res.setHeader('Content-Type', 'application/json'); - var success = { - result: 'success' - }; - res.end(JSON.stringify(success)); }) .catch(function (err) { logger.error({ message: err, path: req.url }); @@ -551,14 +503,14 @@ module.exports.configsetironic = function configsetironic(req, res, next) { if (req.body.hasOwnProperty('os_password')) { var password = req.body.os_password; //replace password with encrypted value - try{ + try { req.body.os_password = encryption.encrypt(password); } catch (err) { logger.error(err); res.end('failed to update ironic config'); } - } + } if (setConfig('ironic', req.body)) { res.end('success'); } @@ -599,19 +551,19 @@ module.exports.configsetglance = function configsetglance(req, res, next) { * @apiVersion 1.1.0 */ module.exports.configset = function configset(req, res, next) { - res.setHeader('content-type', 'text/plain'); - if (setConfig('shovel', req.body) == true) { - res.end('success'); - } - else { - res.end('failed to update shovel config'); - }; + res.setHeader('content-type', 'text/plain'); + if (setConfig('shovel', req.body) == true) { + res.end('success'); + } + else { + res.end('failed to update shovel config'); + }; }; function setConfig(keyValue, entry) { - var filename = require('path').dirname(require.main.filename) + '/config.json'; - try { - jsonfile.readFile(filename, function (err, output) { + var filename = 'config.json'; + jsonfile.readFile(filename, function (err, output) { + try { var content = (keyValue == null) ? output : output[keyValue]; var filteredList = _.pick(content, Object.keys(entry)); _.each(Object.keys(filteredList), function (key) { @@ -621,14 +573,15 @@ function setConfig(keyValue, entry) { }); output[keyValue] = content; jsonfile.writeFile(filename, output, { spaces: 2 }, function (err) { - logger.info(content); + logger.debug(content); }); - }); - } - catch (err) { - logger.error(err); - return false; - } + } + catch (err) { + logger.error(err); + return false; + } + }); + return true; } @@ -638,29 +591,34 @@ function setConfig(keyValue, entry) { * @apiVersion 1.1.0 */ module.exports.configget = function configget(req, res, next) { - var fs = require('fs'); - var path = require('path'); - var appDir = path.dirname(require.main.filename); - var file_content = fs.readFileSync(appDir + '/config.json'); - var content = JSON.parse(file_content); - delete content['key']; - if (content.ironic.hasOwnProperty("os_password")){ - content.ironic.os_password = '[REDACTED]'; - } - if (content.glance.hasOwnProperty("os_password")) { - content.glance.os_password = '[REDACTED]'; - } - res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify(content)); - }; + var filename = 'config.json'; + jsonfile.readFile(filename, function (err, content) { + try { + + delete content['key']; + if (content.ironic.hasOwnProperty("os_password")) { + content.ironic.os_password = '[REDACTED]'; + } + if (content.glance.hasOwnProperty("os_password")) { + content.glance.os_password = '[REDACTED]'; + } + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify(content)); + } + catch (err) { + logger.error(err); + res.end(); + }; + }); +}; /* * @api {get} /api/1.1/glance/images / GET / * @apiDescription get glance images */ module.exports.imagesGet = function imagesGet(req, res, next) { - return keystone.authenticatePassword(glanceConfig.os_tenant_name,glanceConfig.os_username, - glanceConfig.os_password ). + return keystone.authenticatePassword(glanceConfig.os_tenant_name, glanceConfig.os_username, + glanceConfig.os_password). then(function (token) { token = JSON.parse(token).access.token.id; return glance.get_images(token); diff --git a/Shovel/lib/api/monorail/monorail.js b/Shovel/lib/api/monorail/monorail.js index d14bea6..38e936b 100644 --- a/Shovel/lib/api/monorail/monorail.js +++ b/Shovel/lib/api/monorail/monorail.js @@ -1,6 +1,7 @@ var config = require('./../../../config.json'); var client = require('./../client'); var Promise = require('bluebird'); +var _ = require('underscore'); Promise.promisifyAll(client); var pfx = '/api/' + config.monorail.version; @@ -48,6 +49,90 @@ var MonorailWrapper = { request_poller_data_get: function (identifier, ret) { request.path = pfx + '/pollers/' + identifier + '/data/current'; return client.GetAsync(request); + }, + lookupCatalog: function lookupCatalog(node) { + var self = this; + return self.get_catalog_data_by_source(node.id, 'dmi') + .then(function (dmi) { + if (!_.has(JSON.parse(dmi), 'data')) { + return false; + } + }) + .then(function () { + return self.get_catalog_data_by_source(node.id, 'lsscsi') + }) + .then(function (lsscsi) { + if (!_.has(JSON.parse(lsscsi), 'data')) { + return false; + } + }) + .then(function () { + return self.get_catalog_data_by_source(node.id, 'bmc') + }) + .then(function (bmc) { + if (!_.has(JSON.parse(bmc), 'data')) { + return false; + } + else { + return true; + } + }) + .catch(function (err) { + return false; + }) + }, + nodeDiskSize: function nodeDiskSize(node) { + var local_gb = 0; + var self = this; + return self.get_catalog_data_by_source(node.id, 'lsscsi'). + then(function (scsi) { + scsi = JSON.parse(scsi); + if (scsi.data) { + for (var elem in scsi.data) { + var item = (scsi.data[elem]); + if (item['peripheralType'] == 'disk') { + local_gb += parseFloat(item['size'].replace('GB', '').trim()); + } + } + } + return Promise.resolve(local_gb); + }) + .catch(function (err) { + throw err; + }) + }, + get_node_memory_cpu: function get_node_memory_cpu(computeNode) { + var self = this; + var dmiData = { cpus: 0, memory: 0 }; + return self.get_catalog_data_by_source(computeNode.id, 'dmi'). + then(function (dmi) { + dmi = JSON.parse(dmi); + if (dmi.data) { + var dmi_total = 0; + if (dmi.data['Memory Device']) { + var memory_device = dmi.data['Memory Device']; + for (var elem in memory_device) { + var item = memory_device[elem]; + //logger.info(item['Size']); + if (item['Size'].indexOf('GB') > -1) { + dmi_total += parseFloat(item['Size'].replace('GB', '').trim()) * 1000; + } + if (item['Size'].indexOf('MB') > -1) { + dmi_total += parseFloat(item['Size'].replace('MB', '').trim()); + } + } + dmiData.memory = dmi_total; + } + if (dmi['data'].hasOwnProperty('Processor Information')) { + dmiData.cpus = dmi['data']['Processor Information'].length; + } + } + return Promise.resolve(dmiData); + }) + .catch(function (err) { + throw err; + }) } + }; module.exports = Object.create(MonorailWrapper); \ No newline at end of file diff --git a/Shovel/package.json b/Shovel/package.json index 060c921..2ac825f 100644 --- a/Shovel/package.json +++ b/Shovel/package.json @@ -1,33 +1,35 @@ { - "name": "", - "version": "1.0.0", - "description": "", - "main": "index.js", - "keywords": [ - "swagger" - ], - "license": "MIT", - "private": true, - "dependencies": { - "connect": "^3.2.0", - "swagger-tools": "0.8.*", - "bluebird": "" - }, - "devDependencies": { - "should": "~7.0.1", - "mocha": "^2.1.0", - "sinon": "1.16.1", - "sinon-as-promised": "^2.0.3", - "sinon-chai": "^2.7.0", - "supertest": "^0.15.0", - "underscore": "^1.8.3", - "xunit-file": "0.0.6", - "winston": "2.1.1", - "jsonfile": "2.2.3", - "crypto": "0.0.3" - }, - "scripts" :{ - "postinstall": "scripts/post-install.sh", - "start" : "start shovel" - } + "name": "", + "version": "1.0.0", + "description": "", + "main": "index.js", + "keywords": [ + "swagger" + ], + "license": "MIT", + "private": true, + "dependencies": { + "connect": "^3.2.0", + "swagger-tools": "0.8.*", + "bluebird": "" + }, + "devDependencies": { + "should": "~7.0.1", + "mocha": "^2.1.0", + "sinon": "1.16.1", + "sinon-as-promised": "^2.0.3", + "sinon-chai": "^2.7.0", + "supertest": "^0.15.0", + "underscore": "^1.8.3", + "xunit-file": "0.0.6", + "winston": "2.1.1", + "jsonfile": "2.2.3", + "crypto": "0.0.3", + "istanbul": "0.4.1" + }, + "scripts": { + "postinstall": "scripts/post-install.sh", + "start": "start shovel", + "test": "istanbul cover -x '**/test/**' ./node_modules/.bin/_mocha test/* && istanbul report cobertura" + } } diff --git a/Shovel/test/api/monorail.js b/Shovel/test/api/monorail.js new file mode 100644 index 0000000..850f8e4 --- /dev/null +++ b/Shovel/test/api/monorail.js @@ -0,0 +1,119 @@ +var request = require('supertest'); +var should = require('should'); +var assert = require('assert'); +var sinon = require('sinon'); +var monorail = require('./../../lib/api/monorail/monorail'); +var Promise = require('bluebird'); +var _ = require('underscore'); + + +var rackhdNode = [{ workflows: [], autoDiscover: false, identifiers: ["2c:60:0c:83:f5:d1"], name: "2c:60:0c:83:f5:d1", sku: null, type: "compute", id: "5668b6ad8bee16a10989e4e5" }]; +var catalogSource = [{ source: 'dmi', data: {'Memory Device': [{ Size: '1 GB' }, { Size: '1 GB' }], + 'Processor Information': [{}, {}] }}, { source: 'lsscsi', data: [{ peripheralType: 'disk', size: '1GB' }] }]; +var rackhdNode =[ { workflows: [], autoDiscover: false, identifiers: ["2c:60:0c:83:f5:d1"], name: "2c:60:0c:83:f5:d1", sku: null, type: "compute", id: "5668b6ad8bee16a10989e4e5" }]; +var identifier = '123456789'; + +describe('monorail nodeDiskSize', function () { + + beforeEach('set up mocks', function () { + //monorail + sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify(catalogSource[1]))); + + }); + + afterEach('teardown mocks', function () { + //monorail + monorail['get_catalog_data_by_source'].restore(); + }); + + describe('nodeDiskSize', function () { + it('response should returns an integer with value equal to disk size 1GB', function (done) { + return monorail.nodeDiskSize(rackhdNode). + then(function (result) { + result.should.be.exactly(1); + done(); + }); + }); + }); +}); + +describe('monorail get_node_memory_cpu', function () { + beforeEach('set up mocks', function () { + //monorail + sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify(catalogSource[0]))); + + }); + + afterEach('teardown mocks', function () { + //monorail + monorail['get_catalog_data_by_source'].restore(); + }); + + describe('get_node_memory_cpu', function () { + it('response should returns an integer with value equal to memory size 2000MB and cpus=2', function (done) { + return monorail.get_node_memory_cpu(rackhdNode). + then(function (result) { + result.cpus.should.be.exactly(2); + result.memory.should.be.exactly(2000); + done(); + }) + }); + }); +}); + +describe('lookupCatalog true', function () { + beforeEach('set up mocks', function () { + //monorail + sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify(catalogSource[0]))); + + }); + + afterEach('teardown mocks', function () { + //monorail + monorail['get_catalog_data_by_source'].restore(); + }); + + describe('lookupCatalog', function () { + it('lookupCatalog response should be equal to true', function (done) { + return monorail.lookupCatalog(rackhdNode[0]). + then(function (result) { + console.log(result); + JSON.parse(result).should.be.exactly(true); + done(); + }) + .catch(function (err) { + throw err; + }) + }); + }); +}); + +describe('lookupCatalog false', function () { + beforeEach('set up mocks', function () { + //monorail + sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify({}))); + + }); + + afterEach('teardown mocks', function () { + //monorail + monorail['get_catalog_data_by_source'].restore(); + }); + + describe('lookupCatalog', function () { + it('lookupCatalog response should be equal to fasle if catalog does not have property data', function (done) { + return monorail.lookupCatalog(rackhdNode[0]). + then(function (result) { + console.log(result); + JSON.parse(result).should.be.exactly(false); + done(); + }) + .catch(function (err) { + throw err; + }) + }); + }); + + + +}); \ No newline at end of file diff --git a/Shovel/test/controllers/Shovel.js b/Shovel/test/controllers/Shovel.js new file mode 100644 index 0000000..f868a9d --- /dev/null +++ b/Shovel/test/controllers/Shovel.js @@ -0,0 +1,442 @@ +var request = require('supertest'); +var should = require('should'); +var assert = require('assert'); +var sinon = require('sinon'); +var monorail = require('./../../lib/api/monorail/monorail'); +var ironic = require('./../../lib/api/openstack/ironic'); +var keystone = require('./../../lib/api/openstack/keystone'); +var Promise = require('bluebird'); +var _ = require('underscore'); +var helper = require('./../helper'); +var url = 'http://localhost:9008'; + + + + + +var rackhdNode =[ { workflows: [], autoDiscover: false, identifiers: ["2c:60:0c:83:f5:d1"], name: "2c:60:0c:83:f5:d1", sku: null, type: "compute", id: "5668b6ad8bee16a10989e4e5" }]; +var identifier = '9a761508-4eee-4065-b47b-45c22dff54c2'; +var ironic_node_list = [ { uuid: "9a761508-4eee-4065-b47b-45c22dff54c2", extra: { name: "D51B-2U (dual 10G LoM)", eventre: "", nodeid: "564cefa014ee77be18e48efd", + timer: { start: "2015-11-30T21:14:11.753Z", finish: "2015-11-30T21:14:11.772Z", stop: false, isDone: true, timeInteval: 5000 }, eventcnt: 0 } }]; +var nodePollers = [{ config: { command: "sel" }, id: "564dd86285fb1e7c72721543" }]; +var _sel = { sel:[ { logId: "1", date: "12/03/2015", time: "08:54:11", sensorType: "Memory", sensorNumber: "#0x53", event: "Correctable ECC", value: "Asserted"} ] }; +var keyToken = { access:{ token:{id:'123456'} } }; +var selEvent = { message: "There is no cache record for the poller with ID 564cf02a4978dadc187976f5.Perhaps it has not been run yet?" }; +var extraPatch = {extra: {name: "QuantaPlex T41S-2U", eventre: "Correctable ECC",nodeid: "565f3f3b4c95bce26f35c6a0", + timer: {timeInterval: 15000, start: "2015-12-03T17:38:20.569Z",finish: "2015-12-03T17:38:20.604Z",stop: false,isDone: true} } }; +var patchedData = [{ 'path': '/extra', 'value': extraPatch.extra, 'op': 'replace' }]; +var catalog = [{ node: "9a761508-4eee-4065-b47b-45c22dff54c2", source: "dmi", data: {} }]; +var ironicDrivers = { drivers: [{ "hosts": ["localhost"], "name": "pxe_ssh", "links": [] }] }; +var ironicChassis = { uuid: "1ac07daf-264e-4bd5-b0c4-d53095c217ac", link: [], extra: {}, created_at: "", "nodes": [], description: "ironic test chassis" }; +var catalogSource = [{ source: 'dmi', data: {'Memory Device': [{ Size: '1 GB' }, { Size: '1 GB' }], + 'Processor Information': [{}, {}] }}, { source: 'lsscsi', data: [{ peripheralType: 'disk', size: '1GB' }] }]; + +describe('Shovel api unit testing', function () { + var dmiData = { cpus: 1, memory: 1 }; + + before('start HTTP server', function () { + helper.startServer(); + }); + + beforeEach('set up mocks', function () { + //monorail + sinon.stub(monorail, 'request_node_get').returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); + sinon.stub(monorail, 'request_nodes_get').returns(Promise.resolve(JSON.stringify(rackhdNode))); + sinon.stub(monorail, 'lookupCatalog').returns(Promise.resolve(true)); + sinon.stub(monorail, 'request_catalogs_get').returns(Promise.resolve(JSON.stringify(catalog))); + sinon.stub(monorail, 'request_poller_get').returns(Promise.resolve(JSON.stringify(nodePollers))); + sinon.stub(monorail, 'request_poller_data_get').returns(Promise.resolve(JSON.stringify(_sel))); + sinon.stub(monorail, 'request_whitelist_del').returns(Promise.resolve('')); + sinon.stub(monorail, 'nodeDiskSize').returns(Promise.resolve(0)); + sinon.stub(monorail, 'get_node_memory_cpu').returns(Promise.resolve(dmiData)); + sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify(catalogSource[0]))); + //keystone + sinon.stub(keystone, 'authenticatePassword').returns(Promise.resolve(JSON.stringify(keyToken))); + //ironic + sinon.stub(ironic, 'get_driver_list').returns(Promise.resolve(JSON.stringify(ironicDrivers))); + sinon.stub(ironic, 'get_chassis_by_id').returns(Promise.resolve(JSON.stringify(ironicChassis))); + sinon.stub(ironic, 'patch_node').returns(Promise.resolve(JSON.stringify(extraPatch))); + sinon.stub(ironic, 'get_node_list').returns(Promise.resolve(JSON.stringify(ironic_node_list))); + sinon.stub(ironic, 'get_node').returns(Promise.resolve(JSON.stringify(ironic_node_list[0]))); + sinon.stub(ironic, 'delete_node').returns(Promise.resolve('')); + }); + + afterEach('teardown mocks', function () { + //monorail + monorail['request_node_get'].restore(); + monorail['request_nodes_get'].restore(); + monorail['request_poller_get'].restore(); + monorail['request_poller_data_get'].restore(); + monorail['request_catalogs_get'].restore(); + monorail['lookupCatalog'].restore(); + monorail['request_whitelist_del'].restore(); + monorail['nodeDiskSize'].restore(); + monorail['get_node_memory_cpu'].restore(); + monorail['get_catalog_data_by_source'].restore(); + //ironic + ironic['patch_node'].restore(); + ironic['get_node_list'].restore(); + ironic['get_node'].restore(); + ironic['get_chassis_by_id'].restore(); + ironic['get_driver_list'].restore(); + ironic['delete_node'].restore(); + //keystone + keystone['authenticatePassword'].restore(); + }); + + after('stop HTTP server', function () { + helper.stopServer(); + }); + + describe('shovel-info', function () { + it('response should have property \'name\': \'shovel\'', function (done) { + request(url) + .get('/api/1.1/info') + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + // this is should.js syntax, very clear + JSON.parse(res.text).should.have.property('name', 'shovel'); + done(); + }); + }); + }); + + describe('shovel-catalogs/{identifier}', function () { + it('in case of a correct rackHD id, response should include property node, source and data', function (done) { + request(url) + .get('/api/1.1/catalogs/' + identifier) + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + // this is should.js syntax, very clear + JSON.parse(res.text)[0].should.have.property('node', identifier); + JSON.parse(res.text)[0].should.have.property('data'); + JSON.parse(res.text)[0].should.have.property('source'); + done(); + }); + }); + }); + + describe('shovel-catalogs/{identifier}/source', function () { + it('in case of a correct rackHD id, response should include property source and data', function (done) { + request(url) + .get('/api/1.1/catalogs/' + identifier + '/dmi') + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + // this is should.js syntax, very clear + JSON.parse(res.text).should.have.property('data'); + JSON.parse(res.text).should.have.property('source'); + done(); + }); + }); + }); + + describe('shovel-nodes/{identifier}', function () { + it('in case of correct id, response should include property: id,identifiers', function (done) { + request(url) + .get('/api/1.1/nodes/' + identifier) + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + // this is should.js syntax, very clear + JSON.parse(res.text).should.have.property('id'); + JSON.parse(res.text).should.have.property('identifiers'); + done(); + }); + }); + }); + + describe('shovel-ironic/chassis/{identifier}', function () { + it('in case of a correct id, response should include property: uuid , description ', function (done) { + request(url) + .get('/api/1.1/ironic/chassis/' + identifier) + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + // this is should.js syntax, very clear + JSON.parse(res.text).should.have.property('uuid'); + JSON.parse(res.text).should.have.property('description'); + done(); + }); + }); + }); + + describe('shovel-ironic/drivers', function () { + it('response should have property \'drivers\'', function (done) { + request(url) + .get('/api/1.1/ironic/drivers') + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + // this is should.js syntax, very clear + JSON.parse(res.text).should.have.property('drivers'); + done(); + }); + }); + }); + + describe('shovel-ironic/nodes', function () { + it('response should have property \'uuid\'', function (done) { + request(url) + .get('/api/1.1/ironic/nodes') + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + // this is should.js syntax, very clear + JSON.parse(res.text)[0].should.have.property('uuid'); + done(); + }); + }); + }); + + describe('shovel-ironic/nodes/identifier', function () { + it('response should have property \'uuid\'', function (done) { + request(url) + .get('/api/1.1/ironic/nodes/' + identifier) + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + // this is should.js syntax, very clear + JSON.parse(res.text).should.have.property('uuid'); + done(); + }); + }); + }); + + describe('shovel-ironic/patch', function () { + it('response should have property nodeid timer', function (done) { + var body = {}; + request(url) + .patch('/api/1.1/ironic/node/' + identifier) + .send(body) + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + // this is should.js syntax, very clear + JSON.parse(res.text).extra.should.have.property('nodeid'); + JSON.parse(res.text).extra.should.have.property('timer'); + done(); + }); + }); + }); + + describe('shovel-nodes', function () { + it('response should have property "identifiers"', function (done) { + request(url) + .get('/api/1.1/nodes') + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + // this is should.js syntax, very clear + for (item in JSON.parse(res.text)) { + JSON.parse(res.text)[item].should.have.property('identifiers'); + } + done(); + }); + }); + }); + + describe('shovel-unregister/{identifier}', function () { + it('if ironic id exist, response should include property: result: success', function (done) { + request(url) + .delete('/api/1.1/unregister/' + identifier) + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + // this is should.js syntax, very clear + JSON.parse(res.text).result.should.be.equal('success'); + done(); + }); + }); + }); + + describe('shovel-getconfig', function () { + it('', function (done) { + request(url) + .get('/api/1.1/shovel/config') + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('shovel'); + JSON.parse(res.text).should.have.property('ironic'); + JSON.parse(res.text).should.have.property('glance'); + JSON.parse(res.text).should.have.property('keystone') + done(); + }); + }); + }); + + describe('shovel-set ironic config', function () { + it('', function (done) { + var body = { httpHost: "localhost" }; + request(url) + .post('/api/1.1/shovel/ironic/set-config') + .send(body) + .expect('Content-Type', /text/) + .expect(200) //Status code + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + res.text.should.be.exactly('success'); + done(); + }); + }); + }); + + describe('shovel-set monorail config', function () { + it('', function (done) { + var body = { httpHost: "localhost" }; + request(url) + .post('/api/1.1/shovel/monorail/set-config') + .send(body) + .expect('Content-Type', /text/) + .expect(200) //Status code + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + res.text.should.be.exactly('success'); + done(); + }); + }); + }); + + describe('shovel-set glance config', function () { + it('', function (done) { + var body = { httpHost: "localhost" }; + request(url) + .post('/api/1.1/shovel/glance/set-config') + .send(body) + .expect('Content-Type', /text/) + .expect(200) //Status code + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + res.text.should.be.exactly('success'); + done(); + }); + }); + }); + + describe('shovel-set keystone config', function () { + it('', function (done) { + var body = { httpHost: "localhost" }; + request(url) + .post('/api/1.1/shovel/keystone/set-config') + .send(body) + .expect('Content-Type', /text/) + .expect(200) //Status code + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + res.text.should.be.exactly('success'); + done(); + }); + }); + }); +}); + +describe('Shovel api register', function () { + var error_message = '{"error_message": "{\\"debuginfo\\": null, \\"faultcode\\": \\"Client\\", \\"faultstring\\": \\"A node with name 5668b42d8bee16a10989e4e4 already exists.\\"}"}'; + var body = { "id": identifier, "driver": "string", "ipmihost": "string", "ipmiusername": "string", "ipmipasswd": "string" }; + + + beforeEach('set up mocks', function () { + //monorail + //keystone + sinon.stub(keystone, 'authenticatePassword').returns(Promise.resolve(JSON.stringify(keyToken))); + //ironic + sinon.stub(ironic, 'create_node').returns(Promise.resolve(error_message)); + }); + + afterEach('teardown mocks', function () { + //monorail + monorail['nodeDiskSize'].restore(); + monorail['get_node_memory_cpu'].restore(); + monorail['request_node_get'].restore(); + //keystone + keystone['authenticatePassword'].restore(); + //ironic + ironic['create_node'].restore(); + + }); + + describe('shovel-resgister', function () { + it('response in register should have property error_message when any of node info equal to 0 ', function (done) { + sinon.stub(monorail, 'request_node_get').returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); + sinon.stub(monorail, 'nodeDiskSize').returns(Promise.resolve(0)); + sinon.stub(monorail, 'get_node_memory_cpu').returns(Promise.resolve({ cpus: 0, memory: 0 })); + + request(url) + .post('/api/1.1/register') + .send(body) + .expect('Content-Type', /json/) + .expect(200) //Status code + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + // this is should.js syntax, very clear + JSON.parse(res.text).should.have.property('error_message'); + + done(); + }); + }); + it('response in register should have property error_message create node return error in ironic', function (done) { + sinon.stub(monorail, 'request_node_get').returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); + sinon.stub(monorail, 'nodeDiskSize').returns(Promise.resolve(1)); + sinon.stub(monorail, 'get_node_memory_cpu').returns(Promise.resolve({ cpus: 1, memory: 1 })); + + request(url) + .post('/api/1.1/register') + .send(body) + .expect('Content-Type', /json/) + .expect(200) //Status code + // end handles the response + .end(function (err, res) { + if (err) { + throw err; + } + // this is should.js syntax, very clear + JSON.parse(res.text).should.have.property('error_message'); + + done(); + }); + }); + }); + +}); diff --git a/Shovel/test/services/poller.js b/Shovel/test/services/poller.js index 834a4ca..6f9684d 100644 --- a/Shovel/test/services/poller.js +++ b/Shovel/test/services/poller.js @@ -11,68 +11,20 @@ var _ = require('underscore'); describe('Shovel poller unit testing', function () { + var rackhdNode =[ { workflows: [], autoDiscover: false, identifiers: ["2c:60:0c:83:f5:d1"], name: "2c:60:0c:83:f5:d1", sku: null, type: "compute", id: "5668b6ad8bee16a10989e4e5" }]; var identifier = '9a761508-4eee-4065-b47b-45c22dff54c2'; - var ironic_node_list = [ - { - uuid: "9a761508-4eee-4065-b47b-45c22dff54c2", - extra: { - name: "D51B-2U (dual 10G LoM)", - eventre: "", - nodeid: "564cefa014ee77be18e48efd", - timer: { - start: "2015-11-30T21:14:11.753Z", - finish: "2015-11-30T21:14:11.772Z", - stop: false, - isDone: true, - timeInteval: 5000 - }, - eventcnt: 0 - } - } - ] - var nodePollers = [{ - config: { - command: "sel" - }, - id: "564dd86285fb1e7c72721543" - }] - var _sel = { - sel:[ - { - logId: "1", - date: "12/03/2015", - time: "08:54:11", - sensorType: "Memory", - sensorNumber: "#0x53", - event: "Correctable ECC", - value: "Asserted" - } - ] - }; - var keyToken = { - access:{ - token:{id:'123456'} - } - }; + var ironic_node_list = [ { uuid: "9a761508-4eee-4065-b47b-45c22dff54c2", extra: { name: "D51B-2U (dual 10G LoM)", eventre: "", nodeid: "564cefa014ee77be18e48efd", + timer: { start: "2015-11-30T21:14:11.753Z", finish: "2015-11-30T21:14:11.772Z", stop: false, isDone: true, timeInteval: 5000 }, eventcnt: 0 } }]; + var nodePollers = [{ config: { command: "sel" }, id: "564dd86285fb1e7c72721543" }]; + var _sel = { sel:[ { logId: "1", date: "12/03/2015", time: "08:54:11", sensorType: "Memory", sensorNumber: "#0x53", event: "Correctable ECC", value: "Asserted"} ] }; + var keyToken = { access:{ token:{id:'123456'} } }; var selEvent = { message: "There is no cache record for the poller with ID 564cf02a4978dadc187976f5.Perhaps it has not been run yet?" }; - var extraPatch = { - extra: { - name: "QuantaPlex T41S-2U", - eventre: "Correctable ECC", - nodeid: "565f3f3b4c95bce26f35c6a0", - timer: { - timeInterval: 15000, - start: "2015-12-03T17:38:20.569Z", - finish: "2015-12-03T17:38:20.604Z", - stop: false, - isDone: true - } - } - }; + var extraPatch = {extra: {name: "QuantaPlex T41S-2U", eventre: "Correctable ECC",nodeid: "565f3f3b4c95bce26f35c6a0", + timer: {timeInterval: 15000, start: "2015-12-03T17:38:20.569Z",finish: "2015-12-03T17:38:20.604Z",stop: false,isDone: true} } }; var patchedData = [{ 'path': '/extra', 'value': extraPatch.extra, 'op': 'replace' }]; var pollerInstance; - before('start HTTP server', function () { + before('start Poller service', function () { pollerInstance = new Poller(5000);//timeInterval to 5s }); @@ -80,15 +32,15 @@ describe('Shovel poller unit testing', function () { sinon.stub(monorail, 'request_poller_get').returns(Promise.resolve(JSON.stringify(nodePollers))); sinon.stub(monorail, 'request_poller_data_get').returns(Promise.resolve(JSON.stringify(_sel))); sinon.stub(ironic,'patch_node').returns(Promise.resolve(JSON.stringify(extraPatch))); - sinon.stub(keystone,'authenticate').returns(Promise.resolve(JSON.stringify(keyToken))); - sinon.stub(ironic,'get_node_list').returns(Promise.resolve(JSON.stringify(ironic_node_list))) + sinon.stub(keystone,'authenticatePassword').returns(Promise.resolve(JSON.stringify(keyToken))); + sinon.stub(ironic,'get_node_list').returns(Promise.resolve(JSON.stringify(ironic_node_list))); }); afterEach('teardown mocks', function () { monorail['request_poller_get'].restore(); monorail['request_poller_data_get'].restore(); ironic['patch_node'].restore(); - keystone['authenticate'].restore(); + keystone['authenticatePassword'].restore(); ironic['get_node_list'].restore(); }); @@ -104,8 +56,8 @@ describe('Shovel poller unit testing', function () { item.should.have.property('event'); }); done(); - }). - catch (function(err){ + }) + .catch (function(err){ throw(err); }); }); @@ -119,8 +71,8 @@ describe('Shovel poller unit testing', function () { item.should.have.property('value'); }); done(); - }). - catch(function(err){ + }) + .catch(function(err){ throw err; }); }); @@ -131,8 +83,8 @@ describe('Shovel poller unit testing', function () { data.should.have.property('nodeid'); data.should.have.property('timer'); done(); - }). - catch(function(err){ + }) + .catch(function(err){ throw(err); done(); }); @@ -146,8 +98,8 @@ describe('Shovel poller unit testing', function () { item.should.have.property('extra'); }); done(); - }). - catch(function(err){ + }) + .catch(function(err){ throw(err); }); }); @@ -157,8 +109,8 @@ describe('Shovel poller unit testing', function () { then(function(token){ token.should.be.equal('123456'); done(); - }). - catch(function(err){ + }) + .catch(function(err){ throw err; });