From 8583aaf8542225da5542d4871763617c1378b1e3 Mon Sep 17 00:00:00 2001 From: PranaliDeore Date: Tue, 27 Jan 2015 04:46:58 -0800 Subject: [PATCH] Snapshot of bootable volume goes in error state If volume is not created from image, no glance metadata would be available for that volume/snapshot in volume glance metadata table. So simply catch GlanceMetadataNotFound and do nothing. Closes-Bug: #1413880 Change-Id: Ic3267d422912f94e966412859e2b8bddeddfb75f --- cinder/tests/test_volume.py | 219 +++++++++++++++++++ cinder/volume/flows/manager/create_volume.py | 5 + cinder/volume/manager.py | 5 + 3 files changed, 229 insertions(+) diff --git a/cinder/tests/test_volume.py b/cinder/tests/test_volume.py index 4a3756afa..24c202497 100644 --- a/cinder/tests/test_volume.py +++ b/cinder/tests/test_volume.py @@ -1097,6 +1097,202 @@ class VolumeTestCase(BaseVolumeTestCase): # calling manager.create_volume. self.assertRaises(exception.VolumeNotFound, gthreads[0].wait) + def _raise_metadata_copy_failure(self, method, dst_vol_id, **kwargs): + # MetadataCopyFailure exception will be raised if DB service is Down + # while copying the volume glance metadata + with mock.patch.object(db, method) as mock_db: + mock_db.side_effect = exception.MetadataCopyFailure( + reason="Because of DB service down.") + self.assertRaises(exception.MetadataCopyFailure, + self.volume.create_volume, + self.context, + dst_vol_id, + **kwargs) + + # ensure that status of volume is 'error' + vol = db.volume_get(self.context, dst_vol_id) + self.assertEqual('error', vol['status']) + + # cleanup resource + db.volume_destroy(self.context, dst_vol_id) + + def test_create_volume_from_volume_with_glance_volume_metadata_none(self): + # create source volume + src_vol = tests_utils.create_volume(self.context, **self.volume_params) + src_vol_id = src_vol['id'] + + self.volume.create_volume(self.context, src_vol_id) + # set bootable flag of volume to True + db.volume_update(self.context, src_vol['id'], {'bootable': True}) + + # create volume from source volume + dst_vol = tests_utils.create_volume(self.context, + **self.volume_params) + self.volume.create_volume(self.context, + dst_vol['id'], + source_volid=src_vol_id) + + self.assertRaises(exception.GlanceMetadataNotFound, + db.volume_glance_metadata_copy_from_volume_to_volume, + self.context, src_vol_id, dst_vol['id']) + + # ensure that status of volume is 'available' + vol = db.volume_get(self.context, dst_vol['id']) + self.assertEqual('available', vol['status']) + + # cleanup resource + db.volume_destroy(self.context, src_vol_id) + db.volume_destroy(self.context, dst_vol['id']) + + def test_create_volume_from_volume_raise_metadata_copy_failure( + self): + # create source volume + src_vol = tests_utils.create_volume(self.context, **self.volume_params) + src_vol_id = src_vol['id'] + + self.volume.create_volume(self.context, src_vol_id) + # set bootable flag of volume to True + db.volume_update(self.context, src_vol['id'], {'bootable': True}) + + # create volume from source volume + dst_vol = tests_utils.create_volume(self.context, + source_volid=src_vol_id, + **self.volume_params) + self._raise_metadata_copy_failure( + 'volume_glance_metadata_copy_from_volume_to_volume', + dst_vol['id'], + source_volid=src_vol_id) + + # cleanup resource + db.volume_destroy(self.context, src_vol_id) + + def test_create_volume_from_snapshot_raise_metadata_copy_failure( + self): + # create source volume + src_vol = tests_utils.create_volume(self.context, **self.volume_params) + src_vol_id = src_vol['id'] + + self.volume.create_volume(self.context, src_vol_id) + # set bootable flag of volume to True + db.volume_update(self.context, src_vol['id'], {'bootable': True}) + + # create volume from snapshot + snapshot_id = self._create_snapshot(src_vol['id'])['id'] + self.volume.create_snapshot(self.context, src_vol['id'], snapshot_id) + + # ensure that status of snapshot is 'available' + snapshot_ref = db.snapshot_get(self.context, snapshot_id)['status'] + self.assertEqual('available', snapshot_ref) + + dst_vol = tests_utils.create_volume(self.context, + **self.volume_params) + self._raise_metadata_copy_failure( + 'volume_glance_metadata_copy_to_volume', + dst_vol['id'], + snapshot_id=snapshot_id) + + # cleanup resource + db.snapshot_destroy(self.context, snapshot_id) + db.volume_destroy(self.context, src_vol_id) + + @mock.patch( + 'cinder.volume.driver.VolumeDriver.create_replica_test_volume') + def test_create_volume_from_srcreplica_raise_metadata_copy_failure( + self, _create_replica_test): + _create_replica_test.return_value = None + # create source volume + src_vol = tests_utils.create_volume(self.context, **self.volume_params) + src_vol_id = src_vol['id'] + + self.volume.create_volume(self.context, src_vol_id) + # set bootable flag of volume to True + db.volume_update(self.context, src_vol['id'], {'bootable': True}) + + # create volume from source volume + dst_vol = tests_utils.create_volume(self.context, + source_volid=src_vol_id, + **self.volume_params) + self._raise_metadata_copy_failure( + 'volume_glance_metadata_copy_from_volume_to_volume', + dst_vol['id'], + source_volid=src_vol_id) + + # cleanup resource + db.volume_destroy(self.context, src_vol_id) + + def test_create_volume_from_snapshot_with_glance_volume_metadata_none( + self): + # create source volume + src_vol = tests_utils.create_volume(self.context, **self.volume_params) + src_vol_id = src_vol['id'] + + self.volume.create_volume(self.context, src_vol_id) + # set bootable flag of volume to True + db.volume_update(self.context, src_vol['id'], {'bootable': True}) + + volume = db.volume_get(self.context, src_vol_id) + + # create snapshot of volume + snapshot_id = self._create_snapshot(volume['id'])['id'] + self.volume.create_snapshot(self.context, volume['id'], snapshot_id) + + # ensure that status of snapshot is 'available' + snapshot_ref = db.snapshot_get(self.context, snapshot_id)['status'] + self.assertEqual('available', snapshot_ref) + + # create volume from snapshot + dst_vol = tests_utils.create_volume(self.context, + **self.volume_params) + self.volume.create_volume(self.context, + dst_vol['id'], + snapshot_id=snapshot_id) + + self.assertRaises(exception.GlanceMetadataNotFound, + db.volume_glance_metadata_copy_to_volume, + self.context, dst_vol['id'], snapshot_id) + + # ensure that status of volume is 'available' + vol = db.volume_get(self.context, dst_vol['id']) + self.assertEqual('available', vol['status']) + + # cleanup resource + db.snapshot_destroy(self.context, snapshot_id) + db.volume_destroy(self.context, src_vol_id) + db.volume_destroy(self.context, dst_vol['id']) + + @mock.patch( + 'cinder.volume.driver.VolumeDriver.create_replica_test_volume') + def test_create_volume_from_srcreplica_with_glance_volume_metadata_none( + self, _create_replica_test): + """Test volume can be created from a volume replica.""" + _create_replica_test.return_value = None + + volume_src = tests_utils.create_volume(self.context, + **self.volume_params) + self.volume.create_volume(self.context, volume_src['id']) + db.volume_update(self.context, volume_src['id'], {'bootable': True}) + + volume = db.volume_get(self.context, volume_src['id']) + volume_dst = tests_utils.create_volume( + self.context, + source_replicaid=volume['id'], + **self.volume_params) + self.volume.create_volume(self.context, volume_dst['id'], + source_replicaid=volume['id']) + + self.assertRaises(exception.GlanceMetadataNotFound, + db.volume_glance_metadata_copy_from_volume_to_volume, + self.context, volume_src['id'], volume_dst['id']) + + self.assertEqual('available', + db.volume_get(self.context, + volume_dst['id']).status) + self.assertTrue(_create_replica_test.called) + + # cleanup resource + db.volume_destroy(self.context, volume_dst['id']) + db.volume_destroy(self.context, volume_src['id']) + def test_create_volume_from_snapshot_delete_lock_taken(self): # create source volume src_vol = tests_utils.create_volume(self.context, **self.volume_params) @@ -2126,6 +2322,29 @@ class VolumeTestCase(BaseVolumeTestCase): db.snapshot_destroy(ctxt, snap_id) db.volume_destroy(ctxt, volume_id) + def test_create_snapshot_from_bootable_volume_with_volume_metadata_none( + self): + volume = tests_utils.create_volume(self.context, **self.volume_params) + volume_id = volume['id'] + + self.volume.create_volume(self.context, volume_id) + # set bootable flag of volume to True + db.volume_update(self.context, volume_id, {'bootable': True}) + + snapshot_id = self._create_snapshot(volume['id'])['id'] + self.volume.create_snapshot(self.context, volume['id'], snapshot_id) + self.assertRaises(exception.GlanceMetadataNotFound, + db.volume_snapshot_glance_metadata_get, + self.context, snapshot_id) + + # ensure that status of snapshot is 'available' + snapshot_ref = db.snapshot_get(self.context, snapshot_id)['status'] + self.assertEqual('available', snapshot_ref) + + # cleanup resource + db.snapshot_destroy(self.context, snapshot_id) + db.volume_destroy(self.context, volume_id) + def test_delete_busy_snapshot(self): """Test snapshot can be created and deleted.""" diff --git a/cinder/volume/flows/manager/create_volume.py b/cinder/volume/flows/manager/create_volume.py index acc50fabb..fea8a0631 100644 --- a/cinder/volume/flows/manager/create_volume.py +++ b/cinder/volume/flows/manager/create_volume.py @@ -394,6 +394,11 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask): 'vol_id': volume_id}) self._capture_volume_image_metadata(context, volume_id, image_id, image_meta) + except exception.GlanceMetadataNotFound: + # If volume is not created from image, No glance metadata + # would be available for that volume in + # volume glance metadata table + pass except exception.CinderException as ex: LOG.exception(exception_template % {'src_type': src_type, 'src_id': src_id, diff --git a/cinder/volume/manager.py b/cinder/volume/manager.py index e488ecd36..5033c0689 100644 --- a/cinder/volume/manager.py +++ b/cinder/volume/manager.py @@ -568,6 +568,11 @@ class VolumeManager(manager.SchedulerDependentManager): try: self.db.volume_glance_metadata_copy_to_snapshot( context, snapshot_ref['id'], volume_id) + except exception.GlanceMetadataNotFound: + # If volume is not created from image, No glance metadata + # would be available for that volume in + # volume glance metadata table + pass except exception.CinderException as ex: LOG.exception(_LE("Failed updating %(snapshot_id)s" " metadata using the provided volumes" -- 2.45.2