From f9253a4adea0cc8cb84b17b7c95ee600224f19a0 Mon Sep 17 00:00:00 2001 From: whoami-rajat Date: Thu, 17 Mar 2022 14:11:15 +0530 Subject: Add coverage for add method This patch adds test coverage of code paths like extend volume, exception blocks, when image_size is zero, volume delete call etc Change-Id: I59b1579dc9877668b82d4195431c14cc41cfe892 --- glance_store/tests/unit/test_cinder_base.py | 167 ++++++++++++++++++++++ glance_store/tests/unit/test_cinder_store.py | 3 + glance_store/tests/unit/test_multistore_cinder.py | 3 + 3 files changed, 173 insertions(+) diff --git a/glance_store/tests/unit/test_cinder_base.py b/glance_store/tests/unit/test_cinder_base.py index 245372d..d9e6c2d 100644 --- a/glance_store/tests/unit/test_cinder_base.py +++ b/glance_store/tests/unit/test_cinder_base.py @@ -570,6 +570,173 @@ class TestCinderStoreBase(object): if is_multi_store: self.assertEqual(backend, metadata["store"]) + def test_cinder_add_volume_not_found(self): + image_file = mock.MagicMock() + fake_image_id = str(uuid.uuid4()) + expected_size = 0 + fake_volumes = mock.MagicMock(create=mock.MagicMock( + side_effect=cinder.cinder_exception.NotFound(code=404))) + + with mock.patch.object(cinder.Store, 'get_cinderclient') as mock_cc: + mock_cc.return_value = mock.MagicMock(volumes=fake_volumes) + self.assertRaises( + exceptions.BackendException, self.store.add, + fake_image_id, image_file, expected_size, self.hash_algo, + self.context, None) + + def _test_cinder_add_extend(self, is_multi_store=False): + + expected_volume_size = 2 * units.Gi + expected_multihash = 'fake_hash' + + fakebuffer = mock.MagicMock() + fakebuffer.__len__.return_value = expected_volume_size + + def get_fake_hash(type, secure=False): + if type == 'md5': + return mock.MagicMock(hexdigest=lambda: expected_checksum) + else: + return mock.MagicMock(hexdigest=lambda: expected_multihash) + + expected_image_id = str(uuid.uuid4()) + expected_volume_id = str(uuid.uuid4()) + expected_size = 0 + image_file = mock.MagicMock( + read=mock.MagicMock(side_effect=[fakebuffer, None])) + fake_volume = mock.MagicMock(id=expected_volume_id, status='available', + size=1) + expected_checksum = 'fake_checksum' + verifier = None + backend = 'glance_store' + + expected_location = 'cinder://%s' % fake_volume.id + if is_multi_store: + # Default backend is 'glance_store' for single store but in case + # of multi store, if the backend option is not passed, we should + # assign it to the default i.e. 'cinder1' + backend = 'cinder1' + expected_location = 'cinder://%s/%s' % (backend, fake_volume.id) + self.config(cinder_volume_type='some_type', group=backend) + + fake_client = mock.MagicMock(auth_token=None, management_url=None) + fake_volume.manager.get.return_value = fake_volume + fake_volumes = mock.MagicMock(create=mock.Mock( + return_value=fake_volume)) + + @contextlib.contextmanager + def fake_open(client, volume, mode): + self.assertEqual('wb', mode) + yield mock.MagicMock() + + with mock.patch.object(cinder.Store, 'get_cinderclient') as mock_cc, \ + mock.patch.object(self.store, '_open_cinder_volume', + side_effect=fake_open), \ + mock.patch.object(cinder.Store, '_wait_resize_device'), \ + mock.patch.object(cinder.utils, 'get_hasher') as fake_hasher, \ + mock.patch.object(cinder.Store, '_wait_volume_status', + return_value=fake_volume) as mock_wait: + mock_cc.return_value = mock.MagicMock(client=fake_client, + volumes=fake_volumes) + + fake_hasher.side_effect = get_fake_hash + loc, size, checksum, multihash, metadata = self.store.add( + expected_image_id, image_file, expected_size, self.hash_algo, + self.context, verifier) + self.assertEqual(expected_location, loc) + self.assertEqual(expected_volume_size, size) + self.assertEqual(expected_checksum, checksum) + self.assertEqual(expected_multihash, multihash) + fake_volumes.create.assert_called_once_with( + 1, + name='image-%s' % expected_image_id, + metadata={'image_owner': self.context.project_id, + 'glance_image_id': expected_image_id, + 'image_size': str(expected_volume_size)}, + volume_type='some_type') + if is_multi_store: + self.assertEqual(backend, metadata["store"]) + fake_volume.extend.assert_called_once_with( + fake_volume, expected_volume_size // units.Gi) + mock_wait.assert_has_calls( + [mock.call(fake_volume, 'creating', 'available'), + mock.call(fake_volume, 'extending', 'available')]) + + def test_cinder_add_extend_storage_full(self): + + expected_volume_size = 2 * units.Gi + + fakebuffer = mock.MagicMock() + fakebuffer.__len__.return_value = expected_volume_size + + expected_image_id = str(uuid.uuid4()) + expected_volume_id = str(uuid.uuid4()) + expected_size = 0 + image_file = mock.MagicMock( + read=mock.MagicMock(side_effect=[fakebuffer, None])) + fake_volume = mock.MagicMock(id=expected_volume_id, status='available', + size=1) + verifier = None + + fake_client = mock.MagicMock() + fake_volume.manager.get.return_value = fake_volume + fake_volumes = mock.MagicMock(create=mock.Mock( + return_value=fake_volume)) + + with mock.patch.object(cinder.Store, 'get_cinderclient') as mock_cc, \ + mock.patch.object(self.store, '_open_cinder_volume'), \ + mock.patch.object(cinder.Store, '_wait_resize_device'), \ + mock.patch.object(cinder.utils, 'get_hasher'), \ + mock.patch.object( + cinder.Store, '_wait_volume_status') as mock_wait: + + mock_cc.return_value = mock.MagicMock(client=fake_client, + volumes=fake_volumes) + + mock_wait.side_effect = [fake_volume, exceptions.BackendException] + self.assertRaises( + exceptions.StorageFull, self.store.add, expected_image_id, + image_file, expected_size, self.hash_algo, self.context, + verifier) + + def test_cinder_add_extend_volume_delete_exception(self): + + expected_volume_size = 2 * units.Gi + + fakebuffer = mock.MagicMock() + fakebuffer.__len__.return_value = expected_volume_size + + expected_image_id = str(uuid.uuid4()) + expected_volume_id = str(uuid.uuid4()) + expected_size = 0 + image_file = mock.MagicMock( + read=mock.MagicMock(side_effect=[fakebuffer, None])) + fake_volume = mock.MagicMock( + id=expected_volume_id, status='available', size=1, + delete=mock.MagicMock(side_effect=Exception())) + + fake_client = mock.MagicMock() + fake_volume.manager.get.return_value = fake_volume + fake_volumes = mock.MagicMock(create=mock.Mock( + return_value=fake_volume)) + verifier = None + + with mock.patch.object(cinder.Store, 'get_cinderclient') as mock_cc, \ + mock.patch.object(self.store, '_open_cinder_volume'), \ + mock.patch.object(cinder.Store, '_wait_resize_device'), \ + mock.patch.object(cinder.utils, 'get_hasher'), \ + mock.patch.object( + cinder.Store, '_wait_volume_status') as mock_wait: + + mock_cc.return_value = mock.MagicMock(client=fake_client, + volumes=fake_volumes) + + mock_wait.side_effect = [fake_volume, exceptions.BackendException] + self.assertRaises( + Exception, self.store.add, expected_image_id, # noqa + image_file, expected_size, self.hash_algo, self.context, + verifier) + fake_volume.delete.assert_called_once() + def _test_cinder_delete(self, is_multi_store=False): fake_client = mock.MagicMock(auth_token=None, management_url=None) fake_volume_uuid = str(uuid.uuid4()) diff --git a/glance_store/tests/unit/test_cinder_store.py b/glance_store/tests/unit/test_cinder_store.py index 25e098c..efd5419 100644 --- a/glance_store/tests/unit/test_cinder_store.py +++ b/glance_store/tests/unit/test_cinder_store.py @@ -145,6 +145,9 @@ class TestCinderStore(base.StoreBaseTest, fail_resize=True) fake_volume.delete.assert_called_once() + def test_cinder_add_extend(self): + self._test_cinder_add_extend() + def test_cinder_delete(self): self._test_cinder_delete() diff --git a/glance_store/tests/unit/test_multistore_cinder.py b/glance_store/tests/unit/test_multistore_cinder.py index 40e6f51..9161275 100644 --- a/glance_store/tests/unit/test_multistore_cinder.py +++ b/glance_store/tests/unit/test_multistore_cinder.py @@ -283,6 +283,9 @@ class TestMultiCinderStore(base.MultiStoreBaseTest, fail_resize=True, is_multi_store=True) fake_volume.delete.assert_called_once() + def test_cinder_add_extend(self): + self._test_cinder_add_extend(is_multi_store=True) + def test_cinder_delete(self): self._test_cinder_delete(is_multi_store=True) -- cgit v1.2.1