diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/client/test_client_base.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/client/test_client_base.py index 8d18e07cb4c..9f61be8baa4 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/client/test_client_base.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/client/test_client_base.py @@ -16,6 +16,7 @@ import uuid +import ddt from lxml import etree import mock import six @@ -38,6 +39,7 @@ CONNECTION_INFO = {'hostname': 'hostname', 'api_trace_pattern': 'fake_regex'} +@ddt.ddt class NetAppBaseClientTestCase(test.TestCase): def setUp(self): @@ -87,8 +89,27 @@ class NetAppBaseClientTestCase(test.TestCase): self.assertIsNone(self.client.check_is_naelement(element)) self.assertRaises(ValueError, self.client.check_is_naelement, None) - def test_create_lun(self): + @ddt.data({'ontap_version': '9.4', 'space_reservation': 'true'}, + {'ontap_version': '9.4', 'space_reservation': 'false'}, + {'ontap_version': '9.6', 'space_reservation': 'true'}, + {'ontap_version': '9.6', 'space_reservation': 'false'}) + @ddt.unpack + def test_create_lun(self, ontap_version, space_reservation): expected_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun) + self.fake_metadata['SpaceReserved'] = space_reservation + expected_space_reservation = space_reservation + self.mock_object(self.client, 'get_ontap_version', + return_value=ontap_version) + mock_resize_lun = self.mock_object( + client_base.Client, 'do_direct_resize') + mock_set_space_reservation = self.mock_object( + client_base.Client, 'set_lun_space_reservation') + initial_size = self.fake_size + + if ontap_version < '9.5': + initial_size = fake.MAX_SIZE_FOR_A_LUN + expected_space_reservation = 'false' + with mock.patch.object(netapp_api.NaElement, 'create_node_with_children', ) as mock_create_node: @@ -97,19 +118,48 @@ class NetAppBaseClientTestCase(test.TestCase): self.fake_size, self.fake_metadata) - mock_create_node.assert_called_once_with( + mock_create_node.assert_called_with( 'lun-create-by-size', **{'path': expected_path, - 'size': self.fake_size, + 'size': initial_size, 'ostype': self.fake_metadata['OsType'], 'space-reservation-enabled': - self.fake_metadata['SpaceReserved']}) - self.connection.invoke_successfully.assert_called_once_with( + expected_space_reservation}) + self.connection.invoke_successfully.assert_called_with( mock.ANY, True) - def test_create_lun_exact_size(self): + if ontap_version < '9.5': + mock_resize_lun.assert_called_once_with( + expected_path, self.fake_size) + + if ontap_version < '9.5' and space_reservation == 'true': + mock_set_space_reservation.assert_called_once_with( + expected_path, True) + else: + mock_set_space_reservation.assert_not_called() + + @ddt.data({'ontap_version': '9.4', 'space_reservation': 'true'}, + {'ontap_version': '9.4', 'space_reservation': 'false'}, + {'ontap_version': '9.6', 'space_reservation': 'true'}, + {'ontap_version': '9.6', 'space_reservation': 'false'}) + @ddt.unpack + def test_create_lun_exact_size(self, ontap_version, space_reservation): expected_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun) self.connection.get_api_version.return_value = (1, 110) + self.fake_metadata['SpaceReserved'] = space_reservation + expected_space_reservation = self.fake_metadata['SpaceReserved'] + self.mock_object(self.client, 'get_ontap_version', + return_value=ontap_version) + mock_resize_lun = self.mock_object( + client_base.Client, 'do_direct_resize') + mock_set_space_reservation = self.mock_object( + client_base.Client, 'set_lun_space_reservation') + initial_size = self.fake_size + + if ontap_version < '9.5': + initial_size = fake.MAX_SIZE_FOR_A_LUN + expected_space_reservation = 'false' + with mock.patch.object(netapp_api.NaElement, 'create_node_with_children', ) as mock_create_node: @@ -118,22 +168,52 @@ class NetAppBaseClientTestCase(test.TestCase): self.fake_size, self.fake_metadata) - mock_create_node.assert_called_once_with( + mock_create_node.assert_called_with( 'lun-create-by-size', **{'path': expected_path, - 'size': self.fake_size, + 'size': initial_size, 'ostype': self.fake_metadata['OsType'], 'use-exact-size': 'true', 'space-reservation-enabled': - self.fake_metadata['SpaceReserved']}) - self.connection.invoke_successfully.assert_called_once_with( + expected_space_reservation}) + self.connection.invoke_successfully.assert_called_with( mock.ANY, True) - def test_create_lun_with_qos_policy_group_name(self): + if ontap_version < '9.5': + mock_resize_lun.assert_called_once_with( + expected_path, self.fake_size) + + if ontap_version < '9.5' and space_reservation == 'true': + mock_set_space_reservation.assert_called_once_with( + expected_path, True) + else: + mock_set_space_reservation.assert_not_called() + + @ddt.data({'ontap_version': '9.4', 'space_reservation': 'true'}, + {'ontap_version': '9.4', 'space_reservation': 'false'}, + {'ontap_version': '9.6', 'space_reservation': 'true'}, + {'ontap_version': '9.6', 'space_reservation': 'false'}) + @ddt.unpack + def test_create_lun_with_qos_policy_group_name( + self, ontap_version, space_reservation): expected_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun) expected_qos_group_name = 'qos_1' mock_request = mock.Mock() + self.fake_metadata['SpaceReserved'] = space_reservation + expected_space_reservation = self.fake_metadata['SpaceReserved'] + self.mock_object(self.client, 'get_ontap_version', + return_value=ontap_version) + mock_resize_lun = self.mock_object( + client_base.Client, 'do_direct_resize') + mock_set_space_reservation = self.mock_object( + client_base.Client, 'set_lun_space_reservation') + initial_size = self.fake_size + + if ontap_version < '9.5': + initial_size = fake.MAX_SIZE_FOR_A_LUN + expected_space_reservation = 'false' + with mock.patch.object(netapp_api.NaElement, 'create_node_with_children', return_value=mock_request @@ -145,20 +225,65 @@ class NetAppBaseClientTestCase(test.TestCase): self.fake_metadata, qos_policy_group_name=expected_qos_group_name) - mock_create_node.assert_called_once_with( + mock_create_node.assert_called_with( 'lun-create-by-size', - **{'path': expected_path, 'size': self.fake_size, + **{'path': expected_path, 'size': initial_size, 'ostype': self.fake_metadata['OsType'], 'space-reservation-enabled': - self.fake_metadata['SpaceReserved']}) - mock_request.add_new_child.assert_called_once_with( + expected_space_reservation}) + mock_request.add_new_child.assert_called_with( 'qos-policy-group', expected_qos_group_name) + self.connection.invoke_successfully.assert_called_with( + mock.ANY, True) + + if ontap_version < '9.5': + mock_resize_lun.assert_called_once_with( + expected_path, self.fake_size) + + if ontap_version < '9.5' and space_reservation == 'true': + mock_set_space_reservation.assert_called_once_with( + expected_path, True) + else: + mock_set_space_reservation.assert_not_called() + + def test_get_ontap_version(self): + version_response = netapp_api.NaElement( + fake.SYSTEM_GET_VERSION_RESPONSE) + self.connection.invoke_successfully.return_value = version_response + + result = self.client.get_ontap_version(cached=False) + + self.assertEqual(('9.6'), result) + + def test_get_ontap_version_cached(self): + self.connection.get_ontap_version.return_value = '9.6' + + result = self.client.get_ontap_version() + + self.connection.get_ontap_version.assert_called_once_with() + self.assertEqual(('9.6'), result) + + def test_set_lun_space_reservation(self): + path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun) + + with mock.patch.object(netapp_api.NaElement, + 'create_node_with_children', + ) as mock_set_space_reservation: + self.client.set_lun_space_reservation(path, True) + + mock_set_space_reservation.assert_called_once_with( + 'lun-set-space-reservation-info', + **{'path': path, + 'enable': 'True'}) self.connection.invoke_successfully.assert_called_once_with( mock.ANY, True) - def test_create_lun_raises_on_failure(self): + @ddt.data('9.4', '9.6') + def test_create_lun_raises_on_failure(self, ontap_version): self.connection.invoke_successfully = mock.Mock( side_effect=netapp_api.NaApiError) + self.mock_object(self.client, 'get_ontap_version', + return_value=ontap_version) self.assertRaises(netapp_api.NaApiError, self.client.create_lun, diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/client/test_client_cmode.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/client/test_client_cmode.py index 2e9169fa286..7bc3e422066 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/client/test_client_cmode.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/client/test_client_cmode.py @@ -53,6 +53,8 @@ class NetAppCmodeClientTestCase(test.TestCase): super(NetAppCmodeClientTestCase, self).setUp() self.mock_object(client_cmode.Client, '_init_ssh_client') + self.mock_object(client_cmode.Client, 'get_ontap_version', + return_value='9.6') with mock.patch.object(client_cmode.Client, 'get_ontapi_version', return_value=(1, 20)): diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/fakes.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/fakes.py index e9ece0633c5..3f26ffc71eb 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/fakes.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/fakes.py @@ -421,6 +421,21 @@ CG_VOLUME_SNAPSHOT = { 'volume_id': CG_VOLUME_ID, } +SYSTEM_GET_VERSION_RESPONSE = etree.XML(""" + + 1395426307 + true + NetApp Release 9.6P2: Fri Jul 19 06:06:59 UTC 2019 + + + 9 + 6 + 0 + + + +""") + VG_VOLUME_NAME = 'fake_vg_volume' VG_GROUP_NAME = 'fake_volume_group' @@ -435,6 +450,8 @@ VOLUME_GROUP_ID = 'fake_vg_id' VG_SNAPSHOT_ID = 'fake_vg_snapshot_id' VG_SNAPSHOT_NAME = 'snapshot-' + VG_SNAPSHOT_ID VG_VOLUME_SNAPSHOT_ID = 'fake_vg_volume_snapshot_id' +MIN_SIZE_FOR_A_LUN = '4194304' +MAX_SIZE_FOR_A_LUN = '17555678822400' VG_LUN_METADATA = { 'OsType': None, diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_base.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_base.py index 72f4d355b54..9c883058acf 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_base.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_base.py @@ -1205,7 +1205,8 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): mock_extend_volume.assert_called_once_with( fake.VOLUME, new_size, fake.QOS_POLICY_GROUP_NAME) - def test__extend_volume_direct(self): + @ddt.data('9.4', '9.6') + def test__extend_volume_direct(self, ontap_version): current_size = fake.LUN_SIZE current_size_bytes = current_size * units.Gi @@ -1214,6 +1215,9 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): max_size = fake.LUN_SIZE * 10 max_size_bytes = max_size * units.Gi + mock_get_ontap_version = self.mock_object( + self.library.zapi_client, 'get_ontap_version', + return_value=ontap_version) fake_lun = block_base.NetAppLun(fake.LUN_HANDLE, fake.LUN_ID, current_size_bytes, @@ -1232,16 +1236,23 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): self.library._extend_volume(fake.VOLUME, new_size, 'fake_qos_policy') + mock_get_ontap_version.assert_called_once_with(cached=True) mock_get_lun_from_table.assert_called_once_with(fake.VOLUME['name']) - mock_get_lun_geometry.assert_called_once_with( - fake.LUN_METADATA['Path']) + + if ontap_version < '9.5': + mock_get_lun_geometry.assert_called_once_with( + fake.LUN_METADATA['Path']) + else: + mock_get_lun_geometry.assert_not_called() + mock_do_direct_resize.assert_called_once_with( fake.LUN_METADATA['Path'], six.text_type(new_size_bytes)) self.assertFalse(mock_do_sub_clone_resize.called) self.assertEqual(six.text_type(new_size_bytes), self.library.lun_table[fake.VOLUME['name']].size) - def test__extend_attached_volume_direct(self): + @ddt.data('9.4', '9.6') + def test__extend_attached_volume_direct(self, ontap_version): current_size = fake.LUN_SIZE current_size_bytes = current_size * units.Gi @@ -1253,6 +1264,9 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): volume_copy['size'] = new_size volume_copy['attach_status'] = fake.ATTACHED + mock_get_ontap_version = self.mock_object( + self.library.zapi_client, 'get_ontap_version', + return_value=ontap_version) fake_lun = block_base.NetAppLun(fake.LUN_HANDLE, fake.LUN_ID, current_size_bytes, @@ -1267,20 +1281,27 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): 'do_direct_resize') mock_do_sub_clone_resize = self.mock_object(self.library, '_do_sub_clone_resize') - self.library.lun_table = {volume_copy['name']: fake_lun} + self.library.lun_table = {volume_copy['name']: fake_lun} self.library._extend_volume(volume_copy, new_size, 'fake_qos_policy') mock_get_lun_from_table.assert_called_once_with(volume_copy['name']) - mock_get_lun_geometry.assert_called_once_with( - fake.LUN_METADATA['Path']) + mock_get_ontap_version.assert_called_once_with(cached=True) + + if ontap_version < '9.5': + mock_get_lun_geometry.assert_called_once_with( + fake.LUN_METADATA['Path']) + else: + mock_get_lun_geometry.assert_not_called() + mock_do_direct_resize.assert_called_once_with( fake.LUN_METADATA['Path'], six.text_type(new_size_bytes)) self.assertFalse(mock_do_sub_clone_resize.called) self.assertEqual(six.text_type(new_size_bytes), self.library.lun_table[volume_copy['name']].size) - def test__extend_volume_clone(self): + @ddt.data('9.4', '9.6') + def test__extend_volume_clone(self, ontap_version): current_size = fake.LUN_SIZE current_size_bytes = current_size * units.Gi @@ -1289,6 +1310,9 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): max_size = fake.LUN_SIZE * 10 max_size_bytes = max_size * units.Gi + mock_get_ontap_version = self.mock_object( + self.library.zapi_client, 'get_ontap_version', + return_value=ontap_version) fake_lun = block_base.NetAppLun(fake.LUN_HANDLE, fake.LUN_ID, current_size_bytes, @@ -1307,29 +1331,43 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): self.library._extend_volume(fake.VOLUME, new_size, 'fake_qos_policy') + mock_get_ontap_version.assert_called_once_with(cached=True) mock_get_lun_from_table.assert_called_once_with(fake.VOLUME['name']) - mock_get_lun_geometry.assert_called_once_with( - fake.LUN_METADATA['Path']) - self.assertFalse(mock_do_direct_resize.called) - mock_do_sub_clone_resize.assert_called_once_with( - fake.LUN_METADATA['Path'], six.text_type(new_size_bytes), - qos_policy_group_name='fake_qos_policy') + + if ontap_version < '9.5': + self.assertFalse(mock_do_direct_resize.called) + mock_get_lun_geometry.assert_called_once_with( + fake.LUN_METADATA['Path']) + mock_do_sub_clone_resize.assert_called_once_with( + fake.LUN_METADATA['Path'], six.text_type(new_size_bytes), + qos_policy_group_name='fake_qos_policy') + else: + mock_get_lun_geometry.assert_not_called() + mock_do_sub_clone_resize.assert_not_called() + mock_do_direct_resize.assert_called_once_with( + fake.LUN_METADATA['Path'], six.text_type(new_size_bytes)) + self.assertEqual(six.text_type(new_size_bytes), self.library.lun_table[fake.VOLUME['name']].size) - def test__extend_attached_volume_clone_error(self): + @ddt.data('9.4', '9.6') + def test__extend_attached_volume_clone_error(self, ontap_version): current_size = fake.LUN_SIZE current_size_bytes = current_size * units.Gi new_size = fake.LUN_SIZE * 20 + new_size_bytes = new_size * units.Gi max_size = fake.LUN_SIZE * 10 max_size_bytes = max_size * units.Gi volume_copy = copy.copy(fake.VOLUME) volume_copy['attach_status'] = fake.ATTACHED + mock_get_ontap_version = self.mock_object( + self.library.zapi_client, 'get_ontap_version', + return_value=ontap_version) fake_lun = block_base.NetAppLun(fake.LUN_HANDLE, fake.LUN_ID, - current_size_bytes, + six.text_type(current_size_bytes), fake.LUN_METADATA) mock_get_lun_from_table = self.mock_object( self.library, '_get_lun_from_table', return_value=fake_lun) @@ -1343,21 +1381,35 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): '_do_sub_clone_resize') self.library.lun_table = {volume_copy['name']: fake_lun} - self.assertRaises(exception.VolumeBackendAPIException, - self.library._extend_volume, - volume_copy, - new_size, - fake.QOS_POLICY_GROUP_NAME) + # (throne82) This error occurs only with versions older than 9.5 + if ontap_version < '9.5': + self.assertRaises(exception.VolumeBackendAPIException, + self.library._extend_volume, + volume_copy, + new_size, + fake.QOS_POLICY_GROUP_NAME) + self.assertFalse(mock_do_direct_resize.called) + self.assertFalse(mock_do_sub_clone_resize.called) + mock_get_lun_geometry.assert_called_once_with( + fake.LUN_METADATA['Path']) + self.assertEqual(six.text_type(current_size_bytes), + self.library.lun_table[volume_copy['name']].size) + else: + self.library._extend_volume(volume_copy, + new_size, fake.QOS_POLICY_GROUP_NAME) + mock_do_direct_resize.assert_called_once_with( + fake.LUN_METADATA['Path'], six.text_type(new_size_bytes)) + mock_do_sub_clone_resize.assert_not_called() + mock_get_lun_geometry.assert_not_called() + self.assertEqual(six.text_type(new_size_bytes), + self.library.lun_table[volume_copy['name']].size) - mock_get_lun_from_table.assert_called_once_with(volume_copy['name']) - mock_get_lun_geometry.assert_called_once_with( - fake.LUN_METADATA['Path']) - self.assertFalse(mock_do_direct_resize.called) - self.assertFalse(mock_do_sub_clone_resize.called) - self.assertEqual(current_size_bytes, - self.library.lun_table[volume_copy['name']].size) + mock_get_ontap_version.assert_called_once_with(cached=True) + mock_get_lun_from_table.assert_called_once_with( + volume_copy['name']) - def test__extend_volume_no_change(self): + @ddt.data('9.4', '9.6') + def test__extend_volume_no_change(self, ontap_version): current_size = fake.LUN_SIZE current_size_bytes = current_size * units.Gi @@ -1367,6 +1419,8 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): volume_copy = copy.copy(fake.VOLUME) volume_copy['size'] = new_size + mock_get_ontap_version = self.mock_object( + self.library.zapi_client, 'get_ontap_version') fake_lun = block_base.NetAppLun(fake.LUN_HANDLE, fake.LUN_ID, current_size_bytes, @@ -1389,6 +1443,7 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): self.assertFalse(mock_get_lun_geometry.called) self.assertFalse(mock_do_direct_resize.called) self.assertFalse(mock_do_sub_clone_resize.called) + self.assertFalse(mock_get_ontap_version.called) def test_do_sub_clone_resize(self): diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_cmode.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_cmode.py index 8d5832efe3c..d6f4de5f1c0 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_cmode.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_cmode.py @@ -92,6 +92,8 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase): 'check_api_permissions') @mock.patch.object(na_utils, 'check_flags') @mock.patch.object(block_base.NetAppBlockStorageLibrary, 'do_setup') + @mock.patch.object(client_base.Client, 'get_ontap_version', + mock.MagicMock(return_value='9.6')) def test_do_setup(self, super_do_setup, mock_check_flags, mock_check_api_permissions, mock_cluster_user_supported): self.mock_object(client_base.Client, '_init_ssh_client') @@ -419,7 +421,7 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase): 'netapp_raid_type': 'raid_dp', 'netapp_disk_type': 'SSD', 'replication_enabled': False, - 'online_extend_support': False, + 'online_extend_support': True, }] expected[0].update({'QoS_support': cluster_credentials}) diff --git a/cinder/volume/drivers/netapp/dataontap/block_base.py b/cinder/volume/drivers/netapp/dataontap/block_base.py index 832a4dc2aa1..e66b8d5f04f 100644 --- a/cinder/volume/drivers/netapp/dataontap/block_base.py +++ b/cinder/volume/drivers/netapp/dataontap/block_base.py @@ -592,18 +592,26 @@ class NetAppBlockStorageLibrary(object): new_size_bytes = six.text_type(int(new_size) * units.Gi) # Reused by clone scenarios. # Hence comparing the stored size. - if curr_size_bytes != new_size_bytes: + if curr_size_bytes == new_size_bytes: + LOG.info("No need to extend volume %s" + " as it is already the requested new size.", name) + return + + ontap_version = self.zapi_client.get_ontap_version(cached=True) + + if ontap_version >= '9.5': + self.zapi_client.do_direct_resize(path, new_size_bytes) + else: lun_geometry = self.zapi_client.get_lun_geometry(path) - if (lun_geometry and lun_geometry.get("max_resize") - and int(lun_geometry.get("max_resize")) >= + if (lun_geometry and int(lun_geometry.get("max_resize", 0)) >= int(new_size_bytes)): self.zapi_client.do_direct_resize(path, new_size_bytes) else: if volume['attach_status'] != 'detached': msg = _('Volume %(vol_id)s cannot be resized from ' '%(old_size)s to %(new_size)s, because would ' - 'exceed its max geometry %(max_geo)s while not ' - 'being detached.') + 'exceed its max geometry %(max_geo)s while ' + 'not being detached.') raise exception.VolumeBackendAPIException(data=msg % { 'vol_id': name, 'old_size': curr_size_bytes, @@ -612,10 +620,7 @@ class NetAppBlockStorageLibrary(object): self._do_sub_clone_resize( path, new_size_bytes, qos_policy_group_name=qos_policy_group_name) - self.lun_table[name].size = new_size_bytes - else: - LOG.info("No need to extend volume %s" - " as it is already the requested new size.", name) + self.lun_table[name].size = new_size_bytes def _get_vol_option(self, volume_name, option_name): """Get the value for the volume option.""" diff --git a/cinder/volume/drivers/netapp/dataontap/block_cmode.py b/cinder/volume/drivers/netapp/dataontap/block_cmode.py index 741dd4c272d..4fcf6c86a2c 100644 --- a/cinder/volume/drivers/netapp/dataontap/block_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/block_cmode.py @@ -296,7 +296,7 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary, # Add driver capabilities and config info pool['QoS_support'] = self.using_cluster_credentials pool['multiattach'] = True - pool['online_extend_support'] = False + pool['online_extend_support'] = True pool['consistencygroup_support'] = True pool['consistent_group_snapshot_enabled'] = True pool['reserved_percentage'] = self.reserved_percentage diff --git a/cinder/volume/drivers/netapp/dataontap/client/api.py b/cinder/volume/drivers/netapp/dataontap/client/api.py index 4e530dd2e04..61b89cd2530 100644 --- a/cinder/volume/drivers/netapp/dataontap/client/api.py +++ b/cinder/volume/drivers/netapp/dataontap/client/api.py @@ -135,6 +135,12 @@ class NaServer(object): self._ns = NaServer.NETAPP_NS self._refresh_conn = True + def set_ontap_version(self, version): + self._ontap_version = version + + def get_ontap_version(self): + return self._ontap_version + def set_api_version(self, major, minor): """Set the API version.""" try: diff --git a/cinder/volume/drivers/netapp/dataontap/client/client_base.py b/cinder/volume/drivers/netapp/dataontap/client/client_base.py index ddc8bd260c3..791d07a4da6 100644 --- a/cinder/volume/drivers/netapp/dataontap/client/client_base.py +++ b/cinder/volume/drivers/netapp/dataontap/client/client_base.py @@ -32,6 +32,7 @@ from cinder.volume.drivers.netapp import utils as na_utils LOG = logging.getLogger(__name__) DELETED_PREFIX = 'deleted_cinder_' +MAX_SIZE_FOR_A_LUN = '17555678822400' @six.add_metaclass(utils.TraceWrapperMetaclass) @@ -62,6 +63,27 @@ class Client(object): """Set up the repository of available Data ONTAP features.""" self.features = na_utils.Features() + def get_ontap_version(self, cached=True): + """Gets the ONTAP version.""" + + if cached: + return self.connection.get_ontap_version() + + ontap_version = netapp_api.NaElement("system-get-version") + result = self.connection.invoke_successfully(ontap_version, True) + + version_tuple = result.get_child_by_name( + 'version-tuple') or netapp_api.NaElement('none') + system_version_tuple = version_tuple.get_child_by_name( + 'system-version-tuple') or netapp_api.NaElement('none') + + generation = system_version_tuple.get_child_content("generation") + major = system_version_tuple.get_child_content("major") + + return '%(generation)s.%(major)s' % { + 'generation': generation, + 'major': major} + def get_ontapi_version(self, cached=True): """Gets the supported ontapi version.""" @@ -89,9 +111,23 @@ class Client(object): """Issues API request for creating LUN on volume.""" path = '/vol/%s/%s' % (volume_name, lun_name) - params = {'path': path, 'size': six.text_type(size), + space_reservation = metadata['SpaceReserved'] + initial_size = size + ontap_version = self.get_ontap_version() + + # On older ONTAP versions the extend size is limited to its + # geometry on max_resize_size. In order to remove this + # limitation we create the LUN with its maximum possible size + # and then shrink to the requested size. + if ontap_version < '9.5': + initial_size = MAX_SIZE_FOR_A_LUN + # In order to create a LUN with its maximum size (16TB), + # the space_reservation needs to be disabled + space_reservation = 'false' + + params = {'path': path, 'size': str(initial_size), 'ostype': metadata['OsType'], - 'space-reservation-enabled': metadata['SpaceReserved']} + 'space-reservation-enabled': space_reservation} version = self.get_ontapi_version() if version >= (1, 110): params['use-exact-size'] = 'true' @@ -111,6 +147,21 @@ class Client(object): 'volume_name': volume_name, 'ex': ex}) + if ontap_version < '9.5': + self.do_direct_resize(path, six.text_type(size)) + if metadata['SpaceReserved'] == 'true': + self.set_lun_space_reservation(path, True) + + def set_lun_space_reservation(self, path, flag): + """Sets the LUN space reservation on ONTAP.""" + + lun_modify_space_reservation = ( + netapp_api.NaElement.create_node_with_children( + 'lun-set-space-reservation-info', **{ + 'path': path, + 'enable': str(flag)})) + self.connection.invoke_successfully(lun_modify_space_reservation, True) + def destroy_lun(self, path, force=True): """Destroys the LUN at the path.""" lun_destroy = netapp_api.NaElement.create_node_with_children( diff --git a/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py b/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py index 4fd7919783c..15fd1efd4dd 100644 --- a/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py @@ -52,6 +52,8 @@ class Client(client_base.Client): (major, minor) = self.get_ontapi_version(cached=False) self.connection.set_api_version(major, minor) self._init_features() + ontap_version = self.get_ontap_version(cached=False) + self.connection.set_ontap_version(ontap_version) def _init_features(self): super(Client, self)._init_features() diff --git a/doc/source/reference/support-matrix.ini b/doc/source/reference/support-matrix.ini index 4a96a5f54e6..985c041550f 100644 --- a/doc/source/reference/support-matrix.ini +++ b/doc/source/reference/support-matrix.ini @@ -309,7 +309,7 @@ driver.linbit_linstor=complete driver.lvm=complete driver.macrosan=complete driver.nec=complete -driver.netapp_ontap=missing +driver.netapp_ontap=complete driver.netapp_solidfire=complete driver.nexenta=complete driver.nfs=complete diff --git a/releasenotes/notes/bug-1874134-netapp-ONTAP-fix-max-resize-size-ad2d88da8721560e.yaml b/releasenotes/notes/bug-1874134-netapp-ONTAP-fix-max-resize-size-ad2d88da8721560e.yaml new file mode 100644 index 00000000000..24f4a8b6ea8 --- /dev/null +++ b/releasenotes/notes/bug-1874134-netapp-ONTAP-fix-max-resize-size-ad2d88da8721560e.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fix bug `#1874134 `_, + allowing an iSCSI or FCP volume to be extended to a size up to 16TB + regardless of its original size, even if it's attached to an instance.