# Copyright 2012 Pedro Navarro Perez # Copyright 2015 Cloudbase Solutions SRL # 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. """ Unit tests for Windows Server 2012 OpenStack Cinder volume driver """ import mock import os from oslo_utils import fileutils from oslo_utils import units from cinder.image import image_utils from cinder import test from cinder.tests.unit.windows import db_fakes from cinder.volume import configuration as conf from cinder.volume.drivers.windows import windows class TestWindowsDriver(test.TestCase): @mock.patch.object(windows, 'utilsfactory') def setUp(self, mock_utilsfactory): super(TestWindowsDriver, self).setUp() configuration = conf.Configuration(None) configuration.append_config_values(windows.windows_opts) self.flags(windows_iscsi_lun_path=mock.sentinel.iscsi_lun_path) self.flags(image_conversion_dir=mock.sentinel.image_conversion_dir) self._driver = windows.WindowsDriver(configuration=configuration) @mock.patch.object(fileutils, 'ensure_tree') def test_do_setup(self, mock_ensure_tree): self._driver.do_setup(mock.sentinel.context) mock_ensure_tree.assert_has_calls( [mock.call(mock.sentinel.iscsi_lun_path), mock.call(mock.sentinel.image_conversion_dir)]) def test_check_for_setup_error(self): self._driver.check_for_setup_error() self._driver._tgt_utils.get_portal_locations.assert_called_once_with( available_only=True, fail_if_none_found=True) @mock.patch.object(windows.WindowsDriver, '_get_target_name') def test_get_host_information(self, mock_get_target_name): tgt_utils = self._driver._tgt_utils fake_auth_meth = 'CHAP' fake_chap_username = 'fake_chap_username' fake_chap_password = 'fake_chap_password' fake_host_info = {'fake_prop': 'fake_value'} fake_volume = db_fakes.get_fake_volume_info() fake_volume['provider_auth'] = "%s %s %s" % (fake_auth_meth, fake_chap_username, fake_chap_password) mock_get_target_name.return_value = mock.sentinel.target_name tgt_utils.get_portal_locations.return_value = [ mock.sentinel.portal_location] tgt_utils.get_target_information.return_value = fake_host_info expected_host_info = dict(fake_host_info, auth_method=fake_auth_meth, auth_username=fake_chap_username, auth_password=fake_chap_password, target_discovered=False, target_portal=mock.sentinel.portal_location, target_lun=0, volume_id=fake_volume['id']) host_info = self._driver._get_host_information(fake_volume) self.assertEqual(expected_host_info, host_info) mock_get_target_name.assert_called_once_with(fake_volume) tgt_utils.get_portal_locations.assert_called_once_with() tgt_utils.get_target_information.assert_called_once_with( mock.sentinel.target_name) @mock.patch.object(windows.WindowsDriver, '_get_host_information') def test_initialize_connection(self, mock_get_host_info): tgt_utils = self._driver._tgt_utils fake_volume = db_fakes.get_fake_volume_info() fake_initiator = db_fakes.get_fake_connector_info() fake_host_info = {'fake_host_prop': 'fake_value'} mock_get_host_info.return_value = fake_host_info expected_conn_info = {'driver_volume_type': 'iscsi', 'data': fake_host_info} conn_info = self._driver.initialize_connection(fake_volume, fake_initiator) self.assertEqual(expected_conn_info, conn_info) mock_associate = tgt_utils.associate_initiator_with_iscsi_target mock_associate.assert_called_once_with( fake_initiator['initiator'], fake_volume['provider_location']) def test_terminate_connection(self): fake_volume = db_fakes.get_fake_volume_info() fake_initiator = db_fakes.get_fake_connector_info() self._driver.terminate_connection(fake_volume, fake_initiator) self._driver._tgt_utils.deassociate_initiator.assert_called_once_with( fake_initiator['initiator'], fake_volume['provider_location']) @mock.patch.object(windows.WindowsDriver, 'local_path') def test_create_volume(self, mock_local_path): fake_volume = db_fakes.get_fake_volume_info() self._driver.create_volume(fake_volume) mock_local_path.assert_called_once_with(fake_volume) self._driver._tgt_utils.create_wt_disk.assert_called_once_with( mock_local_path.return_value, fake_volume['name'], size_mb=fake_volume['size'] * 1024) def test_local_path(self): fake_volume = db_fakes.get_fake_volume_info() fake_lun_path = 'fake_lun_path' self.flags(windows_iscsi_lun_path=fake_lun_path) disk_format = 'vhd' mock_get_fmt = self._driver._tgt_utils.get_supported_disk_format mock_get_fmt.return_value = disk_format disk_path = self._driver.local_path(fake_volume) expected_fname = "%s.%s" % (fake_volume['name'], disk_format) expected_disk_path = os.path.join(fake_lun_path, expected_fname) self.assertEqual(expected_disk_path, disk_path) mock_get_fmt.assert_called_once_with() @mock.patch.object(windows.WindowsDriver, 'local_path') @mock.patch.object(fileutils, 'delete_if_exists') def test_delete_volume(self, mock_delete_if_exists, mock_local_path): fake_volume = db_fakes.get_fake_volume_info() self._driver.delete_volume(fake_volume) mock_local_path.assert_called_once_with(fake_volume) self._driver._tgt_utils.remove_wt_disk.assert_called_once_with( fake_volume['name']) mock_delete_if_exists.assert_called_once_with( mock_local_path.return_value) def test_create_snapshot(self): fake_snapshot = db_fakes.get_fake_snapshot_info() self._driver.create_snapshot(fake_snapshot) self._driver._tgt_utils.create_snapshot.assert_called_once_with( fake_snapshot['volume_name'], fake_snapshot['name']) @mock.patch.object(windows.WindowsDriver, 'local_path') def test_create_volume_from_snapshot(self, mock_local_path): fake_volume = db_fakes.get_fake_volume_info() fake_snapshot = db_fakes.get_fake_snapshot_info() self._driver.create_volume_from_snapshot(fake_volume, fake_snapshot) self._driver._tgt_utils.export_snapshot.assert_called_once_with( fake_snapshot['name'], mock_local_path.return_value) self._driver._tgt_utils.import_wt_disk.assert_called_once_with( mock_local_path.return_value, fake_volume['name']) def test_delete_snapshot(self): fake_snapshot = db_fakes.get_fake_snapshot_info() self._driver.delete_snapshot(fake_snapshot) self._driver._tgt_utils.delete_snapshot.assert_called_once_with( fake_snapshot['name']) def test_get_target_name(self): fake_volume = db_fakes.get_fake_volume_info() expected_target_name = "%s%s" % ( self._driver.configuration.iscsi_target_prefix, fake_volume['name']) target_name = self._driver._get_target_name(fake_volume) self.assertEqual(expected_target_name, target_name) @mock.patch.object(windows.WindowsDriver, '_get_target_name') @mock.patch.object(windows.utils, 'generate_username') @mock.patch.object(windows.utils, 'generate_password') def test_create_export(self, mock_generate_password, mock_generate_username, mock_get_target_name): tgt_utils = self._driver._tgt_utils fake_volume = db_fakes.get_fake_volume_info() self._driver.configuration.chap_username = None self._driver.configuration.chap_password = None self._driver.configuration.use_chap_auth = True fake_chap_username = 'fake_chap_username' fake_chap_password = 'fake_chap_password' mock_get_target_name.return_value = mock.sentinel.target_name mock_generate_username.return_value = fake_chap_username mock_generate_password.return_value = fake_chap_password tgt_utils.iscsi_target_exists.return_value = False vol_updates = self._driver.create_export(mock.sentinel.context, fake_volume, mock.sentinel.connector) mock_get_target_name.assert_called_once_with(fake_volume) tgt_utils.iscsi_target_exists.assert_called_once_with( mock.sentinel.target_name) tgt_utils.set_chap_credentials.assert_called_once_with( mock.sentinel.target_name, fake_chap_username, fake_chap_password) tgt_utils.add_disk_to_target.assert_called_once_with( fake_volume['name'], mock.sentinel.target_name) expected_provider_auth = ' '.join(('CHAP', fake_chap_username, fake_chap_password)) expected_vol_updates = dict( provider_location=mock.sentinel.target_name, provider_auth=expected_provider_auth) self.assertEqual(expected_vol_updates, vol_updates) @mock.patch.object(windows.WindowsDriver, '_get_target_name') def test_remove_export(self, mock_get_target_name): fake_volume = db_fakes.get_fake_volume_info() self._driver.remove_export(mock.sentinel.context, fake_volume) mock_get_target_name.assert_called_once_with(fake_volume) self._driver._tgt_utils.delete_iscsi_target.assert_called_once_with( mock_get_target_name.return_value) @mock.patch.object(windows.WindowsDriver, 'local_path') @mock.patch.object(image_utils, 'temporary_file') @mock.patch.object(image_utils, 'fetch_to_vhd') @mock.patch('os.unlink') def test_copy_image_to_volume(self, mock_unlink, mock_fetch_to_vhd, mock_tmp_file, mock_local_path): tgt_utils = self._driver._tgt_utils fake_volume = db_fakes.get_fake_volume_info() mock_tmp_file.return_value.__enter__.return_value = ( mock.sentinel.tmp_vhd_path) mock_local_path.return_value = mock.sentinel.vol_vhd_path self._driver.copy_image_to_volume(mock.sentinel.context, fake_volume, mock.sentinel.image_service, mock.sentinel.image_id) mock_local_path.assert_called_once_with(fake_volume) mock_tmp_file.assert_called_once_with(suffix='.vhd') image_utils.fetch_to_vhd.assert_called_once_with( mock.sentinel.context, mock.sentinel.image_service, mock.sentinel.image_id, mock.sentinel.tmp_vhd_path, self._driver.configuration.volume_dd_blocksize) mock_unlink.assert_called_once_with(mock.sentinel.vol_vhd_path) self._driver._vhdutils.convert_vhd.assert_called_once_with( mock.sentinel.tmp_vhd_path, mock.sentinel.vol_vhd_path, tgt_utils.get_supported_vhd_type.return_value) self._driver._vhdutils.resize_vhd.assert_called_once_with( mock.sentinel.vol_vhd_path, fake_volume['size'] * units.Gi, is_file_max_size=False) tgt_utils.change_wt_disk_status.assert_has_calls( [mock.call(fake_volume['name'], enabled=False), mock.call(fake_volume['name'], enabled=True)]) @mock.patch.object(windows.uuidutils, 'generate_uuid') def test_temporary_snapshot(self, mock_generate_uuid): tgt_utils = self._driver._tgt_utils mock_generate_uuid.return_value = mock.sentinel.snap_uuid expected_snap_name = '%s-tmp-snapshot-%s' % ( mock.sentinel.volume_name, mock.sentinel.snap_uuid) with self._driver._temporary_snapshot( mock.sentinel.volume_name) as snap_name: self.assertEqual(expected_snap_name, snap_name) tgt_utils.create_snapshot.assert_called_once_with( mock.sentinel.volume_name, expected_snap_name) tgt_utils.delete_snapshot.assert_called_once_with( expected_snap_name) @mock.patch.object(windows.WindowsDriver, '_temporary_snapshot') @mock.patch.object(image_utils, 'upload_volume') @mock.patch.object(fileutils, 'delete_if_exists') def test_copy_volume_to_image(self, mock_delete_if_exists, mock_upload_volume, mock_tmp_snap): tgt_utils = self._driver._tgt_utils disk_format = 'vhd' fake_image_meta = db_fakes.get_fake_image_meta() fake_volume = db_fakes.get_fake_volume_info() fake_img_conv_dir = 'fake_img_conv_dir' self._driver.configuration.image_conversion_dir = fake_img_conv_dir tgt_utils.get_supported_disk_format.return_value = disk_format mock_tmp_snap.return_value.__enter__.return_value = ( mock.sentinel.tmp_snap_name) expected_tmp_vhd_path = os.path.join( fake_img_conv_dir, fake_image_meta['id'] + '.' + disk_format) self._driver.copy_volume_to_image( mock.sentinel.context, fake_volume, mock.sentinel.image_service, fake_image_meta) mock_tmp_snap.assert_called_once_with(fake_volume['name']) tgt_utils.export_snapshot.assert_called_once_with( mock.sentinel.tmp_snap_name, expected_tmp_vhd_path) mock_upload_volume.assert_called_once_with( mock.sentinel.context, mock.sentinel.image_service, fake_image_meta, expected_tmp_vhd_path, 'vhd') mock_delete_if_exists.assert_called_once_with( expected_tmp_vhd_path) @mock.patch.object(windows.WindowsDriver, '_temporary_snapshot') @mock.patch.object(windows.WindowsDriver, 'local_path') def test_create_cloned_volume(self, mock_local_path, mock_tmp_snap): tgt_utils = self._driver._tgt_utils fake_volume = db_fakes.get_fake_volume_info() fake_src_volume = db_fakes.get_fake_volume_info_cloned() mock_tmp_snap.return_value.__enter__.return_value = ( mock.sentinel.tmp_snap_name) mock_local_path.return_value = mock.sentinel.vol_vhd_path self._driver.create_cloned_volume(fake_volume, fake_src_volume) mock_tmp_snap.assert_called_once_with(fake_src_volume['name']) tgt_utils.export_snapshot.assert_called_once_with( mock.sentinel.tmp_snap_name, mock.sentinel.vol_vhd_path) self._driver._vhdutils.resize_vhd.assert_called_once_with( mock.sentinel.vol_vhd_path, fake_volume['size'] * units.Gi, is_file_max_size=False) tgt_utils.import_wt_disk.assert_called_once_with( mock.sentinel.vol_vhd_path, fake_volume['name']) @mock.patch('os.path.splitdrive') def test_get_capacity_info(self, mock_splitdrive): mock_splitdrive.return_value = (mock.sentinel.drive, mock.sentinel.path_tail) fake_size_gb = 2 fake_free_space_gb = 1 self._driver._hostutils.get_volume_info.return_value = ( fake_size_gb * units.Gi, fake_free_space_gb * units.Gi) total_gb, free_gb = self._driver._get_capacity_info() self.assertEqual(fake_size_gb, total_gb) self.assertEqual(fake_free_space_gb, free_gb) self._driver._hostutils.get_volume_info.assert_called_once_with( mock.sentinel.drive) mock_splitdrive.assert_called_once_with( mock.sentinel.iscsi_lun_path) @mock.patch.object(windows.WindowsDriver, '_get_capacity_info') def test_update_volume_stats(self, mock_get_capacity_info): mock_get_capacity_info.return_value = ( mock.sentinel.size_gb, mock.sentinel.free_space_gb) self.flags(volume_backend_name=mock.sentinel.backend_name) self.flags(reserved_percentage=mock.sentinel.reserved_percentage) expected_volume_stats = dict( volume_backend_name=mock.sentinel.backend_name, vendor_name='Microsoft', driver_version=self._driver.VERSION, storage_protocol='iSCSI', total_capacity_gb=mock.sentinel.size_gb, free_capacity_gb=mock.sentinel.free_space_gb, reserved_percentage=mock.sentinel.reserved_percentage, QoS_support=False) self._driver._update_volume_stats() self.assertEqual(expected_volume_stats, self._driver._stats) def test_extend_volume(self): fake_volume = db_fakes.get_fake_volume_info() new_size_gb = 2 expected_additional_sz_mb = 1024 self._driver.extend_volume(fake_volume, new_size_gb) self._driver._tgt_utils.extend_wt_disk.assert_called_once_with( fake_volume['name'], expected_additional_sz_mb)