# Copyright (c) 2015 Tintri. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Volume driver test for Tintri storage. """ import ddt import mock from oslo_utils import units from cinder import context from cinder import exception from cinder.tests.unit import fake_constants as fake from cinder.tests.unit import fake_snapshot from cinder.tests.unit import fake_volume from cinder.tests.unit import utils as cinder_utils from cinder.tests.unit.volume import test_driver from cinder.volume.drivers.tintri import TClient from cinder.volume.drivers.tintri import TintriDriver class FakeImage(object): def __init__(self): self.id = 'image-id' self.name = 'image-name' self.properties = {'provider_location': 'nfs://share'} def __getitem__(self, key): return self.__dict__[key] @ddt.ddt class TintriDriverTestCase(test_driver.BaseDriverTestCase): driver_name = 'cinder.volume.drivers.tintri.TintriDriver' def setUp(self): super(TintriDriverTestCase, self).setUp() self.context = context.get_admin_context() self.configuration.nfs_mount_point_base = '/mnt/test' self.configuration.nfs_mount_options = None self.configuration.nas_mount_options = None self._driver = TintriDriver(configuration=self.configuration) self._driver._hostname = 'host' self._driver._username = 'user' self._driver._password = 'password' self._driver._api_version = 'v310' self._driver._image_cache_expiry = 30 self._provider_location = 'localhost:/share' self._driver._mounted_shares = [self._provider_location] self.fake_stubs() def fake_stubs(self): self.mock_object(TClient, 'login', self.fake_login) self.mock_object(TClient, 'logout', self.fake_logout) self.mock_object(TClient, 'get_snapshot', self.fake_get_snapshot) self.mock_object(TClient, 'get_image_snapshots_to_date', self.fake_get_image_snapshots_to_date) self.mock_object(TintriDriver, '_move_cloned_volume', self.fake_move_cloned_volume) self.mock_object(TintriDriver, '_get_provider_location', self.fake_get_provider_location) self.mock_object(TintriDriver, '_set_rw_permissions', self.fake_set_rw_permissions) self.mock_object(TintriDriver, '_is_volume_present', self.fake_is_volume_present) self.mock_object(TintriDriver, '_is_share_vol_compatible', self.fake_is_share_vol_compatible) self.mock_object(TintriDriver, '_is_file_size_equal', self.fake_is_file_size_equal) def fake_login(self, user_name, password): return 'session-id' def fake_logout(self): pass def fake_get_snapshot(self, volume_id): return fake.SNAPSHOT_ID def fake_get_image_snapshots_to_date(self, date): return [{'uuid': {'uuid': 'image_snapshot-id'}}] def fake_move_cloned_volume(self, clone_name, volume_id, share=None): pass def fake_get_provider_location(self, volume_path): return self._provider_location def fake_set_rw_permissions(self, path): pass def fake_is_volume_present(self, volume_path): return True def fake_is_share_vol_compatible(self, volume, share): return True def fake_is_file_size_equal(self, path, size): return True @mock.patch.object(TClient, 'create_snapshot', mock.Mock(return_value=fake.PROVIDER_ID)) def test_create_snapshot(self): snapshot = fake_snapshot.fake_snapshot_obj(self.context) volume = fake_volume.fake_volume_obj(self.context) provider_id = fake.PROVIDER_ID snapshot.volume = volume with mock.patch('cinder.objects.snapshot.Snapshot.save'): self.assertEqual({'provider_id': fake.PROVIDER_ID}, self._driver.create_snapshot(snapshot)) self.assertEqual(provider_id, snapshot.provider_id) @mock.patch.object(TClient, 'create_snapshot', mock.Mock( side_effect=exception.VolumeDriverException)) def test_create_snapshot_failure(self): snapshot = fake_snapshot.fake_snapshot_obj(self.context) volume = fake_volume.fake_volume_obj(self.context) snapshot.volume = volume self.assertRaises(exception.VolumeDriverException, self._driver.create_snapshot, snapshot) @mock.patch.object(TClient, 'delete_snapshot', mock.Mock()) @mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall', new= cinder_utils.ZeroIntervalLoopingCall) def test_cleanup_cache(self): self.assertFalse(self._driver.cache_cleanup) timer = self._driver._initiate_image_cache_cleanup() # wait for cache cleanup to complete timer.wait() self.assertFalse(self._driver.cache_cleanup) @mock.patch.object(TClient, 'delete_snapshot', mock.Mock( side_effect=exception.VolumeDriverException)) @mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall', new= cinder_utils.ZeroIntervalLoopingCall) def test_cleanup_cache_delete_fail(self): self.assertFalse(self._driver.cache_cleanup) timer = self._driver._initiate_image_cache_cleanup() # wait for cache cleanup to complete timer.wait() self.assertFalse(self._driver.cache_cleanup) @mock.patch.object(TClient, 'delete_snapshot', mock.Mock()) def test_delete_snapshot(self): snapshot = fake_snapshot.fake_snapshot_obj(self.context) snapshot.provider_id = fake.PROVIDER_ID self.assertIsNone(self._driver.delete_snapshot(snapshot)) @mock.patch.object(TClient, 'delete_snapshot', mock.Mock( side_effect=exception.VolumeDriverException)) def test_delete_snapshot_failure(self): snapshot = fake_snapshot.fake_snapshot_obj(self.context) snapshot.provider_id = fake.PROVIDER_ID self.assertRaises(exception.VolumeDriverException, self._driver.delete_snapshot, snapshot) @mock.patch.object(TClient, 'clone_volume', mock.Mock()) def test_create_volume_from_snapshot(self): snapshot = fake_snapshot.fake_snapshot_obj(self.context) volume = fake_volume.fake_volume_obj(self.context) self.assertEqual({'provider_location': self._provider_location}, self._driver.create_volume_from_snapshot( volume, snapshot)) @mock.patch.object(TClient, 'clone_volume', mock.Mock( side_effect=exception.VolumeDriverException)) def test_create_volume_from_snapshot_failure(self): snapshot = fake_snapshot.fake_snapshot_obj(self.context) volume = fake_volume.fake_volume_obj(self.context) self.assertRaises(exception.VolumeDriverException, self._driver.create_volume_from_snapshot, volume, snapshot) @mock.patch.object(TClient, 'clone_volume', mock.Mock()) @mock.patch.object(TClient, 'create_snapshot', mock.Mock()) def test_create_cloned_volume(self): volume = fake_volume.fake_volume_obj(self.context) self.assertEqual({'provider_location': self._provider_location}, self._driver.create_cloned_volume(volume, volume)) @mock.patch.object(TClient, 'clone_volume', mock.Mock( side_effect=exception.VolumeDriverException)) @mock.patch.object(TClient, 'create_snapshot', mock.Mock()) def test_create_cloned_volume_failure(self): volume = fake_volume.fake_volume_obj(self.context) self.assertRaises(exception.VolumeDriverException, self._driver.create_cloned_volume, volume, volume) @mock.patch.object(TClient, 'clone_volume', mock.Mock()) def test_clone_image(self): volume = fake_volume.fake_volume_obj(self.context) self.assertEqual(({'provider_location': self._provider_location, 'bootable': True}, True), self._driver.clone_image( None, volume, 'image-name', FakeImage().__dict__, None)) @mock.patch.object(TClient, 'clone_volume', mock.Mock( side_effect=exception.VolumeDriverException)) def test_clone_image_failure(self): volume = fake_volume.fake_volume_obj(self.context) self.assertEqual(({'provider_location': None, 'bootable': False}, False), self._driver.clone_image( None, volume, 'image-name', FakeImage().__dict__, None)) def test_manage_existing(self): volume = fake_volume.fake_volume_obj(self.context) existing = {'source-name': self._provider_location + '/' + volume.name} with mock.patch('os.path.isfile', return_value=True): self.assertEqual({'provider_location': self._provider_location}, self._driver.manage_existing(volume, existing)) def test_manage_existing_invalid_ref(self): existing = fake_volume.fake_volume_obj(self.context) volume = fake_volume.fake_volume_obj(self.context) self.assertRaises(exception.ManageExistingInvalidReference, self._driver.manage_existing, volume, existing) def test_manage_existing_not_found(self): volume = fake_volume.fake_volume_obj(self.context) existing = {'source-name': self._provider_location + '/' + volume.name} with mock.patch('os.path.isfile', return_value=False): self.assertRaises(exception.ManageExistingInvalidReference, self._driver.manage_existing, volume, existing) @mock.patch.object(TintriDriver, '_move_file', mock.Mock( return_value=False)) def test_manage_existing_move_failure(self): volume = fake_volume.fake_volume_obj(self.context) existing = {'source-name': self._provider_location + '/source-volume'} with mock.patch('os.path.isfile', return_value=True): self.assertRaises(exception.VolumeDriverException, self._driver.manage_existing, volume, existing) @ddt.data((123, 123), (123.5, 124)) @ddt.unpack def test_manage_existing_get_size(self, st_size, exp_size): volume = fake_volume.fake_volume_obj(self.context) existing = {'source-name': self._provider_location + '/' + volume.name} file = mock.Mock(st_size=int(st_size * units.Gi)) with mock.patch('os.path.isfile', return_value=True): with mock.patch('os.stat', return_value=file): self.assertEqual(exp_size, self._driver.manage_existing_get_size( volume, existing)) def test_manage_existing_get_size_failure(self): volume = fake_volume.fake_volume_obj(self.context) existing = {'source-name': self._provider_location + '/' + volume.name} with mock.patch('os.path.isfile', return_value=True): with mock.patch('os.stat', side_effect=OSError): self.assertRaises(exception.VolumeDriverException, self._driver.manage_existing_get_size, volume, existing) def test_unmanage(self): volume = fake_volume.fake_volume_obj(self.context) volume.provider_location = self._provider_location self._driver.unmanage(volume) def test_retype(self): volume = fake_volume.fake_volume_obj(self.context) retype, update = self._driver.retype(None, volume, None, None, None) self.assertTrue(retype) self.assertIsNone(update)