diff --git a/.gitignore b/.gitignore index a307754e3..6b67ea7b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store +.history node_modules *.log test/fixtures/test-download* diff --git a/docs/providers/compute-commonality.md b/docs/providers/compute-commonality.md index 08820ff98..7e6ec7842 100644 --- a/docs/providers/compute-commonality.md +++ b/docs/providers/compute-commonality.md @@ -34,6 +34,9 @@ The following table outlines the methods that are available on the different com getLimitsNNNYYN getDetailsYNNNNN stopServerNYNNNN +shelveServerNNNYNN +offloadServerNNNYNN +unshelveServerNNNYNN createHostedServiceNYNNNN resizeServerNNNYYN rebuildServerNNNYYN diff --git a/docs/providers/openstack/compute.md b/docs/providers/openstack/compute.md index d687f9530..d2d4113ad 100644 --- a/docs/providers/openstack/compute.md +++ b/docs/providers/openstack/compute.md @@ -1,4 +1,4 @@ -##Using the Openstack Compute provider +## Using the Openstack Compute provider Creating a client is straight-forward: @@ -51,7 +51,7 @@ Takes server or serverId as an argument and returns the server in the callback `f(err, server)` #### client.rebootServer(server, options, callback) -Reboots the specifed server with options +Reboots the specified server with options Options include: @@ -63,7 +63,7 @@ Options include: Returns callback with a confirmation #### client.rebuildServer(server, options, callback) -Rebuilds the specifed server with options +Rebuilds the specified server with options Options include: @@ -83,6 +83,21 @@ Returns callback with a confirmation **Note about backwards compatiblity:** For backwards compatibility, it is also possible to pass an image ID or instance of `pkgcloud.core.compute.Image` as the value of the `options` argument. +#### client.shelveServer(server, callback) +[Shelves](https://developer.openstack.org/api-ref/compute/#shelve-server-shelve-action) the specified server + +Takes server or serverId as an argument and returns a confirmation in the callback `f(err, confirmation)` + +#### client.offloadServer(server, callback) +[Offloads](https://developer.openstack.org/api-ref/compute/#shelf-offload-remove-server-shelveoffload-action) the specified server + +Takes server or serverId as an argument and returns a confirmation in the callback `f(err, confirmation)` + +#### client.unshelveServer(server, callback) +[Unshelves](https://developer.openstack.org/api-ref/compute/#unshelve-restore-shelved-server-unshelve-action) the specified server + +Takes server or serverId as an argument and returns a confirmation in the callback `f(err, confirmation)` + #### client.getVersion(callback) Get the current version of the api returned in a callback `f(err, version)` diff --git a/lib/pkgcloud/common/status.js b/lib/pkgcloud/common/status.js index b4cab0dc8..47f9eca93 100644 --- a/lib/pkgcloud/common/status.js +++ b/lib/pkgcloud/common/status.js @@ -13,5 +13,7 @@ exports.compute = { stopped: 'STOPPED', terminated: 'TERMINATED', unknown: 'UNKNOWN', - updating: 'UPDATING' + updating: 'UPDATING', + shelved: 'SHELVED', + offloaded: 'OFFLOADED' }; \ No newline at end of file diff --git a/lib/pkgcloud/openstack/compute/client/extensions/servers.js b/lib/pkgcloud/openstack/compute/client/extensions/servers.js index b0fc93c01..bb9a0dedd 100644 --- a/lib/pkgcloud/openstack/compute/client/extensions/servers.js +++ b/lib/pkgcloud/openstack/compute/client/extensions/servers.js @@ -41,3 +41,47 @@ exports.stopServer = function (server, callback) { }); }; +/** + * client.shelveServer + * + * @description Shelves a server and changes its status to SHELVED then, eventually, to OFFLOADED. + * + * @see https://developer.openstack.org/api-ref/compute/#shelve-server-shelve-action + * + * @param {String|Object} server The server ID or server to shelve + * @param {function} callback + * @returns {*} + */ +exports.shelveServer = function (server, callback) { + return this._doServerAction(server, {'shelve': null}, callback); +}; + +/** + * client.offloadServer + * + * @description Offload a shelved server and changes its status to OFFLOADED. + * + * @see https://developer.openstack.org/api-ref/compute/#shelf-offload-remove-server-shelveoffload-action + * + * @param {String|Object} server The server ID or server to offload + * @param {function} callback + * @returns {*} + */ +exports.offloadServer = function (server, callback) { + return this._doServerAction(server, {'shelveOffload': null}, callback); +}; + +/** + * client.unshelveServer + * + * @description Unshelve a shelved or offloaded server and changes its status to RUNNING. + * + * @see https://developer.openstack.org/api-ref/compute/#unshelve-restore-shelved-server-unshelve-action + * + * @param {String|Object} server The server ID or server to unshelve + * @param {function} callback + * @returns {*} + */ +exports.unshelveServer = function (server, callback) { + return this._doServerAction(server, {'unshelve': null}, callback); +}; diff --git a/lib/pkgcloud/openstack/compute/server.js b/lib/pkgcloud/openstack/compute/server.js index 9c240186b..fa074e8d9 100644 --- a/lib/pkgcloud/openstack/compute/server.js +++ b/lib/pkgcloud/openstack/compute/server.js @@ -53,6 +53,12 @@ Server.prototype._setProperties = function (details) { case 'ERROR': this.status = this.STATUS.error; break; + case 'SHELVED': + this.status = this.STATUS.shelved; + break; + case 'SHELVED_OFFLOADED': + this.status = this.STATUS.offloaded; + break; default: this.status = this.STATUS.unknown; break; diff --git a/test/openstack/compute/client/offloadServer-test.js b/test/openstack/compute/client/offloadServer-test.js new file mode 100644 index 000000000..e50b9844c --- /dev/null +++ b/test/openstack/compute/client/offloadServer-test.js @@ -0,0 +1,106 @@ +/* +* +* (C) 2014 Alvaro M. Reol +* +*/ + +var helpers = require('../../../helpers'); + +var should = require('should'), + async = require('async'), + hock = require('hock'), + http = require('http'), + mock = !!process.env.MOCK; + +var client = helpers.createClient('openstack', 'compute'); + +describe('pkgcloud/common/compute/server[openstack]', function () { + + var authHockInstance, hockInstance, authServer, server; + + before(function (done) { + + if (!mock) { + return done(); + } + + hockInstance = hock.createHock({ throwOnUnmatched: false }); + authHockInstance = hock.createHock(); + + server = http.createServer(hockInstance.handler); + authServer = http.createServer(authHockInstance.handler); + + async.parallel([ + function (next) { + server.listen(12345, next); + }, + function (next) { + authServer.listen(12346, next); + } + ], done); + }); + + it('the server.offloadServer() method should offload a server instance', function (done) { + if (mock) { + authHockInstance + .post('/v2.0/tokens', { + auth: { + passwordCredentials: { + username: 'MOCK-USERNAME', + password: 'MOCK-PASSWORD' + } + } + }) + .replyWithFile(200, __dirname + '/../../../fixtures/openstack/initialToken.json') + .get('/v2.0/tenants') + .replyWithFile(200, __dirname + '/../../../fixtures/openstack/tenantId.json') + .post('/v2.0/tokens', { + auth: { + passwordCredentials: { + username: 'MOCK-USERNAME', + password: 'MOCK-PASSWORD' + }, + tenantId: '72e90ecb69c44d0296072ea39e537041' + } + }) + .reply(200, helpers.getOpenstackAuthResponse()); + + hockInstance + .post('/v2/72e90ecb69c44d0296072ea39e537041/servers/a2e90ecb69c44d0296072ea39e53704a/action', + { 'shelveOffload': null }) + .reply(200, ''); + } + + client.offloadServer('a2e90ecb69c44d0296072ea39e53704a', function (err) { + should.not.exist(err); + + authHockInstance && authHockInstance.done(); + hockInstance && hockInstance.done(); + + done(); + }); + + + }); + + after(function (done) { + if (!mock) { + return done(); + } + + async.parallel([ + function (next) { + server.close(next); + }, + function (next) { + authServer.close(next); + } + ], done); + }); + +}); + + + + + diff --git a/test/openstack/compute/client/shelveServer-test.js b/test/openstack/compute/client/shelveServer-test.js new file mode 100644 index 000000000..406769700 --- /dev/null +++ b/test/openstack/compute/client/shelveServer-test.js @@ -0,0 +1,106 @@ +/* +* +* (C) 2014 Alvaro M. Reol +* +*/ + +var helpers = require('../../../helpers'); + +var should = require('should'), + async = require('async'), + hock = require('hock'), + http = require('http'), + mock = !!process.env.MOCK; + +var client = helpers.createClient('openstack', 'compute'); + +describe('pkgcloud/common/compute/server[openstack]', function () { + + var authHockInstance, hockInstance, authServer, server; + + before(function (done) { + + if (!mock) { + return done(); + } + + hockInstance = hock.createHock({ throwOnUnmatched: false }); + authHockInstance = hock.createHock(); + + server = http.createServer(hockInstance.handler); + authServer = http.createServer(authHockInstance.handler); + + async.parallel([ + function (next) { + server.listen(12345, next); + }, + function (next) { + authServer.listen(12346, next); + } + ], done); + }); + + it('the server.shelveServer() method should shelve a server instance', function (done) { + if (mock) { + authHockInstance + .post('/v2.0/tokens', { + auth: { + passwordCredentials: { + username: 'MOCK-USERNAME', + password: 'MOCK-PASSWORD' + } + } + }) + .replyWithFile(200, __dirname + '/../../../fixtures/openstack/initialToken.json') + .get('/v2.0/tenants') + .replyWithFile(200, __dirname + '/../../../fixtures/openstack/tenantId.json') + .post('/v2.0/tokens', { + auth: { + passwordCredentials: { + username: 'MOCK-USERNAME', + password: 'MOCK-PASSWORD' + }, + tenantId: '72e90ecb69c44d0296072ea39e537041' + } + }) + .reply(200, helpers.getOpenstackAuthResponse()); + + hockInstance + .post('/v2/72e90ecb69c44d0296072ea39e537041/servers/a2e90ecb69c44d0296072ea39e53704a/action', + { 'shelve': null }) + .reply(200, ''); + } + + client.shelveServer('a2e90ecb69c44d0296072ea39e53704a', function (err) { + should.not.exist(err); + + authHockInstance && authHockInstance.done(); + hockInstance && hockInstance.done(); + + done(); + }); + + + }); + + after(function (done) { + if (!mock) { + return done(); + } + + async.parallel([ + function (next) { + server.close(next); + }, + function (next) { + authServer.close(next); + } + ], done); + }); + +}); + + + + + diff --git a/test/openstack/compute/client/unshelveServer-test.js b/test/openstack/compute/client/unshelveServer-test.js new file mode 100644 index 000000000..8324dc0d8 --- /dev/null +++ b/test/openstack/compute/client/unshelveServer-test.js @@ -0,0 +1,106 @@ +/* +* +* (C) 2014 Alvaro M. Reol +* +*/ + +var helpers = require('../../../helpers'); + +var should = require('should'), + async = require('async'), + hock = require('hock'), + http = require('http'), + mock = !!process.env.MOCK; + +var client = helpers.createClient('openstack', 'compute'); + +describe('pkgcloud/common/compute/server[openstack]', function () { + + var authHockInstance, hockInstance, authServer, server; + + before(function (done) { + + if (!mock) { + return done(); + } + + hockInstance = hock.createHock({ throwOnUnmatched: false }); + authHockInstance = hock.createHock(); + + server = http.createServer(hockInstance.handler); + authServer = http.createServer(authHockInstance.handler); + + async.parallel([ + function (next) { + server.listen(12345, next); + }, + function (next) { + authServer.listen(12346, next); + } + ], done); + }); + + it('the server.unshelveServer() method should unshelve a server instance', function (done) { + if (mock) { + authHockInstance + .post('/v2.0/tokens', { + auth: { + passwordCredentials: { + username: 'MOCK-USERNAME', + password: 'MOCK-PASSWORD' + } + } + }) + .replyWithFile(200, __dirname + '/../../../fixtures/openstack/initialToken.json') + .get('/v2.0/tenants') + .replyWithFile(200, __dirname + '/../../../fixtures/openstack/tenantId.json') + .post('/v2.0/tokens', { + auth: { + passwordCredentials: { + username: 'MOCK-USERNAME', + password: 'MOCK-PASSWORD' + }, + tenantId: '72e90ecb69c44d0296072ea39e537041' + } + }) + .reply(200, helpers.getOpenstackAuthResponse()); + + hockInstance + .post('/v2/72e90ecb69c44d0296072ea39e537041/servers/a2e90ecb69c44d0296072ea39e53704a/action', + { 'unshelve': null }) + .reply(200, ''); + } + + client.unshelveServer('a2e90ecb69c44d0296072ea39e53704a', function (err) { + should.not.exist(err); + + authHockInstance && authHockInstance.done(); + hockInstance && hockInstance.done(); + + done(); + }); + + + }); + + after(function (done) { + if (!mock) { + return done(); + } + + async.parallel([ + function (next) { + server.close(next); + }, + function (next) { + authServer.close(next); + } + ], done); + }); + +}); + + + + +