summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorJoe Gordon <jogo@cloudscaling.com>2013-04-10 01:05:42 +0000
committerJoe Gordon <jogo@cloudscaling.com>2013-05-01 19:38:40 +0000
commit5ada427935a0664f6c2534163f9988fb85d7b6ca (patch)
tree93f450ecbf6f0734b2e5ad79f8b71ea3e60bc25a /nova
parent423289d50d92cf4b780a11c2c3da4e1dbbd865f2 (diff)
downloadnova-5ada427935a0664f6c2534163f9988fb85d7b6ca.tar.gz
nova-5ada427935a0664f6c2534163f9988fb85d7b6ca.tar.xz
nova-5ada427935a0664f6c2534163f9988fb85d7b6ca.zip
Prevent rescuing a VM with a partially mounted volume.
If a VM goes into rescue mode with a partially mounted volume, the volume won't re-appear after the VM is unrescued. Fix bug 1158942 Change-Id: I1e104236c41c59e67a0f0e9ef26143c57f6e0094
Diffstat (limited to 'nova')
-rw-r--r--nova/api/openstack/compute/contrib/rescue.py2
-rw-r--r--nova/compute/api.py6
-rw-r--r--nova/tests/compute/test_compute.py30
-rw-r--r--nova/volume/cinder.py6
4 files changed, 43 insertions, 1 deletions
diff --git a/nova/api/openstack/compute/contrib/rescue.py b/nova/api/openstack/compute/contrib/rescue.py
index 143b59063..c89d11117 100644
--- a/nova/api/openstack/compute/contrib/rescue.py
+++ b/nova/api/openstack/compute/contrib/rescue.py
@@ -60,6 +60,8 @@ class RescueController(wsgi.Controller):
except exception.InstanceInvalidState as state_error:
common.raise_http_conflict_for_instance_invalid_state(state_error,
'rescue')
+ except exception.InvalidVolume as volume_error:
+ raise exc.HTTPConflict(explanation=volume_error.format_message())
except exception.InstanceNotRescuable as non_rescuable:
raise exc.HTTPBadRequest(
explanation=non_rescuable.format_message())
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 3f3078d5b..6a7ca79c7 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -2143,6 +2143,12 @@ class API(base.Base):
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED])
def rescue(self, context, instance, rescue_password=None):
"""Rescue the given instance."""
+
+ bdms = self.get_instance_bdms(context, instance)
+ for bdm in bdms:
+ if bdm['volume_id']:
+ volume = self.volume_api.get(context, bdm['volume_id'])
+ self.volume_api.check_attached(context, volume)
# TODO(ndipanov): This check can be generalized as a decorator to
# check for valid combinations of src and dests - for now check
# if it's booted from volume only
diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py
index 277e804ac..4128f87e6 100644
--- a/nova/tests/compute/test_compute.py
+++ b/nova/tests/compute/test_compute.py
@@ -5592,11 +5592,17 @@ class ComputeAPITestCase(BaseTestCase):
volume_backed_uuid_2 = volume_backed_inst_2['uuid']
def fake_get_instance_bdms(*args, **kwargs):
- return [{'device_name': '/dev/vda'}]
+ return [{'device_name': '/dev/vda',
+ 'volume_id':'bf0b6b00-a20c-11e2-9e96-0800200c9a66'}]
self.stubs.Set(self.compute_api, 'get_instance_bdms',
fake_get_instance_bdms)
+ def fake_volume_get(self, context, volume_id):
+ return {'id': volume_id, 'status': 'in-use'}
+
+ self.stubs.Set(cinder.API, 'get', fake_volume_get)
+
self.compute.run_instance(self.context,
instance=volume_backed_inst_1)
self.compute.run_instance(self.context,
@@ -6847,6 +6853,28 @@ class ComputeAPITestCase(BaseTestCase):
self.compute_api.detach_volume,
self.context, instance, volume)
+ def test_no_rescue_in_volume_state_attaching(self):
+ # Make sure a VM cannot be rescued while volume is being attached
+ instance = self._create_fake_instance()
+
+ def fake_get_instance_bdms(*args, **kwargs):
+ return [{'device_name': '/dev/vda',
+ 'volume_id':'bf0b6b00-a20c-11e2-9e96-0800200c9a66'}]
+
+ self.stubs.Set(self.compute_api, 'get_instance_bdms',
+ fake_get_instance_bdms)
+
+ def fake_volume_get(self, context, volume_id):
+ return {'id': volume_id, 'status': 'attaching'}
+
+ self.stubs.Set(cinder.API, 'get', fake_volume_get)
+
+ volume = {'id': 'bf0b6b00-a20c-11e2-9e96-0800200c9a66',
+ 'state': 'active', 'instance_uuid': instance['uuid']}
+
+ self.assertRaises(exception.InvalidVolume,
+ self.compute_api.rescue, self.context, instance)
+
def test_vnc_console(self):
# Make sure we can a vnc console for an instance.
diff --git a/nova/volume/cinder.py b/nova/volume/cinder.py
index 0d2666038..de4948928 100644
--- a/nova/volume/cinder.py
+++ b/nova/volume/cinder.py
@@ -213,6 +213,12 @@ class API(base.Base):
return rval
+ def check_attached(self, context, volume):
+ """Raise exception if volume in use."""
+ if volume['status'] != "in-use":
+ msg = _("status must be 'in-use'")
+ raise exception.InvalidVolume(reason=msg)
+
def check_attach(self, context, volume, instance=None):
# TODO(vish): abstract status checking?
if volume['status'] != "available":