From c5368a739456a2864b731ed40de9d48190dd1765 Mon Sep 17 00:00:00 2001 From: Tom Swanson Date: Mon, 5 Dec 2016 17:16:40 -0600 Subject: [PATCH] Dell SC: Missing volume creation options. The Dell SC driver is missing options for Data Reduction, Group QOS and Volume QOS. Added. Change-Id: I01951e41eea9985471830b5410892be37e13805a --- cinder/exception.py | 4 + .../unit/volume/drivers/dell/test_dellsc.py | 177 ++++- .../volume/drivers/dell/test_dellscapi.py | 690 +++++++++++++++++- .../drivers/dell/dell_storagecenter_api.py | 221 ++++-- .../drivers/dell/dell_storagecenter_common.py | 74 +- ...l-SC-New-Extra-Specs-1de0d3f1ebc62881.yaml | 8 + 6 files changed, 1045 insertions(+), 129 deletions(-) create mode 100644 releasenotes/notes/Dell-SC-New-Extra-Specs-1de0d3f1ebc62881.yaml diff --git a/cinder/exception.py b/cinder/exception.py index 05b61994967..3c8e6f47e93 100644 --- a/cinder/exception.py +++ b/cinder/exception.py @@ -898,6 +898,10 @@ class DellDriverRetryableException(VolumeBackendAPIException): message = _("Retryable Dell Exception encountered") +class DellDriverUnknownSpec(VolumeDriverException): + message = _("Dell driver failure: %(reason)s") + + # Pure Storage class PureDriverException(VolumeDriverException): message = _("Pure Storage Cinder driver failure: %(reason)s") diff --git a/cinder/tests/unit/volume/drivers/dell/test_dellsc.py b/cinder/tests/unit/volume/drivers/dell/test_dellsc.py index 383832862a2..6066cc27d90 100644 --- a/cinder/tests/unit/volume/drivers/dell/test_dellsc.py +++ b/cinder/tests/unit/volume/drivers/dell/test_dellsc.py @@ -568,10 +568,8 @@ class DellSCSanISCSIDriverTestCase(test.TestCase): mock_init): volume = {'id': fake.VOLUME_ID, 'size': 1} self.driver.create_volume(volume) - mock_create_volume.assert_called_once_with(fake.VOLUME_ID, - 1, - None, - None) + mock_create_volume.assert_called_once_with( + fake.VOLUME_ID, 1, None, None, None, None, None) @mock.patch.object(dell_storagecenter_api.StorageCenterApi, 'find_replay_profile', @@ -591,13 +589,65 @@ class DellSCSanISCSIDriverTestCase(test.TestCase): volume = {'id': fake.VOLUME_ID, 'size': 1, 'consistencygroup_id': fake.CONSISTENCY_GROUP_ID} self.driver.create_volume(volume) - mock_create_volume.assert_called_once_with(fake.VOLUME_ID, - 1, - None, - None) + mock_create_volume.assert_called_once_with( + fake.VOLUME_ID, 1, None, None, None, None, None) self.assertTrue(mock_find_replay_profile.called) self.assertTrue(mock_update_cg_volumes.called) + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'create_volume', + return_value=VOLUME) + @mock.patch.object( + volume_types, + 'get_volume_type_extra_specs', + return_value={'storagetype:volumeqos': 'volumeqos'}) + def test_create_volume_volumeqos_profile(self, + mock_extra, + mock_create_volume, + mock_close_connection, + mock_open_connection, + mock_init): + volume = {'id': fake.VOLUME_ID, 'size': 1, 'volume_type_id': 'abc'} + self.driver.create_volume(volume) + mock_create_volume.assert_called_once_with( + fake.VOLUME_ID, 1, None, None, 'volumeqos', None, None) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'create_volume', + return_value=VOLUME) + @mock.patch.object( + volume_types, + 'get_volume_type_extra_specs', + return_value={'storagetype:groupqos': 'groupqos'}) + def test_create_volume_groupqos_profile(self, + mock_extra, + mock_create_volume, + mock_close_connection, + mock_open_connection, + mock_init): + volume = {'id': fake.VOLUME_ID, 'size': 1, 'volume_type_id': 'abc'} + self.driver.create_volume(volume) + mock_create_volume.assert_called_once_with( + fake.VOLUME_ID, 1, None, None, None, 'groupqos', None) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'create_volume', + return_value=VOLUME) + @mock.patch.object( + volume_types, + 'get_volume_type_extra_specs', + return_value={'storagetype:datareductionprofile': 'drprofile'}) + def test_create_volume_data_reduction_profile(self, + mock_extra, + mock_create_volume, + mock_close_connection, + mock_open_connection, + mock_init): + volume = {'id': fake.VOLUME_ID, 'size': 1, 'volume_type_id': 'abc'} + self.driver.create_volume(volume) + mock_create_volume.assert_called_once_with( + fake.VOLUME_ID, 1, None, None, None, None, 'drprofile') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, 'create_volume', return_value=VOLUME) @@ -613,10 +663,8 @@ class DellSCSanISCSIDriverTestCase(test.TestCase): mock_init): volume = {'id': fake.VOLUME_ID, 'size': 1, 'volume_type_id': 'abc'} self.driver.create_volume(volume) - mock_create_volume.assert_called_once_with(fake.VOLUME_ID, - 1, - "HighPriority", - None) + mock_create_volume.assert_called_once_with( + fake.VOLUME_ID, 1, "HighPriority", None, None, None, None) @mock.patch.object(dell_storagecenter_api.StorageCenterApi, 'create_volume', @@ -633,10 +681,8 @@ class DellSCSanISCSIDriverTestCase(test.TestCase): mock_init): volume = {'id': fake.VOLUME_ID, 'size': 1, 'volume_type_id': 'abc'} self.driver.create_volume(volume) - mock_create_volume.assert_called_once_with(fake.VOLUME_ID, - 1, - None, - 'Daily') + mock_create_volume.assert_called_once_with( + fake.VOLUME_ID, 1, None, 'Daily', None, None, None) @mock.patch.object(dell_storagecenter_api.StorageCenterApi, 'create_volume', @@ -1492,9 +1538,50 @@ class DellSCSanISCSIDriverTestCase(test.TestCase): snapshot = {'id': fake.SNAPSHOT_ID, 'volume_id': fake.VOLUME_ID, 'volume_size': 1} res = self.driver.create_volume_from_snapshot(volume, snapshot) - mock_create_view_volume.assert_called_once_with(fake.VOLUME_ID, - 'fake', - None) + mock_create_view_volume.assert_called_once_with( + fake.VOLUME_ID, 'fake', None, None, None, None) + self.assertTrue(mock_find_replay.called) + self.assertTrue(mock_find_volume.called) + self.assertFalse(mock_find_replay_profile.called) + # This just makes sure that we created + self.assertTrue(mock_create_replications.called) + self.assertEqual(model_update, res) + + @mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver, + '_create_replications') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'find_replay_profile') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'find_volume') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'find_replay') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'create_view_volume') + @mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver, + '_get_volume_extra_specs') + def test_create_volume_from_snapshot_with_profiles( + self, mock_get_volume_extra_specs, mock_create_view_volume, + mock_find_replay, mock_find_volume, mock_find_replay_profile, + mock_create_replications, mock_close_connection, + mock_open_connection, mock_init): + mock_get_volume_extra_specs.return_value = { + 'storagetype:replayprofiles': 'replayprofiles', + 'storagetype:volumeqos': 'volumeqos', + 'storagetype:groupqos': 'groupqos', + 'storagetype:datareductionprofile': 'drprofile'} + + mock_create_view_volume.return_value = self.VOLUME + mock_find_replay.return_value = 'fake' + mock_find_volume.return_value = self.VOLUME + model_update = {'something': 'something'} + mock_create_replications.return_value = model_update + volume = {'id': fake.VOLUME_ID, 'size': 1} + snapshot = {'id': fake.SNAPSHOT_ID, 'volume_id': fake.VOLUME_ID, + 'volume_size': 1} + res = self.driver.create_volume_from_snapshot(volume, snapshot) + mock_create_view_volume.assert_called_once_with( + fake.VOLUME_ID, 'fake', 'replayprofiles', 'volumeqos', 'groupqos', + 'drprofile') self.assertTrue(mock_find_replay.called) self.assertTrue(mock_find_volume.called) self.assertFalse(mock_find_replay_profile.called) @@ -1534,9 +1621,8 @@ class DellSCSanISCSIDriverTestCase(test.TestCase): snapshot = {'id': fake.SNAPSHOT_ID, 'volume_id': fake.VOLUME_ID, 'volume_size': 1} res = self.driver.create_volume_from_snapshot(volume, snapshot) - mock_create_view_volume.assert_called_once_with(fake.VOLUME_ID, - 'fake', - None) + mock_create_view_volume.assert_called_once_with( + fake.VOLUME_ID, 'fake', None, None, None, None) self.assertTrue(mock_find_replay.called) self.assertTrue(mock_find_volume.called) self.assertFalse(mock_find_replay_profile.called) @@ -1578,9 +1664,8 @@ class DellSCSanISCSIDriverTestCase(test.TestCase): snapshot = {'id': fake.SNAPSHOT_ID, 'volume_id': fake.VOLUME_ID, 'volume_size': 1} res = self.driver.create_volume_from_snapshot(volume, snapshot) - mock_create_view_volume.assert_called_once_with(fake.VOLUME_ID, - 'fake', - None) + mock_create_view_volume.assert_called_once_with( + fake.VOLUME_ID, 'fake', None, None, None, None) self.assertTrue(mock_find_replay.called) self.assertTrue(mock_find_volume.called) self.assertTrue(mock_find_replay_profile.called) @@ -1700,9 +1785,37 @@ class DellSCSanISCSIDriverTestCase(test.TestCase): src_vref = {'id': fake.VOLUME2_ID, 'size': 1} ret = self.driver.create_cloned_volume(volume, src_vref) mock_create_cloned_volume.assert_called_once_with( - fake.VOLUME_ID, - self.VOLUME, - None) + fake.VOLUME_ID, self.VOLUME, None, None, None, None) + self.assertTrue(mock_find_volume.called) + self.assertEqual({'provider_id': provider_id}, ret) + + @mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver, + '_create_replications') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'find_volume') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'create_cloned_volume') + @mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver, + '_get_volume_extra_specs') + def test_create_cloned_volume_with_profiles( + self, mock_get_volume_extra_specs, mock_create_cloned_volume, + mock_find_volume, mock_create_replications, mock_close_connection, + mock_open_connection, mock_init): + mock_get_volume_extra_specs.return_value = { + 'storagetype:replayprofiles': 'replayprofiles', + 'storagetype:volumeqos': 'volumeqos', + 'storagetype:groupqos': 'groupqos', + 'storagetype:datareductionprofile': 'drprofile'} + mock_find_volume.return_value = self.VOLUME + mock_create_cloned_volume.return_value = self.VOLUME + mock_create_replications.return_value = {} + provider_id = self.VOLUME[u'instanceId'] + volume = {'id': fake.VOLUME_ID, 'size': 1} + src_vref = {'id': fake.VOLUME2_ID, 'size': 1} + ret = self.driver.create_cloned_volume(volume, src_vref) + mock_create_cloned_volume.assert_called_once_with( + fake.VOLUME_ID, self.VOLUME, 'replayprofiles', 'volumeqos', + 'groupqos', 'drprofile') self.assertTrue(mock_find_volume.called) self.assertEqual({'provider_id': provider_id}, ret) @@ -1731,9 +1844,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase): src_vref = {'id': fake.VOLUME2_ID, 'size': 1} ret = self.driver.create_cloned_volume(volume, src_vref) mock_create_cloned_volume.assert_called_once_with( - fake.VOLUME_ID, - self.VOLUME, - None) + fake.VOLUME_ID, self.VOLUME, None, None, None, None) self.assertTrue(mock_find_volume.called) self.assertEqual({'provider_id': provider_id}, ret) self.assertTrue(mock_expand_volume.called) @@ -1847,9 +1958,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase): src_vref = {'id': fake.VOLUME2_ID, 'size': 1} self.driver.create_cloned_volume(volume, src_vref) mock_create_cloned_volume.assert_called_once_with( - fake.VOLUME_ID, - self.VOLUME, - None) + fake.VOLUME_ID, self.VOLUME, None, None, None, None) self.assertTrue(mock_find_volume.called) self.assertTrue(mock_find_replay_profile.called) self.assertTrue(mock_update_cg_volumes.called) diff --git a/cinder/tests/unit/volume/drivers/dell/test_dellscapi.py b/cinder/tests/unit/volume/drivers/dell/test_dellscapi.py index 0550322b170..8aeea4b5e5c 100644 --- a/cinder/tests/unit/volume/drivers/dell/test_dellscapi.py +++ b/cinder/tests/unit/volume/drivers/dell/test_dellscapi.py @@ -2097,6 +2097,84 @@ class DellSCSanAPITestCase(test.TestCase): mock_find_volume_folder.assert_called_once_with(True) self.assertEqual(self.VOLUME, res, 'Unexpected ScVolume') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_json') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_volume_folder') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_qos_profile') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_data_reduction_profile') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_storage_profile') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_replay_profiles') + def test_create_volume_with_profiles(self, + mock_find_replay_profiles, + mock_find_storage_profile, + mock_find_data_reduction_profile, + mock_find_qos_profile, + mock_post, + mock_find_volume_folder, + mock_get_json, + mock_close_connection, + mock_open_connection, + mock_init): + mock_find_replay_profiles.return_value = (['12345.4'], []) + mock_get_json.return_value = self.VOLUME + mock_find_volume_folder.return_value = {'instanceId': '12345.200'} + mock_post.return_value = self.RESPONSE_201 + mock_find_storage_profile.return_value = {'instanceId': '12345.0'} + mock_find_data_reduction_profile.return_value = {'instanceId': + '12345.1'} + mock_find_qos_profile.side_effect = [{'instanceId': '12345.2'}, + {'instanceId': '12345.3'}] + res = self.scapi.create_volume(self.volume_name, 1, 'storage_profile', + 'replay_profile_string', 'volume_qos', + 'group_qos', 'datareductionprofile') + expected_payload = {'Name': self.volume_name, + 'Notes': 'Created by Dell Cinder Driver', + 'Size': '1 GB', + 'StorageCenter': 12345, + 'VolumeFolder': '12345.200', + 'StorageProfile': '12345.0', + 'VolumeQosProfile': '12345.2', + 'GroupQosProfile': '12345.3', + 'DataReductionProfile': '12345.1', + 'ReplayProfileList': ['12345.4']} + mock_find_volume_folder.assert_called_once_with(True) + mock_post.assert_called_once_with('StorageCenter/ScVolume', + expected_payload, True) + self.assertEqual(self.VOLUME, res) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_volume_folder') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_qos_profile') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_storage_profile') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_replay_profiles') + def test_create_volume_profile_not_found(self, + mock_find_replay_profiles, + mock_find_storage_profile, + mock_find_qos_profile, + mock_find_volume_folder, + mock_close_connection, + mock_open_connection, + mock_init): + mock_find_replay_profiles.return_value = (['12345.4'], []) + mock_find_volume_folder.return_value = self.FLDR + mock_find_storage_profile.return_value = [{'instanceId': '12345.0'}] + # Failure is on the volumeqosprofile. + mock_find_qos_profile.return_value = None + self.assertRaises(exception.VolumeBackendAPIException, + self.scapi.create_volume, self.volume_name, 1, + 'storage_profile', 'replay_profile_string', + 'volume_qos', 'group_qos', 'datareductionprofile') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, '_find_storage_profile', return_value=None) @@ -4351,9 +4429,7 @@ class DellSCSanAPITestCase(test.TestCase): mock_init): vol_name = u'Test_create_vol' res = self.scapi.create_view_volume( - vol_name, - self.TST_RPLAY, - None) + vol_name, self.TST_RPLAY, None, None, None, None) self.assertTrue(mock_post.called) mock_find_volume_folder.assert_called_once_with(True) self.assertTrue(mock_first_result.called) @@ -4378,9 +4454,7 @@ class DellSCSanAPITestCase(test.TestCase): # Test case where volume folder does not exist and must be created vol_name = u'Test_create_vol' res = self.scapi.create_view_volume( - vol_name, - self.TST_RPLAY, - None) + vol_name, self.TST_RPLAY, None, None, None, None) self.assertTrue(mock_post.called) mock_find_volume_folder.assert_called_once_with(True) self.assertTrue(mock_first_result.called) @@ -4405,9 +4479,7 @@ class DellSCSanAPITestCase(test.TestCase): # Test case where volume folder does not exist and cannot be created vol_name = u'Test_create_vol' res = self.scapi.create_view_volume( - vol_name, - self.TST_RPLAY, - None) + vol_name, self.TST_RPLAY, None, None, None, None) self.assertTrue(mock_post.called) mock_find_volume_folder.assert_called_once_with(True) self.assertTrue(mock_first_result.called) @@ -4428,13 +4500,148 @@ class DellSCSanAPITestCase(test.TestCase): # Test case where view volume create fails vol_name = u'Test_create_vol' res = self.scapi.create_view_volume( - vol_name, - self.TST_RPLAY, - None) + vol_name, self.TST_RPLAY, None, None, None, None) self.assertTrue(mock_post.called) mock_find_volume_folder.assert_called_once_with(True) self.assertIsNone(res, 'Expected None') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_first_result') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_volume_folder') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_qos_profile') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_replay_profiles') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'update_datareduction_profile') + def test_create_view_volume_with_profiles( + self, mock_update_datareduction_profile, mock_find_replay_profiles, + mock_find_qos_profile, mock_post, mock_find_volume_folder, + mock_first_result, mock_close_connection, mock_open_connection, + mock_init): + mock_find_replay_profiles.return_value = (['12345.4'], []) + mock_first_result.return_value = {'name': 'name'} + mock_post.return_value = self.RESPONSE_200 + mock_find_volume_folder.return_value = {'instanceId': '12345.200'} + mock_find_qos_profile.side_effect = [{'instanceId': '12345.2'}, + {'instanceId': '12345.3'}] + screplay = {'instanceId': '12345.100.1'} + res = self.scapi.create_view_volume( + 'name', screplay, 'replay_profile_string', 'volume_qos', + 'group_qos', 'datareductionprofile') + expected_payload = {'Name': 'name', + 'Notes': 'Created by Dell Cinder Driver', + 'VolumeFolder': '12345.200', + 'ReplayProfileList': ['12345.4'], + 'VolumeQosProfile': '12345.2', + 'GroupQosProfile': '12345.3'} + mock_find_volume_folder.assert_called_once_with(True) + mock_post.assert_called_once_with( + 'StorageCenter/ScReplay/12345.100.1/CreateView', expected_payload, + True) + mock_update_datareduction_profile.assert_called_once_with( + {'name': 'name'}, 'datareductionprofile') + self.assertEqual({'name': 'name'}, res) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_first_result') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_volume_folder') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_qos_profile') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_replay_profiles') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + 'update_datareduction_profile') + def test_create_view_volume_with_profiles_no_dr( + self, mock_update_datareduction_profile, mock_find_replay_profiles, + mock_find_qos_profile, mock_post, mock_find_volume_folder, + mock_first_result, mock_close_connection, mock_open_connection, + mock_init): + mock_find_replay_profiles.return_value = (['12345.4'], []) + mock_first_result.return_value = {'name': 'name'} + mock_post.return_value = self.RESPONSE_200 + mock_find_volume_folder.return_value = {'instanceId': '12345.200'} + mock_find_qos_profile.side_effect = [{'instanceId': '12345.2'}, + {'instanceId': '12345.3'}] + screplay = {'instanceId': '12345.100.1'} + res = self.scapi.create_view_volume('name', screplay, + 'replay_profile_string', + 'volume_qos', + 'group_qos', + None) + expected_payload = {'Name': 'name', + 'Notes': 'Created by Dell Cinder Driver', + 'VolumeFolder': '12345.200', + 'ReplayProfileList': ['12345.4'], + 'VolumeQosProfile': '12345.2', + 'GroupQosProfile': '12345.3'} + mock_find_volume_folder.assert_called_once_with(True) + mock_post.assert_called_once_with( + 'StorageCenter/ScReplay/12345.100.1/CreateView', expected_payload, + True) + mock_update_datareduction_profile.assert_not_called() + self.assertEqual({'name': 'name'}, res) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_first_result') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_volume_folder') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_qos_profile') + def test_create_view_volume_with_profiles_no_replayprofiles( + self, mock_find_qos_profile, mock_post, mock_find_volume_folder, + mock_first_result, mock_close_connection, mock_open_connection, + mock_init): + mock_first_result.return_value = {'name': 'name'} + mock_post.return_value = self.RESPONSE_200 + mock_find_volume_folder.return_value = {'instanceId': '12345.200'} + mock_find_qos_profile.side_effect = [{'instanceId': '12345.2'}, + {'instanceId': '12345.3'}] + screplay = {'instanceId': '12345.100.1'} + res = self.scapi.create_view_volume('name', screplay, + None, + 'volume_qos', + 'group_qos', + None) + expected_payload = {'Name': 'name', + 'Notes': 'Created by Dell Cinder Driver', + 'VolumeFolder': '12345.200', + 'VolumeQosProfile': '12345.2', + 'GroupQosProfile': '12345.3'} + mock_find_volume_folder.assert_called_once_with(True) + mock_post.assert_called_once_with( + 'StorageCenter/ScReplay/12345.100.1/CreateView', expected_payload, + True) + self.assertEqual({'name': 'name'}, res) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_volume_folder') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_qos_profile') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_replay_profiles') + def test_create_view_volume_with_profiles_not_found( + self, mock_find_replay_profiles, mock_find_qos_profile, + mock_find_volume_folder, mock_close_connection, + mock_open_connection, mock_init): + mock_find_replay_profiles.return_value = (['12345.4'], []) + mock_find_volume_folder.return_value = {'instanceId': '12345.200'} + # Our qos profile isn't found. + mock_find_qos_profile.return_value = None + screplay = {'instanceId': '12345.100.1'} + self.assertRaises(exception.VolumeBackendAPIException, + self.scapi.create_view_volume, + 'name', screplay, 'replay_profile_string', + 'volume_qos', 'group_qos', 'datareductionprofile') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, 'create_view_volume', return_value=VOLUME) @@ -4449,16 +4656,14 @@ class DellSCSanAPITestCase(test.TestCase): mock_init): vol_name = u'Test_create_clone_vol' res = self.scapi.create_cloned_volume( - vol_name, - self.VOLUME, - ['Daily']) + vol_name, self.VOLUME, ['Daily'], + 'volume_qos', 'group_qos', 'dr_profile') mock_create_replay.assert_called_once_with(self.VOLUME, 'Cinder Clone Replay', 60) mock_create_view_volume.assert_called_once_with( - vol_name, - self.RPLAY, - ['Daily']) + vol_name, self.RPLAY, ['Daily'], + 'volume_qos', 'group_qos', 'dr_profile') self.assertEqual(self.VOLUME, res, 'Unexpected ScVolume') @mock.patch.object(dell_storagecenter_api.StorageCenterApi, @@ -4477,9 +4682,7 @@ class DellSCSanAPITestCase(test.TestCase): vol_name = u'Test_create_clone_vol' mock_create_replay.return_value = None res = self.scapi.create_cloned_volume( - vol_name, - self.VOLUME, - ['Daily']) + vol_name, self.VOLUME, ['Daily'], None, None, None) mock_create_replay.assert_called_once_with(self.VOLUME, 'Cinder Clone Replay', 60) @@ -4488,13 +4691,11 @@ class DellSCSanAPITestCase(test.TestCase): # Again buy let create_view_volume fail. mock_create_replay.return_value = self.RPLAY res = self.scapi.create_cloned_volume( - vol_name, - self.VOLUME, - ['Daily']) + vol_name, self.VOLUME, ['Daily'], + 'volume_qos', 'group_qos', 'dr_profile') mock_create_view_volume.assert_called_once_with( - vol_name, - self.RPLAY, - ['Daily']) + vol_name, self.RPLAY, ['Daily'], + 'volume_qos', 'group_qos', 'dr_profile') self.assertIsNone(res) @mock.patch.object(dell_storagecenter_api.StorageCenterApi, @@ -7107,6 +7308,441 @@ class DellSCSanAPITestCase(test.TestCase): True) self.assertFalse(ret) + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post') + def test_swap_roles_live_volume(self, + mock_post, + mock_close_connection, + mock_open_connection, + mock_init): + mock_post.return_value = self.RESPONSE_200 + lv = {'instanceId': '12345.0'} + ret = self.scapi.swap_roles_live_volume(lv) + self.assertTrue(ret) + + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post') + def test_swap_roles_live_volume_fail(self, + mock_post, + mock_close_connection, + mock_open_connection, + mock_init): + mock_post.return_value = self.RESPONSE_400 + lv = {'instanceId': '12345.0'} + ret = self.scapi.swap_roles_live_volume(lv) + self.assertFalse(ret) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_json') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post') + def test__find_qos_profile(self, + mock_post, + mock_get_json, + mock_close_connection, + mock_open_connection, + mock_init): + mock_post.return_value = self.RESPONSE_200 + mock_get_json.return_value = [{'instanceId': '12345.0'}] + expected_payload = {'filter': {'filterType': 'AND', 'filters': [ + {'filterType': 'Equals', 'attributeName': 'ScSerialNumber', + 'attributeValue': 12345}, + {'filterType': 'Equals', 'attributeName': 'Name', + 'attributeValue': 'Default'}, + {'filterType': 'Equals', 'attributeName': 'profileType', + 'attributeValue': 'VolumeQosProfile'}]}} + ret = self.scapi._find_qos_profile('Default', False) + self.assertEqual({'instanceId': '12345.0'}, ret) + mock_post.assert_called_once_with('StorageCenter/ScQosProfile/GetList', + expected_payload) + + def test__find_qos_no_qosprofile(self, + mock_close_connection, + mock_open_connection, + mock_init): + ret = self.scapi._find_qos_profile('', False) + self.assertIsNone(ret) + + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post') + def test__find_qos_error(self, + mock_post, + mock_close_connection, + mock_open_connection, + mock_init): + mock_post.return_value = self.RESPONSE_400 + ret = self.scapi._find_qos_profile('Default', False) + self.assertIsNone(ret) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_json') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post') + def test__find_qos_profile_empty_list(self, + mock_post, + mock_get_json, + mock_close_connection, + mock_open_connection, + mock_init): + mock_post.return_value = self.RESPONSE_200 + mock_get_json.return_value = [] + ret = self.scapi._find_qos_profile('Default', False) + self.assertIsNone(ret) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_json') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post') + def test__find_qos_profile_group(self, + mock_post, + mock_get_json, + mock_close_connection, + mock_open_connection, + mock_init): + mock_post.return_value = self.RESPONSE_200 + mock_get_json.return_value = [{'instanceId': '12345.0'}] + expected_payload = {'filter': {'filterType': 'AND', 'filters': [ + {'filterType': 'Equals', 'attributeName': 'ScSerialNumber', + 'attributeValue': 12345}, + {'filterType': 'Equals', 'attributeName': 'Name', + 'attributeValue': 'Default'}, + {'filterType': 'Equals', 'attributeName': 'profileType', + 'attributeValue': 'GroupQosProfile'}]}} + ret = self.scapi._find_qos_profile('Default', True) + self.assertEqual({'instanceId': '12345.0'}, ret) + mock_post.assert_called_once_with('StorageCenter/ScQosProfile/GetList', + expected_payload) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_json') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post') + def test__find_datareduction_profile(self, + mock_post, + mock_get_json, + mock_close_connection, + mock_open_connection, + mock_init): + mock_post.return_value = self.RESPONSE_200 + mock_get_json.return_value = [{'instanceId': '12345.0'}] + expected_payload = {'filter': {'filterType': 'AND', 'filters': [ + {'filterType': 'Equals', 'attributeName': 'ScSerialNumber', + 'attributeValue': 12345}, + {'filterType': 'Equals', 'attributeName': 'instanceName', + 'attributeValue': 'Compression'}]}} + ret = self.scapi._find_data_reduction_profile('Compression') + self.assertEqual({'instanceId': '12345.0'}, ret) + mock_post.assert_called_once_with( + 'StorageCenter/ScDataReductionProfile/GetList', expected_payload) + + def test__find_datareduction_profile_no_drprofile(self, + mock_close_connection, + mock_open_connection, + mock_init): + ret = self.scapi._find_data_reduction_profile('') + self.assertIsNone(ret) + + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post') + def test__find_datareduction_profile_error(self, + mock_post, + mock_close_connection, + mock_open_connection, + mock_init): + mock_post.return_value = self.RESPONSE_400 + ret = self.scapi._find_data_reduction_profile('Compression') + self.assertIsNone(ret) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_json') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'post') + def test__find_datareduction_profile_empty_list(self, + mock_post, + mock_get_json, + mock_close_connection, + mock_open_connection, + mock_init): + mock_post.return_value = self.RESPONSE_200 + mock_get_json.return_value = [] + ret = self.scapi._find_data_reduction_profile('Compression') + self.assertIsNone(ret) + + def test__check_add_profile_payload(self, + mock_close_connection, + mock_open_connection, + mock_init): + payload = {} + profile = {'instanceId': '12345.0'} + self.scapi._check_add_profile_payload(payload, profile, + 'Profile1', 'GroupQosProfile') + self.assertEqual({'GroupQosProfile': '12345.0'}, payload) + + def test__check_add_profile_payload_no_name(self, + mock_close_connection, + mock_open_connection, + mock_init): + payload = {} + profile = {'instanceId': '12345.0'} + self.scapi._check_add_profile_payload(payload, profile, + None, 'GroupQosProfile') + self.assertEqual({}, payload) + + def test__check_add_profile_payload_no_profile(self, + mock_close_connection, + mock_open_connection, + mock_init): + payload = {} + profile = None + self.assertRaises(exception.VolumeBackendAPIException, + self.scapi._check_add_profile_payload, + payload, profile, 'Profile1', + 'VolumeQosProfile') + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'put') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_data_reduction_profile') + def test_update_datareduction_profile( + self, mock_find_datareduction_profile, mock_put, mock_prefs, + mock_close_connection, mock_open_connection, mock_init): + # Test we get and set our default + mock_find_datareduction_profile.return_value = {} + mock_prefs.return_value = { + 'allowDataReductionSelection': True, + 'dataReductionProfile': {'name': 'Default', + 'instanceId': '12345.0'}} + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + mock_put.return_value = self.RESPONSE_200 + expected = {'dataReductionProfile': '12345.0'} + res = self.scapi.update_datareduction_profile(scvolume, None) + self.assertTrue(res) + mock_put.assert_called_once_with( + 'StorageCenter/ScVolumeConfiguration/12345.101', expected, True) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'put') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_data_reduction_profile') + def test_update_datareduction_profile_error( + self, mock_find_datareduction_profile, mock_put, mock_prefs, + mock_close_connection, mock_open_connection, mock_init): + # Test we get and set our default + mock_find_datareduction_profile.return_value = {} + mock_prefs.return_value = { + 'allowDataReductionSelection': True, + 'dataReductionProfile': {'name': 'Default', + 'instanceId': '12345.0'}} + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + mock_put.return_value = self.RESPONSE_400 + expected = {'dataReductionProfile': '12345.0'} + res = self.scapi.update_datareduction_profile(scvolume, None) + self.assertFalse(res) + mock_put.assert_called_once_with( + 'StorageCenter/ScVolumeConfiguration/12345.101', expected, True) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_data_reduction_profile') + def test_update_datareduction_profile_not_found( + self, mock_find_datareduction_profile, mock_prefs, + mock_close_connection, mock_open_connection, + mock_init): + mock_find_datareduction_profile.return_value = None + mock_prefs.return_value = {'allowDataReductionSelection': True} + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + res = self.scapi.update_datareduction_profile(scvolume, 'Profile') + self.assertFalse(res) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_data_reduction_profile') + def test_update_datareduction_profile_not_allowed( + self, mock_find_datareduction_profile, mock_prefs, + mock_close_connection, mock_open_connection, + mock_init): + mock_find_datareduction_profile.return_value = None + mock_prefs.return_value = {'allowDataReductionSelection': False} + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + res = self.scapi.update_datareduction_profile(scvolume, None) + self.assertFalse(res) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_data_reduction_profile') + def test_update_datareduction_profile_prefs_not_found( + self, mock_find_datareduction_profile, mock_prefs, + mock_close_connection, mock_open_connection, + mock_init): + mock_find_datareduction_profile.return_value = None + mock_prefs.return_value = None + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + res = self.scapi.update_datareduction_profile(scvolume, None) + self.assertFalse(res) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_data_reduction_profile') + def test_update_datareduction_profile_default_not_found( + self, mock_find_datareduction_profile, mock_prefs, + mock_close_connection, mock_open_connection, + mock_init): + mock_find_datareduction_profile.return_value = None + mock_prefs.return_value = {'allowDataReductionSelection': True} + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + res = self.scapi.update_datareduction_profile(scvolume, None) + self.assertFalse(res) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'put', + return_value=RESPONSE_200) + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_data_reduction_profile') + def test_update_datareduction_profile_default( + self, mock_find_datareduction_profile, mock_put, mock_prefs, + mock_close_connection, mock_open_connection, mock_init): + # Test we get and set our default + mock_find_datareduction_profile.return_value = None + mock_prefs.return_value = { + 'allowDataReductionSelection': True, + 'dataReductionProfile': {'name': 'Default', + 'instanceId': '12345.0'}} + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + res = self.scapi.update_datareduction_profile(scvolume, None) + self.assertTrue(res) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'put') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_qos_profile') + def test_update_qos_profile( + self, mock_find_qos_profile, mock_put, mock_prefs, + mock_close_connection, mock_open_connection, mock_init): + # Test we get and set our default + mock_find_qos_profile.return_value = {} + mock_prefs.return_value = { + 'allowQosProfileSelection': True, + 'volumeQosProfile': {'name': 'Default', + 'instanceId': '12345.0'}} + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + mock_put.return_value = self.RESPONSE_200 + expected = {'volumeQosProfile': '12345.0'} + res = self.scapi.update_qos_profile(scvolume, None) + self.assertTrue(res) + mock_put.assert_called_once_with( + 'StorageCenter/ScVolumeConfiguration/12345.101', expected, True) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'put') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_data_reduction_profile') + def test_update_qos_profile_error( + self, mock_find_qos_profile, mock_put, mock_prefs, + mock_close_connection, mock_open_connection, mock_init): + # Test we get and set our default + mock_find_qos_profile.return_value = {} + mock_prefs.return_value = { + 'allowQosProfileSelection': True, + 'volumeQosProfile': {'name': 'Default', + 'instanceId': '12345.0'}} + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + mock_put.return_value = self.RESPONSE_400 + expected = {'volumeQosProfile': '12345.0'} + res = self.scapi.update_qos_profile(scvolume, None) + self.assertFalse(res) + mock_put.assert_called_once_with( + 'StorageCenter/ScVolumeConfiguration/12345.101', expected, True) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_qos_profile') + def test_update_qos_profile_not_found( + self, mock_find_qos_profile, mock_prefs, + mock_close_connection, mock_open_connection, + mock_init): + mock_find_qos_profile.return_value = None + mock_prefs.return_value = {'allowQosProfileSelection': True} + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + res = self.scapi.update_qos_profile(scvolume, 'Profile') + self.assertFalse(res) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_qos_profile') + def test_update_qos_profile_not_allowed( + self, mock_find_qos_profile, mock_prefs, + mock_close_connection, mock_open_connection, + mock_init): + mock_find_qos_profile.return_value = None + mock_prefs.return_value = {'allowQosProfileSelection': False} + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + res = self.scapi.update_qos_profile(scvolume, None) + self.assertFalse(res) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_qos_profile') + def test_update_qos_profile_prefs_not_found( + self, mock_find_qos_profile, mock_prefs, + mock_close_connection, mock_open_connection, + mock_init): + mock_find_qos_profile.return_value = None + mock_prefs.return_value = None + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + res = self.scapi.update_qos_profile(scvolume, None) + self.assertFalse(res) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_qos_profile') + def test_update_qos_profile_default_not_found( + self, mock_find_qos_profile, mock_prefs, + mock_close_connection, mock_open_connection, + mock_init): + mock_find_qos_profile.return_value = None + mock_prefs.return_value = {'allowQosProfileSelection': True} + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + res = self.scapi.update_qos_profile(scvolume, None) + self.assertFalse(res) + + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_get_user_preferences') + @mock.patch.object(dell_storagecenter_api.HttpClient, + 'put') + @mock.patch.object(dell_storagecenter_api.StorageCenterApi, + '_find_qos_profile') + def test_update_qos_profile_default( + self, mock_find_qos_profile, mock_put, mock_prefs, + mock_close_connection, mock_open_connection, mock_init): + # Test we get and set our default + mock_find_qos_profile.return_value = None + mock_prefs.return_value = { + 'allowQosProfileSelection': True, + 'volumeQosProfile': {'name': 'Default', + 'instanceId': '12345.0'}} + mock_put.return_value = self.RESPONSE_200 + scvolume = {'name': fake.VOLUME_ID, 'instanceId': '12345.101'} + res = self.scapi.update_qos_profile(scvolume, None) + self.assertTrue(res) + class DellSCSanAPIConnectionTestCase(test.TestCase): diff --git a/cinder/volume/drivers/dell/dell_storagecenter_api.py b/cinder/volume/drivers/dell/dell_storagecenter_api.py index 3f71a5ef7c9..f30f45d2e34 100644 --- a/cinder/volume/drivers/dell/dell_storagecenter_api.py +++ b/cinder/volume/drivers/dell/dell_storagecenter_api.py @@ -1000,8 +1000,17 @@ class StorageCenterApi(object): return False return True + def _check_add_profile_payload(self, payload, profile, name, type): + if name: + if profile is None: + msg = _('Profile %s not found.') % name + raise exception.VolumeBackendAPIException(data=msg) + else: + payload[type] = self._get_id(profile) + def create_volume(self, name, size, storage_profile=None, - replay_profile_string=None): + replay_profile_string=None, volume_qos=None, + group_qos=None, datareductionprofile=None): """Creates a new volume on the Storage Center. It will create it in a folder called self.vfname. If self.vfname @@ -1014,15 +1023,22 @@ class StorageCenterApi(object): :param storage_profile: Optional storage profile to set for the volume. :param replay_profile_string: Optional replay profile to set for the volume. + :param volume_qos: Volume QOS profile name. + :param group_qos: Group QOS profile name. + :param datareductionprofile: Data reduction profile name + :returns: Dell Volume object or None. """ - LOG.debug('create_volume: %(name)s %(ssn)s %(folder)s %(profile)s', + LOG.debug('create_volume: %(name)s %(ssn)s %(folder)s %(profile)s ' + '%(vqos)r %(gqos)r %(dup)r', {'name': name, 'ssn': self.ssn, 'folder': self.vfname, 'profile': storage_profile, - 'replay': replay_profile_string - }) + 'replay': replay_profile_string, + 'vqos': volume_qos, + 'gqos': group_qos, + 'dup': datareductionprofile}) # Find our folder folder = self._find_volume_folder(True) @@ -1031,12 +1047,6 @@ class StorageCenterApi(object): if folder is None: LOG.warning(_LW('Unable to create folder %s'), self.vfname) - # See if we need a storage profile - profile = self._find_storage_profile(storage_profile) - if storage_profile and profile is None: - msg = _('Storage Profile %s not found.') % storage_profile - raise exception.VolumeBackendAPIException(data=msg) - # Find our replay_profiles. addids, removeids = self._find_replay_profiles(replay_profile_string) @@ -1051,11 +1061,27 @@ class StorageCenterApi(object): payload['StorageCenter'] = self.ssn if folder is not None: payload['VolumeFolder'] = self._get_id(folder) - if profile: - payload['StorageProfile'] = self._get_id(profile) + # Add our storage profile. + self._check_add_profile_payload( + payload, self._find_storage_profile(storage_profile), + storage_profile, 'StorageProfile') + # Add our Volume QOS Profile. + self._check_add_profile_payload( + payload, self._find_qos_profile(volume_qos), volume_qos, + 'VolumeQosProfile') + # Add our Group QOS Profile. + self._check_add_profile_payload( + payload, self._find_qos_profile(group_qos, True), group_qos, + 'GroupQosProfile') + # Add our Data Reduction Proflie. + self._check_add_profile_payload( + payload, self._find_data_reduction_profile(datareductionprofile), + datareductionprofile, 'DataReductionProfile') + # This is a new volume so there is nothing to remove. if addids: payload['ReplayProfileList'] = addids + r = self.client.post('StorageCenter/ScVolume', payload, True) if self._check_result(r): # Our volume should be in the return. @@ -2063,12 +2089,16 @@ class StorageCenterApi(object): # If we couldn't find it we call that a success. return ret - def create_view_volume(self, volname, screplay, replay_profile_string): + def create_view_volume(self, volname, screplay, replay_profile_string, + volume_qos, group_qos, dr_profile): """Creates a new volume named volname from the screplay. :param volname: Name of new volume. This is the cinder volume ID. :param screplay: Dell replay object from which to make a new volume. :param replay_profile_string: Profiles to be applied to the volume + :param volume_qos: Volume QOS Profile to use. + :param group_qos: Group QOS Profile to use. + :param dr_profile: Data reduction profile to use. :returns: Dell volume object or None. """ folder = self._find_volume_folder(True) @@ -2084,19 +2114,34 @@ class StorageCenterApi(object): payload['VolumeFolder'] = self._get_id(folder) if addids: payload['ReplayProfileList'] = addids + # Add our Volume QOS Profile. + self._check_add_profile_payload( + payload, self._find_qos_profile(volume_qos), volume_qos, + 'VolumeQosProfile') + # Add our Group QOS Profile. + self._check_add_profile_payload( + payload, self._find_qos_profile(group_qos, True), group_qos, + 'GroupQosProfile') r = self.client.post('StorageCenter/ScReplay/%s/CreateView' % self._get_id(screplay), payload, True) volume = None if self._check_result(r): volume = self._first_result(r) + # If we have a dr_profile to apply we should do so now. + if dr_profile and not self.update_datareduction_profile(volume, + dr_profile): + LOG.error(_LE('Unable to apply %s to volume.'), dr_profile) + volume = None + if volume is None: LOG.error(_LE('Unable to create volume %s from replay'), volname) return volume - def create_cloned_volume(self, volumename, scvolume, replay_profile_list): + def create_cloned_volume(self, volumename, scvolume, replay_profile_list, + volume_qos, group_qos, dr_profile): """Creates a volume named volumename from a copy of scvolume. This is done by creating a replay and then a view volume from @@ -2108,12 +2153,16 @@ class StorageCenterApi(object): :param volumename: Name of new volume. This is the cinder volume ID. :param scvolume: Dell volume object. :param replay_profile_list: List of snapshot profiles. + :param volume_qos: Volume QOS Profile to use. + :param group_qos: Group QOS Profile to use. + :param dr_profile: Data reduction profile to use. :returns: The new volume's Dell volume object. """ replay = self.create_replay(scvolume, 'Cinder Clone Replay', 60) if replay is not None: return self.create_view_volume(volumename, replay, - replay_profile_list) + replay_profile_list, volume_qos, + group_qos, dr_profile) LOG.error(_LE('Error: unable to snap replay')) return None @@ -2162,6 +2211,48 @@ class StorageCenterApi(object): 'name': name}) return False + def _update_profile(self, scvolume, profile, profilename, + profiletype, restname, allowprefname, + continuewithoutdefault=False): + prefs = self._get_user_preferences() + if not prefs: + return False + + if not prefs.get(allowprefname): + LOG.error(_LE('User does not have permission to change ' + '%s selection.'), profiletype) + return False + + if profilename: + if not profile: + LOG.error(_LE('%(ptype)s %(pname)s was not found.'), + {'ptype': profiletype, + 'pname': profilename}) + return False + else: + # Going from specific profile to the user default + profile = prefs.get(restname) + if not profile and not continuewithoutdefault: + LOG.error(_LE('Default %s was not found.'), profiletype) + return False + + LOG.info(_LI('Switching volume %(vol)s to profile %(prof)s.'), + {'vol': scvolume['name'], + 'prof': profile.get('name')}) + payload = {} + payload[restname] = self._get_id(profile) if profile else None + r = self.client.put('StorageCenter/ScVolumeConfiguration/%s' + % self._get_id(scvolume), payload, True) + if self._check_result(r): + return True + + LOG.error(_LE('Error changing %(ptype)s for volume ' + '%(original)s to %(name)s'), + {'ptype': profiletype, + 'original': scvolume['name'], + 'name': profilename}) + return False + def update_storage_profile(self, scvolume, storage_profile): """Update a volume's Storage Profile. @@ -2173,43 +2264,45 @@ class StorageCenterApi(object): :param storage_profile: The requested Storage Profile name. :returns: True if successful, False otherwise. """ - prefs = self._get_user_preferences() - if not prefs: - return False - - if not prefs.get('allowStorageProfileSelection'): - LOG.error(_LE('User does not have permission to change ' - 'Storage Profile selection.')) - return False - profile = self._find_storage_profile(storage_profile) - if storage_profile: - if not profile: - LOG.error(_LE('Storage Profile %s was not found.'), - storage_profile) - return False - else: - # Going from specific profile to the user default - profile = prefs.get('storageProfile') - if not profile: - LOG.error(_LE('Default Storage Profile was not found.')) - return False + return self._update_profile(scvolume, profile, storage_profile, + 'Storage Profile', 'storageProfile', + 'allowStorageProfileSelection') - LOG.info(_LI('Switching volume %(vol)s to profile %(prof)s.'), - {'vol': scvolume['name'], - 'prof': profile.get('name')}) - payload = {} - payload['StorageProfile'] = self._get_id(profile) - r = self.client.put('StorageCenter/ScVolumeConfiguration/%s' - % self._get_id(scvolume), payload, True) - if self._check_result(r): - return True + def update_datareduction_profile(self, scvolume, dr_profile): + """Update a volume's Data Reduction Profile - LOG.error(_LE('Error changing Storage Profile for volume ' - '%(original)s to %(name)s'), - {'original': scvolume['name'], - 'name': storage_profile}) - return False + Changes the volume setting to use a different data reduction profile. + If dr_profile is None, will reset to the default profile for the + cinder user account. + + :param scvolume: The Storage Center volume to be updated. + :param dr_profile: The requested data reduction profile name. + :returns: True if successful, False otherwise. + """ + profile = self._find_data_reduction_profile(dr_profile) + return self._update_profile(scvolume, profile, dr_profile, + 'Data Reduction Profile', + 'dataReductionProfile', + 'allowDataReductionSelection') + + def update_qos_profile(self, scvolume, qosprofile, grouptype=False): + """Update a volume's QOS profile + + Changes the volume setting to use a different QOS Profile. + + :param scvolume: The Storage Center volume to be updated. + :param qosprofile: The requested QOS profile name. + :param grouptype: Is this a group QOS profile? + :returns: True if successful, False otherwise. + """ + profiletype = 'groupQosProfile' if grouptype else 'volumeQosProfile' + + profile = self._find_qos_profile(qosprofile, grouptype) + return self._update_profile(scvolume, profile, qosprofile, + 'Qos Profile', profiletype, + 'allowQosProfileSelection', + grouptype) def _get_user_preferences(self): """Gets the preferences and defaults for this user. @@ -3277,3 +3370,33 @@ class StorageCenterApi(object): if self._check_result(r): return True return False + + def _find_qos_profile(self, qosprofile, grouptype=False): + if qosprofile: + pf = self._get_payload_filter() + pf.append('ScSerialNumber', self.ssn) + pf.append('Name', qosprofile) + if grouptype: + pf.append('profileType', 'GroupQosProfile') + else: + pf.append('profileType', 'VolumeQosProfile') + r = self.client.post('StorageCenter/ScQosProfile/GetList', + pf.payload) + if self._check_result(r): + qosprofiles = self._get_json(r) + if len(qosprofiles): + return qosprofiles[0] + return None + + def _find_data_reduction_profile(self, drprofile): + if drprofile: + pf = self._get_payload_filter() + pf.append('ScSerialNumber', self.ssn) + pf.append('instanceName', drprofile) + r = self.client.post( + 'StorageCenter/ScDataReductionProfile/GetList', pf.payload) + if self._check_result(r): + drps = self._get_json(r) + if len(drps): + return drps[0] + return None diff --git a/cinder/volume/drivers/dell/dell_storagecenter_common.py b/cinder/volume/drivers/dell/dell_storagecenter_common.py index 38eea147890..363db399b79 100644 --- a/cinder/volume/drivers/dell/dell_storagecenter_common.py +++ b/cinder/volume/drivers/dell/dell_storagecenter_common.py @@ -287,21 +287,21 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD, # Look for our volume volume_size = volume.get('size') - # See if we have any extra specs. - specs = self._get_volume_extra_specs(volume) - storage_profile = specs.get('storagetype:storageprofile') - replay_profile_string = specs.get('storagetype:replayprofiles') - LOG.debug('Creating volume %(name)s of size %(size)s', {'name': volume_name, 'size': volume_size}) scvolume = None with self._client.open_connection() as api: try: - scvolume = api.create_volume(volume_name, - volume_size, - storage_profile, - replay_profile_string) + # Get our extra specs. + specs = self._get_volume_extra_specs(volume) + scvolume = api.create_volume( + volume_name, volume_size, + specs.get('storagetype:storageprofile'), + specs.get('storagetype:replayprofiles'), + specs.get('storagetype:volumeqos'), + specs.get('storagetype:groupqos'), + specs.get('storagetype:datareductionprofile')) if scvolume is None: raise exception.VolumeBackendAPIException( message=_('Unable to create volume %s') % @@ -494,10 +494,12 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD, if replay is not None: # See if we have any extra specs. specs = self._get_volume_extra_specs(volume) - replay_profile_string = specs.get( - 'storagetype:replayprofiles') scvolume = api.create_view_volume( - volume_name, replay, replay_profile_string) + volume_name, replay, + specs.get('storagetype:replayprofiles'), + specs.get('storagetype:volumeqos'), + specs.get('storagetype:groupqos'), + specs.get('storagetype:datareductionprofile')) # Extend Volume if scvolume and (volume['size'] > @@ -555,13 +557,15 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD, try: srcvol = api.find_volume(src_volume_name, src_provider_id) if srcvol is not None: - # See if we have any extra specs. + # Get our specs. specs = self._get_volume_extra_specs(volume) - replay_profile_string = specs.get( - 'storagetype:replayprofiles') # Create our volume scvolume = api.create_cloned_volume( - volume_name, srcvol, replay_profile_string) + volume_name, srcvol, + specs.get('storagetype:replayprofiles'), + specs.get('storagetype:volumeqos'), + specs.get('storagetype:groupqos'), + specs.get('storagetype:datareductionprofile')) # Extend Volume if scvolume and volume['size'] > src_vref['size']: @@ -1094,6 +1098,40 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD, LOG.error(_LE('Failed to update replay profiles')) return False + # Volume QOS profiles. + current, requested = ( + self._get_retype_spec(diff, volume_name, + 'Volume QOS Profile', + 'storagetype:volumeqos')) + if current != requested: + if not api.update_qos_profile(scvolume, requested): + LOG.error(_LE('Failed to update volume ' + 'qos profile')) + + # Group QOS profiles. + current, requested = ( + self._get_retype_spec(diff, volume_name, + 'Group QOS Profile', + 'storagetype:groupqos')) + if current != requested: + if not api.update_qos_profile(scvolume, requested, + True): + LOG.error(_LE('Failed to update group ' + 'qos profile')) + return False + + # Data reduction profiles. + current, requested = ( + self._get_retype_spec( + diff, volume_name, 'Data Reduction Profile', + 'storagetype:datareductionprofile')) + if current != requested: + if not api.update_datareduction_profile(scvolume, + requested): + LOG.error(_LE('Failed to update data reduction ' + 'profile')) + return False + # Replication_enabled. current, requested = ( self._get_retype_spec(diff, @@ -1124,8 +1162,6 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD, 'replication:activereplay setting')) return False - # TODO(tswanson): replaytype once it actually works. - except exception.VolumeBackendAPIException: # We do nothing with this. We simply return failure. return False @@ -1187,7 +1223,7 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD, return qosnode def _parse_extraspecs(self, volume): - # Digest our extra specs. + # Digest our extra specs for replication. extraspecs = {} specs = self._get_volume_extra_specs(volume) if specs.get('replication_type') == ' sync': diff --git a/releasenotes/notes/Dell-SC-New-Extra-Specs-1de0d3f1ebc62881.yaml b/releasenotes/notes/Dell-SC-New-Extra-Specs-1de0d3f1ebc62881.yaml new file mode 100644 index 00000000000..37d904af5cf --- /dev/null +++ b/releasenotes/notes/Dell-SC-New-Extra-Specs-1de0d3f1ebc62881.yaml @@ -0,0 +1,8 @@ +--- +features: + - Dell SC - Compression and Dedupe support + added for Storage Centers that support the + options. + - Dell SC - Volume and Group QOS support + added for Storage Centers that support and + have enabled the option.