From 1ecd9b3d00f002799a9eab92f0179dcbea8b8c37 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Thu, 31 Mar 2011 14:15:42 -0400 Subject: adding 'building' power state; testing for 409 from OSAPI when rebuild requested on server being rebuild --- nova/api/openstack/servers.py | 15 ++++++++-- nova/compute/api.py | 6 ++++ nova/compute/power_state.py | 21 ++++++++------ nova/exception.py | 4 +++ nova/tests/api/openstack/test_servers.py | 48 ++++++++++++++++++++++++++++++-- 5 files changed, 81 insertions(+), 13 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index edf98fe93..fc33a8257 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -611,7 +611,13 @@ class ControllerV10(Controller): return faults.Fault(exc.HTTPBadRequest(msg)) image_id = input_dict['rebuild']['imageId'] - self.compute_api.rebuild(context, id, image_id) + + try: + self.compute_api.rebuild(context, id, image_id) + except exception.BuildInProgress: + msg = _("Unable to rebuild server that is being rebuilt") + return faults.Fault(exc.HTTPConflict(msg)) + return exc.HTTPAccepted() @@ -660,7 +666,12 @@ class ControllerV11(Controller): msg = _("Improperly formatted metadata provided") return exc.HTTPBadRequest(msg) - self.compute_api.rebuild(context, id, image_id, metadata) + try: + self.compute_api.rebuild(context, id, image_id, metadata) + except exception.BuildInProgress: + msg = _("Unable to rebuild server that is being rebuilt") + return faults.Fault(exc.HTTPConflict(msg)) + return exc.HTTPAccepted() diff --git a/nova/compute/api.py b/nova/compute/api.py index 43f972882..32b283d31 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -34,6 +34,7 @@ from nova import rpc from nova import utils from nova import volume from nova.compute import instance_types +from nova.compute import power_state from nova.scheduler import api as scheduler_api from nova.db import base @@ -488,6 +489,11 @@ class API(base.Base): def rebuild(self, context, instance_id, image_id, metadata=None): """Rebuild the given instance with the provided metadata.""" + instance = db.api.instance_get(context, instance_id) + if instance["state"] == power_state.BUILDING: + msg = _("Instance already building") + raise exception.BuildInProgress(msg) + metadata = metadata or [] self._check_metadata_quota(context, metadata) self._check_metadata_item_length(context, metadata) diff --git a/nova/compute/power_state.py b/nova/compute/power_state.py index ef013b2ef..c468fe6b3 100644 --- a/nova/compute/power_state.py +++ b/nova/compute/power_state.py @@ -30,20 +30,23 @@ SHUTOFF = 0x05 CRASHED = 0x06 SUSPENDED = 0x07 FAILED = 0x08 +BUILDING = 0x09 # TODO(justinsb): Power state really needs to be a proper class, # so that we're not locked into the libvirt status codes and can put mapping # logic here rather than spread throughout the code _STATE_MAP = { - NOSTATE: 'pending', - RUNNING: 'running', - BLOCKED: 'blocked', - PAUSED: 'paused', - SHUTDOWN: 'shutdown', - SHUTOFF: 'shutdown', - CRASHED: 'crashed', - SUSPENDED: 'suspended', - FAILED: 'failed to spawn'} + NOSTATE: 'pending', + RUNNING: 'running', + BLOCKED: 'blocked', + PAUSED: 'paused', + SHUTDOWN: 'shutdown', + SHUTOFF: 'shutdown', + CRASHED: 'crashed', + SUSPENDED: 'suspended', + FAILED: 'failed to spawn', + BUILDING: 'building', +} def name(code): diff --git a/nova/exception.py b/nova/exception.py index 4e2bbdbaf..5fa7fb56e 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -96,6 +96,10 @@ class TimeoutException(Error): pass +class BuildInProgress(Error): + pass + + class DBError(Error): """Wraps an implementation specific exception""" def __init__(self, inner_exception): diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index aab89b0b3..9e77738a6 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -31,6 +31,7 @@ from nova import flags from nova import test import nova.api.openstack from nova.api.openstack import servers +from nova.compute import power_state import nova.compute.api import nova.db.api from nova.db.sqlalchemy.models import Instance @@ -55,6 +56,12 @@ def return_server_with_addresses(private, public): return _return_server +def return_server_with_state(state): + def _return_server(context, id): + return stub_instance(id, state=state) + return _return_server + + def return_servers(context, user_id=1): return [stub_instance(i, user_id) for i in xrange(5)] @@ -71,7 +78,8 @@ def instance_address(context, instance_id): return None -def stub_instance(id, user_id=1, private_address=None, public_addresses=None): +def stub_instance(id, user_id=1, private_address=None, public_addresses=None, + state=0): metadata = [] metadata.append(InstanceMetadata(key='seq', value=id)) @@ -89,7 +97,7 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None): "launch_index": 0, "key_name": "", "key_data": "", - "state": 0, + "state": state, "state_description": "", "memory_mb": 0, "vcpus": 0, @@ -789,6 +797,24 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) + def test_server_rebuild_rejected_when_building(self): + body = { + "rebuild": { + "imageId": 2, + }, + } + + new_return_server = return_server_with_state(power_state.BUILDING) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + + req = webob.Request.blank('/v1.0/servers/1/action') + req.method = 'POST' + req.content_type = 'application/json' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 409) + def test_server_rebuild_bad_entity(self): body = { "rebuild": { @@ -818,6 +844,24 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) + def test_server_rebuild_rejected_when_building_v11(self): + body = { + "rebuild": { + "imageRef": "http://localhost/images/2", + }, + } + + new_return_server = return_server_with_state(power_state.BUILDING) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + + req = webob.Request.blank('/v1.1/servers/1/action') + req.method = 'POST' + req.content_type = 'application/json' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 409) + def test_server_rebuild_accepted_with_metadata_v11(self): body = { "rebuild": { -- cgit From 59e8112994e293fa1155b703abcc3e33d7cfc6c7 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Thu, 31 Mar 2011 14:35:25 -0400 Subject: pep8 --- nova/compute/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index 32b283d31..17bec2545 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -34,7 +34,7 @@ from nova import rpc from nova import utils from nova import volume from nova.compute import instance_types -from nova.compute import power_state +from nova.compute import power_state from nova.scheduler import api as scheduler_api from nova.db import base -- cgit