From e53fb7dcddfa2ec4cfd8d1ef984577a06344e07a Mon Sep 17 00:00:00 2001 From: Andrew Laski Date: Fri, 21 Jun 2013 15:01:43 -0400 Subject: [PATCH] API for shelving Adds new 'shelve', 'shelveOffload'/'shelve_offload'(V3), and 'unshelve' actions to the API. Exposes the functionality already provided in the compute api. Part of bp shelve-instance Co-author: Dan Smith (Instance objects) Change-Id: Idd485b591730c6ac025ee57a1242afdd02191b2f --- .../all_extensions/extensions-get-resp.json | 8 ++ .../all_extensions/extensions-get-resp.xml | 3 + doc/api_samples/os-shelve/os-shelve.json | 3 + doc/api_samples/os-shelve/os-shelve.xml | 2 + .../os-shelve/server-post-req.json | 16 +++ doc/api_samples/os-shelve/server-post-req.xml | 19 ++++ .../os-shelve/server-post-resp.json | 16 +++ .../os-shelve/server-post-resp.xml | 6 + etc/nova/policy.json | 6 + nova/api/openstack/common.py | 6 + nova/api/openstack/compute/contrib/shelve.py | 100 ++++++++++++++++ .../openstack/compute/plugins/v3/shelve.py | 105 +++++++++++++++++ .../openstack/compute/contrib/test_shelve.py | 107 ++++++++++++++++++ nova/tests/fake_policy.py | 6 + .../extensions-get-resp.json.tpl | 8 ++ .../extensions-get-resp.xml.tpl | 3 + .../api_samples/os-shelve/os-shelve.json.tpl | 3 + .../api_samples/os-shelve/os-shelve.xml.tpl | 2 + .../os-shelve/server-post-req.json.tpl | 16 +++ .../os-shelve/server-post-req.xml.tpl | 19 ++++ .../os-shelve/server-post-resp.json.tpl | 16 +++ .../os-shelve/server-post-resp.xml.tpl | 6 + nova/tests/integrated/test_api_samples.py | 34 ++++++ 23 files changed, 510 insertions(+) create mode 100644 doc/api_samples/os-shelve/os-shelve.json create mode 100644 doc/api_samples/os-shelve/os-shelve.xml create mode 100644 doc/api_samples/os-shelve/server-post-req.json create mode 100644 doc/api_samples/os-shelve/server-post-req.xml create mode 100644 doc/api_samples/os-shelve/server-post-resp.json create mode 100644 doc/api_samples/os-shelve/server-post-resp.xml create mode 100644 nova/api/openstack/compute/contrib/shelve.py create mode 100644 nova/api/openstack/compute/plugins/v3/shelve.py create mode 100644 nova/tests/api/openstack/compute/contrib/test_shelve.py create mode 100644 nova/tests/integrated/api_samples/os-shelve/os-shelve.json.tpl create mode 100644 nova/tests/integrated/api_samples/os-shelve/os-shelve.xml.tpl create mode 100644 nova/tests/integrated/api_samples/os-shelve/server-post-req.json.tpl create mode 100644 nova/tests/integrated/api_samples/os-shelve/server-post-req.xml.tpl create mode 100644 nova/tests/integrated/api_samples/os-shelve/server-post-resp.json.tpl create mode 100644 nova/tests/integrated/api_samples/os-shelve/server-post-resp.xml.tpl diff --git a/doc/api_samples/all_extensions/extensions-get-resp.json b/doc/api_samples/all_extensions/extensions-get-resp.json index 3caa0e893bfd..a52c8fd9099e 100644 --- a/doc/api_samples/all_extensions/extensions-get-resp.json +++ b/doc/api_samples/all_extensions/extensions-get-resp.json @@ -512,6 +512,14 @@ "namespace": "http://docs.openstack.org/compute/ext/extended_services/api/v2", "updated": "2013-05-17T00:00:00-00:00" }, + { + "alias": "os-shelve", + "description": "Instance shelve mode.", + "links": [], + "name": "Shelve", + "namespace": "http://docs.openstack.org/compute/ext/shelve/api/v1.1", + "updated": "2013-04-06T00:00:00+00:00" + }, { "alias": "os-simple-tenant-usage", "description": "Simple tenant usage extension.", diff --git a/doc/api_samples/all_extensions/extensions-get-resp.xml b/doc/api_samples/all_extensions/extensions-get-resp.xml index fd4fed4b1b45..5cac81097d49 100644 --- a/doc/api_samples/all_extensions/extensions-get-resp.xml +++ b/doc/api_samples/all_extensions/extensions-get-resp.xml @@ -210,6 +210,9 @@ Extended services support. + + Instance shelve mode. + Simple tenant usage extension. diff --git a/doc/api_samples/os-shelve/os-shelve.json b/doc/api_samples/os-shelve/os-shelve.json new file mode 100644 index 000000000000..e33b05865aca --- /dev/null +++ b/doc/api_samples/os-shelve/os-shelve.json @@ -0,0 +1,3 @@ +{ + "shelve": null +} \ No newline at end of file diff --git a/doc/api_samples/os-shelve/os-shelve.xml b/doc/api_samples/os-shelve/os-shelve.xml new file mode 100644 index 000000000000..687b1b1f58c8 --- /dev/null +++ b/doc/api_samples/os-shelve/os-shelve.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/doc/api_samples/os-shelve/server-post-req.json b/doc/api_samples/os-shelve/server-post-req.json new file mode 100644 index 000000000000..d88eb4122223 --- /dev/null +++ b/doc/api_samples/os-shelve/server-post-req.json @@ -0,0 +1,16 @@ +{ + "server" : { + "name" : "new-server-test", + "imageRef" : "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "flavorRef" : "http://openstack.example.com/openstack/flavors/1", + "metadata" : { + "My Server Name" : "Apache1" + }, + "personality" : [ + { + "path" : "/etc/banner.txt", + "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" + } + ] + } +} \ No newline at end of file diff --git a/doc/api_samples/os-shelve/server-post-req.xml b/doc/api_samples/os-shelve/server-post-req.xml new file mode 100644 index 000000000000..0a3c8bb5303d --- /dev/null +++ b/doc/api_samples/os-shelve/server-post-req.xml @@ -0,0 +1,19 @@ + + + + Apache1 + + + + ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp + dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k + IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs + c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g + QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo + ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv + dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy + c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 + b25zLiINCg0KLVJpY2hhcmQgQmFjaA== + + + \ No newline at end of file diff --git a/doc/api_samples/os-shelve/server-post-resp.json b/doc/api_samples/os-shelve/server-post-resp.json new file mode 100644 index 000000000000..23de4473f9a1 --- /dev/null +++ b/doc/api_samples/os-shelve/server-post-resp.json @@ -0,0 +1,16 @@ +{ + "server": { + "adminPass": "bGZzzzeaSp9z", + "id": "9582b762-0964-4509-8fff-0146c02abe31", + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/servers/9582b762-0964-4509-8fff-0146c02abe31", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/servers/9582b762-0964-4509-8fff-0146c02abe31", + "rel": "bookmark" + } + ] + } +} \ No newline at end of file diff --git a/doc/api_samples/os-shelve/server-post-resp.xml b/doc/api_samples/os-shelve/server-post-resp.xml new file mode 100644 index 000000000000..7b789b224689 --- /dev/null +++ b/doc/api_samples/os-shelve/server-post-resp.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/etc/nova/policy.json b/etc/nova/policy.json index 6f08f997ac80..ed5fdd424152 100644 --- a/etc/nova/policy.json +++ b/etc/nova/policy.json @@ -162,10 +162,16 @@ "compute_extension:services": "rule:admin_api", "compute_extension:v3:os-services": "rule:admin_api", "compute_extension:v3:servers:discoverable": "", + "compute_extension:shelve": "", + "compute_extension:shelveOffload": "rule:admin_api", + "compute_extension:v3:os-shelve:shelve": "", + "compute_extension:v3:os-shelve:shelve_offload": "rule:admin_api", "compute_extension:simple_tenant_usage:show": "rule:admin_or_owner", "compute_extension:v3:os-simple-tenant-usage:show": "rule:admin_or_owner", "compute_extension:simple_tenant_usage:list": "rule:admin_api", "compute_extension:v3:os-simple-tenant-usage:list": "rule:admin_api", + "compute_extension:unshelve": "", + "compute_extension:v3:os-shelve:unshelve": "", "compute_extension:users": "rule:admin_api", "compute_extension:virtual_interfaces": "", "compute_extension:virtual_storage_arrays": "", diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 509256d87ea8..59a58103da7e 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -103,6 +103,12 @@ _STATE_MAP = { vm_states.SOFT_DELETED: { 'default': 'DELETED', }, + vm_states.SHELVED: { + 'default': 'SHELVED', + }, + vm_states.SHELVED_OFFLOADED: { + 'default': 'SHELVED_OFFLOADED', + }, } diff --git a/nova/api/openstack/compute/contrib/shelve.py b/nova/api/openstack/compute/contrib/shelve.py new file mode 100644 index 000000000000..778e2febad02 --- /dev/null +++ b/nova/api/openstack/compute/contrib/shelve.py @@ -0,0 +1,100 @@ +# Copyright 2013 Rackspace Hosting +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""The shelved mode extension.""" + +import webob +from webob import exc + +from nova.api.openstack import common +from nova.api.openstack import extensions as exts +from nova.api.openstack import wsgi +from nova import compute +from nova import exception + + +auth_shelve = exts.extension_authorizer('compute', 'shelve') +auth_shelve_offload = exts.extension_authorizer('compute', 'shelveOffload') +auth_unshelve = exts.extension_authorizer('compute', 'unshelve') + + +class ShelveController(wsgi.Controller): + def __init__(self, *args, **kwargs): + super(ShelveController, self).__init__(*args, **kwargs) + self.compute_api = compute.API() + + def _get_instance(self, context, instance_id): + try: + return self.compute_api.get(context, instance_id, + want_objects=True) + except exception.InstanceNotFound: + msg = _("Server not found") + raise exc.HTTPNotFound(msg) + + @wsgi.action('shelve') + def _shelve(self, req, id, body): + """Move an instance into shelved mode.""" + context = req.environ["nova.context"] + auth_shelve(context) + + instance = self._get_instance(context, id) + try: + self.compute_api.shelve(context, instance) + except exception.InstanceInvalidState as state_error: + common.raise_http_conflict_for_instance_invalid_state(state_error, + 'shelve') + + return webob.Response(status_int=202) + + @wsgi.action('shelveOffload') + def _shelve_offload(self, req, id, body): + """Force removal of a shelved instance from the compute node.""" + context = req.environ["nova.context"] + auth_shelve_offload(context) + + instance = self._get_instance(context, id) + try: + self.compute_api.shelve_offload(context, instance) + except exception.InstanceInvalidState as state_error: + common.raise_http_conflict_for_instance_invalid_state(state_error, + 'shelveOffload') + + return webob.Response(status_int=202) + + @wsgi.action('unshelve') + def _unshelve(self, req, id, body): + """Restore an instance from shelved mode.""" + context = req.environ["nova.context"] + auth_unshelve(context) + instance = self._get_instance(context, id) + try: + self.compute_api.unshelve(context, instance) + except exception.InstanceInvalidState as state_error: + common.raise_http_conflict_for_instance_invalid_state(state_error, + 'unshelve') + return webob.Response(status_int=202) + + +class Shelve(exts.ExtensionDescriptor): + """Instance shelve mode.""" + + name = "Shelve" + alias = "os-shelve" + namespace = "http://docs.openstack.org/compute/ext/shelve/api/v1.1" + updated = "2013-04-06T00:00:00+00:00" + + def get_controller_extensions(self): + controller = ShelveController() + extension = exts.ControllerExtension(self, 'servers', controller) + return [extension] diff --git a/nova/api/openstack/compute/plugins/v3/shelve.py b/nova/api/openstack/compute/plugins/v3/shelve.py new file mode 100644 index 000000000000..56b0683847b1 --- /dev/null +++ b/nova/api/openstack/compute/plugins/v3/shelve.py @@ -0,0 +1,105 @@ +# Copyright 2013 Rackspace Hosting +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""The shelved mode extension.""" + +import webob +from webob import exc + +from nova.api.openstack import common +from nova.api.openstack import extensions as exts +from nova.api.openstack import wsgi +from nova import compute +from nova import exception + + +ALIAS = 'os-shelve' +auth_shelve = exts.extension_authorizer('compute', 'v3:%s:shelve' % ALIAS) +auth_shelve_offload = exts.extension_authorizer('compute', + 'v3:%s:shelve_offload' % ALIAS) +auth_unshelve = exts.extension_authorizer('compute', 'v3:%s:unshelve' % ALIAS) + + +class ShelveController(wsgi.Controller): + def __init__(self, *args, **kwargs): + super(ShelveController, self).__init__(*args, **kwargs) + self.compute_api = compute.API() + + def _get_instance(self, context, instance_id): + try: + return self.compute_api.get(context, instance_id, + want_objects=True) + except exception.InstanceNotFound: + msg = _("Server not found") + raise exc.HTTPNotFound(msg) + + @wsgi.action('shelve') + def _shelve(self, req, id, body): + """Move an instance into shelved mode.""" + context = req.environ["nova.context"] + auth_shelve(context) + + instance = self._get_instance(context, id) + try: + self.compute_api.shelve(context, instance) + except exception.InstanceInvalidState as state_error: + common.raise_http_conflict_for_instance_invalid_state(state_error, + 'shelve') + + return webob.Response(status_int=202) + + @wsgi.action('shelve_offload') + def _shelve_offload(self, req, id, body): + """Force removal of a shelved instance from the compute node.""" + context = req.environ["nova.context"] + auth_shelve_offload(context) + + instance = self._get_instance(context, id) + try: + self.compute_api.shelve_offload(context, instance) + except exception.InstanceInvalidState as state_error: + common.raise_http_conflict_for_instance_invalid_state(state_error, + 'shelve_offload') + + return webob.Response(status_int=202) + + @wsgi.action('unshelve') + def _unshelve(self, req, id, body): + """Restore an instance from shelved mode.""" + context = req.environ["nova.context"] + auth_unshelve(context) + instance = self._get_instance(context, id) + try: + self.compute_api.unshelve(context, instance) + except exception.InstanceInvalidState as state_error: + common.raise_http_conflict_for_instance_invalid_state(state_error, + 'unshelve') + return webob.Response(status_int=202) + + +class Shelve(exts.V3APIExtensionBase): + """Instance shelve mode.""" + + name = "Shelve" + alias = ALIAS + namespace = "http://docs.openstack.org/compute/ext/shelve/api/v3" + version = 1 + + def get_controller_extensions(self): + controller = ShelveController() + extension = exts.ControllerExtension(self, 'servers', controller) + return [extension] + + def get_resources(self): + return [] diff --git a/nova/tests/api/openstack/compute/contrib/test_shelve.py b/nova/tests/api/openstack/compute/contrib/test_shelve.py new file mode 100644 index 000000000000..125d4f715404 --- /dev/null +++ b/nova/tests/api/openstack/compute/contrib/test_shelve.py @@ -0,0 +1,107 @@ +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import uuid + +from nova.api.openstack.compute.contrib import shelve +from nova import db +from nova import exception +from nova.openstack.common import policy +from nova import test +from nova.tests.api.openstack import fakes +from nova.tests import fake_instance + + +class ShelvePolicyTest(test.TestCase): + def setUp(self): + super(ShelvePolicyTest, self).setUp() + self.controller = shelve.ShelveController() + + def test_shelve_restricted_by_role(self): + rules = policy.Rules({'compute_extension:shelve': + policy.parse_rule('role:admin')}) + policy.set_rules(rules) + + req = fakes.HTTPRequest.blank('/v2/123/servers/12/os-shelve') + self.assertRaises(exception.NotAuthorized, self.controller._shelve, + req, str(uuid.uuid4()), {}) + + def test_shelve_allowed(self): + rules = policy.Rules({'compute:get': policy.parse_rule(''), + 'compute_extension:shelve': + policy.parse_rule('')}) + policy.set_rules(rules) + + def fake_instance_get_by_uuid(context, instance_id, + columns_to_join=None): + return fake_instance.fake_db_instance( + **{'name': 'fake', 'project_id': '%s_unequal' % + context.project_id}) + + self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid) + req = fakes.HTTPRequest.blank('/v2/123/servers/12/os-shelve') + self.assertRaises(exception.NotAuthorized, self.controller._shelve, + req, str(uuid.uuid4()), {}) + + def test_unshelve_restricted_by_role(self): + rules = policy.Rules({'compute_extension:unshelve': + policy.parse_rule('role:admin')}) + policy.set_rules(rules) + + req = fakes.HTTPRequest.blank('/v2/123/servers/12/os-shelve') + self.assertRaises(exception.NotAuthorized, self.controller._unshelve, + req, str(uuid.uuid4()), {}) + + def test_unshelve_allowed(self): + rules = policy.Rules({'compute:get': policy.parse_rule(''), + 'compute_extension:unshelve': + policy.parse_rule('')}) + policy.set_rules(rules) + + def fake_instance_get_by_uuid(context, instance_id, + columns_to_join=None): + return fake_instance.fake_db_instance( + **{'name': 'fake', 'project_id': '%s_unequal' % + context.project_id}) + + self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid) + req = fakes.HTTPRequest.blank('/v2/123/servers/12/os-shelve') + self.assertRaises(exception.NotAuthorized, self.controller._unshelve, + req, str(uuid.uuid4()), {}) + + def test_shelve_offload_restricted_by_role(self): + rules = policy.Rules({'compute_extension:shelveOffload': + policy.parse_rule('role:admin')}) + policy.set_rules(rules) + + req = fakes.HTTPRequest.blank('/v2/123/servers/12/os-shelve') + self.assertRaises(exception.NotAuthorized, + self.controller._shelve_offload, req, str(uuid.uuid4()), {}) + + def test_shelve_offload_allowed(self): + rules = policy.Rules({'compute:get': policy.parse_rule(''), + 'compute_extension:shelveOffload': + policy.parse_rule('')}) + policy.set_rules(rules) + + def fake_instance_get_by_uuid(context, instance_id, + columns_to_join=None): + return fake_instance.fake_db_instance( + **{'name': 'fake', 'project_id': '%s_unequal' % + context.project_id}) + + self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid) + req = fakes.HTTPRequest.blank('/v2/123/servers/12/os-shelve') + self.assertRaises(exception.NotAuthorized, + self.controller._shelve_offload, req, str(uuid.uuid4()), {}) diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py index fb06ee35f3d5..691b031e0daf 100644 --- a/nova/tests/fake_policy.py +++ b/nova/tests/fake_policy.py @@ -238,10 +238,16 @@ policy_data = """ "compute_extension:server_usage": "", "compute_extension:services": "", "compute_extension:v3:os-services": "", + "compute_extension:shelve": "", + "compute_extension:shelveOffload": "", + "compute_extension:v3:os-shelve:shelve": "", + "compute_extension:v3:os-shelve:shelve_offload": "", "compute_extension:simple_tenant_usage:show": "", "compute_extension:v3:os-simple-tenant-usage:show": "", "compute_extension:simple_tenant_usage:list": "", "compute_extension:v3:os-simple-tenant-usage:list": "", + "compute_extension:unshelve": "", + "compute_extension:v3:os-shelve:unshelve": "", "compute_extension:users": "", "compute_extension:virtual_interfaces": "", "compute_extension:virtual_storage_arrays": "", diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl index 23ce1f611cec..eca786e27470 100644 --- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl +++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl @@ -512,6 +512,14 @@ "namespace": "http://docs.openstack.org/compute/ext/servers/api/v1.1", "updated": "%(timestamp)s" }, + { + "alias": "os-shelve", + "description": "%(text)s", + "links": [], + "name": "Shelve", + "namespace": "http://docs.openstack.org/compute/ext/shelve/api/v1.1", + "updated": "%(timestamp)s" + }, { "alias": "os-simple-tenant-usage", "description": "%(text)s", diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl index 31a755ec2e2b..8edf7f7e3d80 100644 --- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl +++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl @@ -192,6 +192,9 @@ %(text)s + + %(text)s + %(text)s diff --git a/nova/tests/integrated/api_samples/os-shelve/os-shelve.json.tpl b/nova/tests/integrated/api_samples/os-shelve/os-shelve.json.tpl new file mode 100644 index 000000000000..5a19f85cffa9 --- /dev/null +++ b/nova/tests/integrated/api_samples/os-shelve/os-shelve.json.tpl @@ -0,0 +1,3 @@ +{ + "%(action)s": null +} diff --git a/nova/tests/integrated/api_samples/os-shelve/os-shelve.xml.tpl b/nova/tests/integrated/api_samples/os-shelve/os-shelve.xml.tpl new file mode 100644 index 000000000000..41d18bdac00e --- /dev/null +++ b/nova/tests/integrated/api_samples/os-shelve/os-shelve.xml.tpl @@ -0,0 +1,2 @@ + + <%(action)s/> diff --git a/nova/tests/integrated/api_samples/os-shelve/server-post-req.json.tpl b/nova/tests/integrated/api_samples/os-shelve/server-post-req.json.tpl new file mode 100644 index 000000000000..d3916d1aa68a --- /dev/null +++ b/nova/tests/integrated/api_samples/os-shelve/server-post-req.json.tpl @@ -0,0 +1,16 @@ +{ + "server" : { + "name" : "new-server-test", + "imageRef" : "%(host)s/openstack/images/%(image_id)s", + "flavorRef" : "%(host)s/openstack/flavors/1", + "metadata" : { + "My Server Name" : "Apache1" + }, + "personality" : [ + { + "path" : "/etc/banner.txt", + "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" + } + ] + } +} diff --git a/nova/tests/integrated/api_samples/os-shelve/server-post-req.xml.tpl b/nova/tests/integrated/api_samples/os-shelve/server-post-req.xml.tpl new file mode 100644 index 000000000000..f92614984242 --- /dev/null +++ b/nova/tests/integrated/api_samples/os-shelve/server-post-req.xml.tpl @@ -0,0 +1,19 @@ + + + + Apache1 + + + + ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp + dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k + IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs + c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g + QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo + ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv + dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy + c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 + b25zLiINCg0KLVJpY2hhcmQgQmFjaA== + + + diff --git a/nova/tests/integrated/api_samples/os-shelve/server-post-resp.json.tpl b/nova/tests/integrated/api_samples/os-shelve/server-post-resp.json.tpl new file mode 100644 index 000000000000..d5f030c8730b --- /dev/null +++ b/nova/tests/integrated/api_samples/os-shelve/server-post-resp.json.tpl @@ -0,0 +1,16 @@ +{ + "server": { + "adminPass": "%(password)s", + "id": "%(id)s", + "links": [ + { + "href": "%(host)s/v2/openstack/servers/%(uuid)s", + "rel": "self" + }, + { + "href": "%(host)s/openstack/servers/%(uuid)s", + "rel": "bookmark" + } + ] + } +} diff --git a/nova/tests/integrated/api_samples/os-shelve/server-post-resp.xml.tpl b/nova/tests/integrated/api_samples/os-shelve/server-post-resp.xml.tpl new file mode 100644 index 000000000000..3bb13e69bd6d --- /dev/null +++ b/nova/tests/integrated/api_samples/os-shelve/server-post-resp.xml.tpl @@ -0,0 +1,6 @@ + + + + + + diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py index d8ce7c9eedb3..c68630134510 100644 --- a/nova/tests/integrated/test_api_samples.py +++ b/nova/tests/integrated/test_api_samples.py @@ -1522,6 +1522,40 @@ class RescueXmlTest(RescueJsonTest): ctype = 'xml' +class ShelveJsonTest(ServersSampleBase): + extension_name = "nova.api.openstack.compute.contrib.shelve.Shelve" + + def setUp(self): + super(ShelveJsonTest, self).setUp() + # Don't offload instance, so we can test the offload call. + CONF.shelved_offload_time = -1 + + def _test_server_action(self, uuid, action): + response = self._do_post('servers/%s/action' % uuid, + 'os-shelve', + {'action': action}) + self.assertEqual(response.status, 202) + self.assertEqual(response.read(), "") + + def test_shelve(self): + uuid = self._post_server() + self._test_server_action(uuid, 'shelve') + + def test_shelve_offload(self): + uuid = self._post_server() + self._test_server_action(uuid, 'shelve') + self._test_server_action(uuid, 'shelveOffload') + + def test_unshelve(self): + uuid = self._post_server() + self._test_server_action(uuid, 'shelve') + self._test_server_action(uuid, 'unshelve') + + +class ShelveXmlTest(ShelveJsonTest): + ctype = 'xml' + + class VirtualInterfacesJsonTest(ServersSampleBase): extension_name = ("nova.api.openstack.compute.contrib" ".virtual_interfaces.Virtual_interfaces")