diff options
author | Joe Gordon <jogo@cloudscaling.com> | 2013-04-10 01:05:42 +0000 |
---|---|---|
committer | Joe Gordon <jogo@cloudscaling.com> | 2013-05-01 19:38:40 +0000 |
commit | 5ada427935a0664f6c2534163f9988fb85d7b6ca (patch) | |
tree | 93f450ecbf6f0734b2e5ad79f8b71ea3e60bc25a /nova | |
parent | 423289d50d92cf4b780a11c2c3da4e1dbbd865f2 (diff) | |
download | nova-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.py | 2 | ||||
-rw-r--r-- | nova/compute/api.py | 6 | ||||
-rw-r--r-- | nova/tests/compute/test_compute.py | 30 | ||||
-rw-r--r-- | nova/volume/cinder.py | 6 |
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": |