UNPKG

cloud-blender

Version:

A high level library for cloud compute operations

533 lines (442 loc) 19.4 kB
var request = require('request'), underscore = require('underscore'); // This module implements some of the hp cloud compute functionality. // The rest api is taken from hp cloud documentation (http://hpcloud.com) // (some of the api is broken - like in createNodes) // The function names are inspired by libcloud with minor differences. module.exports = (function() { // internal members // ---------------- var proxyURL; // internal functions // ------------------ // The connect method is being called in the beginning // of each high level function (listNodes, createNodes etc.) // some of these high level function calls to other high level functions // (polling for example). In order to prevent many unnecessary rest // calls, we save the identityToken in the identitySettings // and we check the existence and expiration date of the token. // // The best practice is that the caller of this function // will also save this token in the session (so we can save rest calls) // parameters: // ----------- // identitySettings should contain auth object, identity url and tenantid function connect(identitySettings, callback) { // the threshold is 1 hour to be on the safe side // (the token expires every 12hours) var DIFF_THRESH_HOURS = 1, expires = '', hourDiff = 0, requestSettings; if ('identityToken' in identitySettings && 'access' in identitySettings.identityToken && 'token' in identitySettings.identityToken.access && 'expires' in identitySettings.identityToken.access.token) { expires = identitySettings.identityToken.access.token.expires; hourDiff = (new Date(expires).getTime() - new Date().getTime()) / 1000 / 60 / 60; if (hourDiff > DIFF_THRESH_HOURS) { callback(null, identitySettings.identityToken); return; } } requestSettings = { method: 'POST', url: identitySettings.url, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({auth: identitySettings.auth}), proxy: proxyURL, timeout: 120000 }; request(requestSettings, function(error, response, bodyString) { var identityToken, normalResponseCode = '200'; if (error !== null || (typeof (bodyString) !== 'string') || (response.statusCode + '' !== normalResponseCode)) { callback(new Error('cannot retrieve token from hp cloud. reason: ' + (response?response.statusCode:' empty response - probably bad tunneling proxy'))); return; } identityToken = JSON.parse(bodyString); if ((('access' in identityToken) === false) || (('token' in identityToken.access) === false)) { callback(new Error('invalid identityToken structure: ' + JSON.stringify(identityToken))); return; } identitySettings.identityToken = identityToken; callback(null, identityToken); }); } function createSimpleNodeData(rawNode) { var node = { id: rawNode.id, status: rawNode.status, tags: rawNode.metadata, addresses: [undefined, undefined], releaseInfo: {} }; if (rawNode.addresses['private']) { if (rawNode.addresses['private'][0]) { node.addresses[0] = rawNode.addresses['private'][0].addr; } if (rawNode.addresses['private'][1]) { node.addresses[1] = rawNode.addresses['private'][1].addr; } } return node; } var that = { setProxy: function(proxyUrl) { proxyURL = proxyUrl; }, createPreparation: function (settings, callback) { callback(null,null); }, createRegionContext: function(authSettings, limits) { return { identitySettings: { auth: { apiAccessKeyCredentials: { "accessKey": authSettings.accessKey, "secretKey": authSettings.secretKey }, tenantId: authSettings.tenantId }, url: 'https://' + authSettings.region + '.identity.hpcloudsvc.com:35357/v2.0/tokens' }, computeSettings: { url: 'https://' + authSettings.availabilityZone + '.' + authSettings.region + '.compute.hpcloudsvc.com/v1.1/' + authSettings.tenantId }, limits: limits, providerName: 'hpcs', pollingCount: 180 }; }, createNode: function(settings, cloudServicesTestSettings, nodeIndex, callback) { connect(settings.regionContext.identitySettings, function(errorConnect, identityToken) { var createNodeSettings, nodeParams = { name: new Date().valueOf() + '-createdByStorm', imageRef: settings.nodeParams.imageId, flavorRef: settings.nodeParams.instanceType, metadata: settings.nodeParams.tags, key_name: settings.nodeParams.keyName }, securityGroups = settings.nodeParams.securityGroups, userData = settings.nodeParams.userData; if (errorConnect) { callback(errorConnect, {}); return; } // adding securityGroups if (securityGroups) { nodeParams.security_groups = []; underscore.each(securityGroups, function(securityGroup) { nodeParams.security_groups.push({"name": securityGroup}); }); } // adding user data if (userData) { nodeParams.user_data = new Buffer(JSON.stringify(userData)).toString('base64'); } // vendor specific extension must be last underscore.extend(nodeParams, settings.nodeParams.vendorSpecificParams); createNodeSettings = { method: 'POST', url: settings.regionContext.computeSettings.url + '/servers', headers: { 'X-Auth-Token': identityToken.access.token.id, 'Content-Type': 'application/json', 'Accept': 'application/json' }, proxy: proxyURL, body: JSON.stringify({server: nodeParams}), timeout: 120000 }; request(createNodeSettings, function(error, response, bodyString) { var nodeData, normalResponseCode = '202', finalResult = {}, errorCreate; finalResult.rawResult = bodyString; // may not be json if something bad happens if (error === null && (response.statusCode + '' === normalResponseCode)) { if (typeof (bodyString) === 'string') { finalResult.rawResult = JSON.parse(bodyString); } nodeData = finalResult.rawResult.server; finalResult.node = createSimpleNodeData(nodeData); } else{ finalResult.node = {logicName: settings.nodeParams.logicName, status: 'ERROR'}; errorCreate = new Error('can not createNode with paramters: ' + JSON.stringify(settings.nodeParams) + '. statusCode: ' + (response?response.statusCode:'undefined') + ' ,body string' + bodyString); } callback(errorCreate, finalResult); }); }); }, listNodes: function(settings, callback) { connect(settings.regionContext.identitySettings, function(errorConnect, identityToken) { var listRequestSettings; if (errorConnect) { callback(errorConnect, {}); return; } listRequestSettings = { method: 'GET', url: settings.regionContext.computeSettings.url + '/servers/detail', headers: { 'X-Auth-Token': identityToken.access.token.id, 'Accept': 'application/json' }, proxy: proxyURL, timeout: 120000 }; request(listRequestSettings, function(error, response, bodyString) { var finalResults = { nodes: [] }, normalResponseCodes = { '200': '1', '203': '1' }, errorList; if (typeof (bodyString) === 'string') { finalResults.rawResult = JSON.parse(bodyString); } if (error === null && (typeof (bodyString) === 'string') && (normalResponseCodes[response.statusCode + ''] === '1')) { underscore.each(finalResults.rawResult.servers, function(server) { finalResults.nodes.push(createSimpleNodeData(server)); }); } else { errorList = new Error('cannot retrieve node machines list from hp cloud. reason: ' + '. statusCode: ' + (response?response.statusCode:'undefined')); } callback(errorList, finalResults); }); }); }, deleteNode: function(settings, callback) { connect(settings.regionContext.identitySettings, function(errorConnect, identityToken) { var deleteRequestSettings; if (errorConnect) { callback(errorConnect, {}); return; } deleteRequestSettings = { method: 'DELETE', headers: { 'X-Auth-Token': identityToken.access.token.id, 'Accept': 'application/json' }, url: settings.regionContext.computeSettings.url + '/servers/' + settings.node.id, proxy: proxyURL, timeout: 120000 }; request(deleteRequestSettings, function(error, response, bodyString) { var normalResponseCode = '204', finalResult = {rawResult: undefined}, errorDelete; if (error === null && (response.statusCode + '' === normalResponseCode)) { finalResult.result = 'SUCCESS'; } else { finalResult.result = 'ERROR'; errorDelete = new Error('deleteNode failed for id: ' + JSON.stringify(settings.nodeParams) + '. statusCode: ' + (response?response.statusCode:'undefined') + ', request settings: ' + JSON.stringify(deleteRequestSettings)); } callback(errorDelete, finalResult); }); }); }, createImage: function(settings, callback) { connect(settings.regionContext.identitySettings, function(errorConnect, identityToken) { var createImageRequestSettings, imageParams = { name: new Date().valueOf() + '-createdByStorm', serverId: settings.imageParams.nodeId, metadata: settings.imageParams.tags }; if (errorConnect) { callback(errorConnect, {}); return; } underscore.extend(imageParams, settings.imageParams.vendorSpecificParams); createImageRequestSettings = { method: 'POST', url: settings.regionContext.computeSettings.url + '/servers/' + settings.imageParams.nodeId + '/action', headers: { 'X-Auth-Token': identityToken.access.token.id, 'Content-Type': 'application/json', 'Accept': 'application/json' }, proxy: proxyURL, body: JSON.stringify({createImage: imageParams}), timeout: 120000 }; request(createImageRequestSettings, function(errorRequest, response, bodyString) { var normalResponseCode = '202', location = '', imageId = '', finalResult = {}, errorCreate; // console.log('error: ' + JSON.stringify(error) + // ', result: ' + JSON.stringify(bodyString)); if (errorRequest !== null || (response.statusCode + '' !== normalResponseCode) ) { errorCreate = new Error('cannot createImage with params: '+ JSON.stringify(settings.imageParams) + ', error: ' + JSON.stringify(errorRequest) + '. statusCode: ' + (response?response.statusCode:'undefined')); } else { location = response.headers.location; imageId = location.slice(location.lastIndexOf('/') + 1); finalResult.rawResult = location; finalResult.imageId = imageId; } callback(errorCreate, finalResult); }); }); }, listImages: function(settings, callback) { connect(settings.regionContext.identitySettings, function(errorConnect, identityToken) { var requestSettings; if (errorConnect) { callback(errorConnect, {}); return; } requestSettings = { method: 'GET', url: settings.regionContext.computeSettings.url + '/images/detail', headers: { 'X-Auth-Token': identityToken.access.token.id, 'Accept': 'application/json' }, proxy: proxyURL, timeout: 120000 }; request(requestSettings, function(error, response, bodyString) { var imagesData, normalResponseCodes = { '200': '1', '203': '1' }, errorListImage, finalResult = {}; if (error !== null || (typeof (bodyString) !== 'string') || (normalResponseCodes[response.statusCode + ''] !== '1') ) { errorListImage = new Error('cannot retrieve images list from hp cloud. ' + '. statusCode: ' + (response?response.statusCode:'undefined')); } else { imagesData = JSON.parse(bodyString); finalResult.rawResult = imagesData; finalResult.images = []; underscore.each(imagesData.images, function(rawImage) { var image = underscore.pick(rawImage, 'id', 'status', 'name'); image.tags = rawImage.metadata; finalResult.images.push(image); }); } callback(errorListImage, finalResult); }); }); }, deleteImage: function(settings, callback) { connect(settings.regionContext.identitySettings, function(errorConnect, identityToken) { var requestSettings; if (errorConnect) { callback(errorConnect, {}); return; } requestSettings = { url: settings.regionContext.computeSettings.url + '/images/' + settings.imageParams.imageId, method: 'DELETE', headers: { 'X-Auth-Token': identityToken.access.token.id }, proxy: proxyURL, timeout: 120000 }; request(requestSettings, function(error, response, bodyString) { var normalResponseCodes = { '200': '1', '204': '1' }, errorDeleteImage, finalResult = {rawResult: undefined}; if (error !== null || (normalResponseCodes[response.statusCode + ''] !== '1') ) { errorDeleteImage = new Error('cannot deleteImage, error: ' + error + ', code: ' + (response?response.statusCode:'undefined')); finalResult.result = 'ERROR'; } else { finalResult.result = 'SUCCESS'; } callback(errorDeleteImage, finalResult); }); }); }, associateAddress: function(settings, callback){ var error = new Error('no implementation'); callback(error, null); }, disassociateAddress: function(settings, callback){ var error = new Error('no implementation'); callback(error, null); } // getLimits: function(settings, callback) { // connect(settings.regionContext.identitySettings, function(errorConnect, identityToken) { // var requestSettings; // if (errorConnect) { // callback(errorConnect, {}); // return; // } // requestSettings = { // method: 'GET', // url: settings.regionContext.computeSettings.url + '/limits', // headers: { // 'X-Auth-Token': identityToken.access.token.id, // 'Accept': 'application/json' // }, // proxy: proxyURL // }; // request(requestSettings, function(error, response, bodyString) { // var imagesData, // normalResponseCodes = { // '200': '1', // '203': '1' // }, // limits, // finalError; // if (error !== null || (typeof (bodyString) !== 'string') || // (normalResponseCodes[response.statusCode + ''] !== '1') // ) { // finalError = new Error('cannot retrieve images list from hp cloud. ' + // '. statusCode: ' + (response?response.statusCode:'undefined')); // } // else { // limits = JSON.parse(bodyString); // } // callback(finalError, limits); // }); // }); //}, }; return that; })();