@yawetse/pkgcloud
Version:
An infrastructure-as-a-service agnostic cloud library for node.js
417 lines (372 loc) • 12.2 kB
JavaScript
/*
* servers.js: Instance methods for working with servers from OpenStack Cloud
*
* (C) 2013 Charlie Robbins, Ken Perkins, Ross Kukulinski & the Contributors.
*
*/
var base = require('../../../core/compute'),
pkgcloud = require('../../../../../lib/pkgcloud'),
errs = require('errs'),
urlJoin = require('url-join'),
util = require('util'),
_ = require('underscore'),
Server = require('../server').Server,
compute = pkgcloud.providers.openstack.compute;
var _urlPrefix = '/servers';
/**
* validateProperties
*
* @description local helper function for validating arguments
*
* @param {Array} required The list of required properties
* @param {object} options The options object to validate
* @param {String} formatString String formatter for the error message
* @param {Function} callback
* @returns {boolean}
*/
function validateProperties(required, options, formatString, callback) {
return !required.some(function (item) {
if (typeof(options[item]) === 'undefined') {
errs.handle(
errs.create({ message: util.format(formatString, item) }),
callback
);
return true;
}
return false;
});
}
/**
* client._doServerAction
*
* @description exported helper function that is wrapped by a number of other more
* specific functions. Calls the standard server action api for any manner of tasks
*
* @param {object|String} server The server or serverId to get volumes for
* @param {object} body The body of the server action to perform
* @param {function} callback
* @returns {*}
*/
exports._doServerAction = function(server, body, callback) {
var self = this,
serverId = server instanceof Server ? server.id : server;
var actionOptions = {
method: 'POST',
path: urlJoin(_urlPrefix, serverId, 'action'),
body: body
};
return self._request(actionOptions, function (err, body, res) {
return callback(err, body, res);
});
};
/**
* client.getServers
*
* @description get the list of servers for the current account
*
* @param {object|Function} [options] A set of options for the getServers call
* @param {function} callback f(err, servers) where servers is an array of Server
* @returns {*}
*/
exports.getServers = function getServers(options, callback) {
var self = this;
if (typeof options === 'function') {
callback = options;
options = {};
}
var requestOptions = {
path: urlJoin(_urlPrefix, 'detail')
};
requestOptions.qs = _.pick(options,
'image',
'flavor',
'name',
'status',
'marker',
'limit',
'changes-since');
return this._request(requestOptions, function (err, body) {
if (err) {
return callback(err);
}
if (!body || !body.servers) {
return callback(new Error('Unexpected empty response'));
}
else {
return callback(null, body.servers.map(function (result) {
return new compute.Server(self, result);
}));
}
});
};
/**
* client.createServer
*
* @description Creates a server with the specified options. The flavor properties
* of the options can be instances of Flavor OR ids to those entities in OpenStack.
* @param {object} details the details to create this server
* @param {String} details.name the name of the new server
* @param {Object|String} details.flavor the flavor or flavorId for the new server
* @param {Object|String} details.image the image or imageId for the new server
* @param {Object} [details.networks] optional networking configuration
* @param {Object} [details.keyname] optional keyname configuration
* @param {Object} [details.personality] optional personality configuration
* @param {Object} [details.metadata] optional metadata configuration
* @param callback
* @returns {request|*}
*/
exports.createServer = function createServer(details, callback) {
if (typeof details === 'function') {
callback = details;
details = {};
}
details = details || {};
if (!validateProperties(['flavor', 'image', 'name'], details,
'options.%s is a required argument.', callback)) {
return;
}
var self = this,
createOptions = {
method: 'POST',
path: _urlPrefix,
body: {
server: _.pick(details, ['name', 'metadata', 'personality'])
}
};
if (details.flavor) {
createOptions.body.server.flavorRef = details.flavor instanceof base.Flavor
? details.flavor.id
: details.flavor;
}
if (details.image) {
createOptions.body.server.imageRef = details.image instanceof base.Image
? details.image.id
: details.image;
}
if (details.networks) {
createOptions.body.server.networks = details.networks;
}
if (details.keyname) {
createOptions.body.server.key_name = details.keyname;
}
if (details.securityGroups) {
createOptions.body.server.security_groups = details.securityGroups;
}
if (details.cloudConfig) {
createOptions.body.server.user_data = details.cloudConfig;
createOptions.body.server.config_drive = true;
}
return this._request(createOptions, function (err, body) {
if (err) {
return callback(err);
}
if (!body || !body.server) {
return new Error('Server not passed back from OpenStack.');
}
callback(null, new compute.Server(self, {
id: body.server.id,
name: details.name,
adminPass: body.server.adminPass,
flavorId: body.server.flavorRef,
imageId: body.server.imageRef,
personality: body.server.personality
}));
});
};
/**
* client.getServer
*
* @description Gets a server from the account
*
* @param {String|object} server The server or serverId to fetch
* @param {Function} callback
* @returns {request|*}
*/
exports.getServer = function getServer(server, callback) {
var self = this,
serverId = server instanceof base.Server ? server.id : server;
return this._request({
path: urlJoin(_urlPrefix, serverId)
}, function (err, body, res) {
if (err) {
return callback(err);
}
if (!body.server) {
return new Error('Unexpected empty response');
}
else {
callback(null, new compute.Server(self, body.server), res);
}
});
};
/**
* client.destroyServer
*
* @description Delete a server
*
* @param {String|object} server The server or serverId to delete
* @param {Function} callback
* @returns {request|*}
*/
exports.destroyServer = function destroyServer(server, callback) {
var serverId = server instanceof base.Server ? server.id : server;
return this._request({
method: 'DELETE',
path: urlJoin(_urlPrefix, serverId)
}, function (err) {
return err
? callback(err)
: callback(err, { ok: serverId });
});
};
/**
* client.rebootServer
*
* @description Reboot the provider server
*
* @param {String|object} server The server or serverId to reboot
* @param {object} [options] Optionally determine if it's a hard or soft reboot
* @param {String} [options.type] HARD or SOFT reboot. Default is SOFT
* @param {Function} callback
* @returns {*}
*/
exports.rebootServer = function rebootServer(server, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
options.type = options.type ? options.type.toUpperCase() : 'SOFT';
return this._doServerAction.call(this, server, { reboot: options }, callback);
};
/**
* client.rebuildServer
*
* @description Rebuild the provider server
*
* @param {String|object} server The server or serverId to rebuild
* @param {String|object} options The image or imageId to use in the rebuild (for backwards compatibility)
* @param {String|object} options.image The image or imageId to use in the rebuild
* @param {String} options.accessIPv4 The IP version 4 address of the server.
* @param {String} options.accessIPv6 The IP version 6 address of the server.
* @param {String} options.adminPass The administrator password for the server.
* @param {object} options.metadata Metadata key/value pairs.
* @param {array} options.personality Personality files - path and contents
* @param {String} options.personality[n].path Personality file path
* @param {String} options.personality[n].contents Personality file contents
* @param {String} options['OS-DCF:diskConfig'] The disk configuration value ("AUTO" | "MANUAL").
* @param {Function} callback
* @returns {*}
*/
exports.rebuildServer = function rebootServer(server, options, callback) {
var rebuildBody = {};
if (options instanceof base.Image) {
// Image object was passed in as options (backwards compatible)
rebuildBody.imageRef = options.id;
} else if (typeof options === 'object') {
// Several options were passed in as options
rebuildBody = _.pick(options, [
'accessIPv4',
'accessIPv6',
'adminPass',
'metadata',
'personality'
]);
rebuildBody.imageRef = options.image instanceof base.Image ? options.image.id : options.image;
} else {
// Image ID was passed in as options (backwards compatible)
rebuildBody.imageRef = options;
}
this._doServerAction.call(this, server, {
rebuild: rebuildBody
}, callback);
};
/**
* client.resizeServer
*
* @description Resize the provider server
*
* @param {String|object} server The server or serverId to resize
* @param {String|object} flavor The flavor or flavorId to use in the resize
* @param {Function} callback
* @returns {*}
*/
exports.resizeServer = function rebootServer(server, flavor, callback) {
var flavorId = flavor instanceof base.Flavor ? flavor.id : flavor;
this._doServerAction.call(this, server, {
resize: {
flavorRef: flavorId
}
}, callback);
};
/**
* client.confirmServerResize
*
* @description Confirm the resize operation for the provided server
*
* @param {String|object} server The server or serverId
* @param {Function} callback
* @returns {*}
*/
exports.confirmServerResize = function (server, callback) {
this._doServerAction.call(this, server, { confirmResize: null }, callback);
};
/**
* client.revertServerResize
*
* @description Revert the resize operation for the provided server
*
* @param {String|object} server The server or serverId
* @param {Function} callback
* @returns {*}
*/
exports.revertServerResize = function (server, callback) {
this._doServerAction.call(this, server, { revertResize: null }, callback);
};
/**
* client.renameServer
*
* @description Rename the provided server
*
* @param {String|object} server The server or serverId
* @param {String} name The new name for the server
* @param {Function} callback
* @returns {*}
*/
exports.renameServer = function (server, name, callback) {
var serverId = server instanceof base.Server ? server.id : server;
this._request({
method: 'PUT',
path: urlJoin(_urlPrefix, serverId),
body: { server: { name: name } }
}, function (err) {
return callback(err);
});
};
/**
* client.getServerAddresses
*
* @description Get the ip addresses for the provided server
*
* @param {String|object} server The server or serverId
* @param {String} [type] Optionally provide the type of addresses by network label
* @param {Function} callback
* @returns {*}
*/
exports.getServerAddresses = function (server, type, callback) {
var serverId = server instanceof base.Server ? server.id : server;
if (!callback && typeof type === 'function') {
callback = type;
type = '';
}
var options = {
path: urlJoin(_urlPrefix, serverId, 'ips')
};
if (type) {
options.path = urlJoin(options.path, type);
}
this._request(options, function (err, body) {
return err
? callback(err)
: callback(null, body.addresses || body);
});
};