summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/api/openstack/servers.py15
-rw-r--r--nova/compute/api.py12
-rw-r--r--nova/compute/power_state.py21
-rw-r--r--nova/exception.py4
-rw-r--r--nova/tests/api/openstack/test_servers.py48
5 files changed, 80 insertions, 20 deletions
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 bb909f925..17bec2545 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -489,17 +489,15 @@ 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)
- instance_ref = self.db.instance_get(context, instance_id)
- if instance_ref.state_description == "rebuilding":
- raise exception.NotFound("blah")
- else:
- LOG.error("SENDING REBUILD MESSAGE, INSTANCE HAD STATE: %s" %
- instance_ref.state_description)
-
self._cast_compute_message('rebuild_instance', context,
instance_id, params={"image_id": image_id})
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": {