From ecde3db38074b1118d159f5f0de19844206d3478 Mon Sep 17 00:00:00 2001 From: Goutham Pacha Ravi Date: Thu, 23 Jul 2015 17:37:44 -0400 Subject: [PATCH] Configure space reservation on NetApp Data ONTAP This patchset allows configuring Cinder volumes on NetApp Data ONTAP backends to be thick or thin provisioned. DocImpact Implements: blueprint netapp-thin-provisioned-luns Change-Id: I1a2679c72ca0fb56edd0c9e39d361408022380e3 --- .../volume/drivers/netapp/dataontap/fakes.py | 27 +++++++++++ .../netapp/dataontap/test_block_7mode.py | 42 +++++++---------- .../netapp/dataontap/test_block_base.py | 26 +++++++++- .../netapp/dataontap/test_block_cmode.py | 47 +++++++++---------- .../drivers/netapp/dataontap/block_7mode.py | 4 +- .../drivers/netapp/dataontap/block_base.py | 9 +++- .../drivers/netapp/dataontap/block_cmode.py | 4 +- cinder/volume/drivers/netapp/options.py | 9 +++- 8 files changed, 111 insertions(+), 57 deletions(-) diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/fakes.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/fakes.py index 4f0f710e9..e340f3104 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/fakes.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/fakes.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api + VOLUME_ID = 'f10d1a84-9b7b-427e-8fec-63c48b509a56' LUN_ID = 'ee6b4cc7-477b-4016-aa0c-7127b4e3af86' LUN_HANDLE = 'fake_lun_handle' @@ -209,3 +211,28 @@ SNAPSHOT = { VOLUME_REF = {'name': 'fake_vref_name', 'size': 42} FILE_LIST = ['file1', 'file2', 'file3'] + +FAKE_LUN = netapp_api.NaElement.create_node_with_children( + 'lun-info', + **{'alignment': 'indeterminate', + 'block-size': '512', + 'comment': '', + 'creation-timestamp': '1354536362', + 'is-space-alloc-enabled': 'false', + 'is-space-reservation-enabled': 'true', + 'mapped': 'false', + 'multiprotocol-type': 'linux', + 'online': 'true', + 'path': '/vol/fakeLUN/fakeLUN', + 'prefix-size': '0', + 'qtree': '', + 'read-only': 'false', + 'serial-number': '2FfGI$APyN68', + 'share-state': 'none', + 'size': '20971520', + 'size-used': '0', + 'staging': 'false', + 'suffix-size': '0', + 'uuid': 'cec1f3d7-3d41-11e2-9cf4-123478563412', + 'volume': 'fakeLUN', + 'vserver': 'fake_vserver'}) diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_7mode.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_7mode.py index a4f4a6126..ac7b21b59 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_7mode.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_7mode.py @@ -269,42 +269,32 @@ class NetAppBlockStorage7modeLibraryTestCase(test.TestCase): def test_clone_lun_zero_block_count(self): """Test for when clone lun is not passed a block count.""" + self.library._get_lun_attr = mock.Mock(return_value={ + 'Volume': 'fakeLUN', 'Path': '/vol/fake/fakeLUN'}) + self.library.zapi_client = mock.Mock() + self.library.zapi_client.get_lun_by_args.return_value = [fake.FAKE_LUN] + self.library._add_lun_to_table = mock.Mock() + + self.library._clone_lun('fakeLUN', 'newFakeLUN', 'false') + + self.library.zapi_client.clone_lun.assert_called_once_with( + '/vol/fake/fakeLUN', '/vol/fake/newFakeLUN', 'fakeLUN', + 'newFakeLUN', 'false', block_count=0, dest_block=0, src_block=0) - lun = netapp_api.NaElement.create_node_with_children( - 'lun-info', - **{'alignment': 'indeterminate', - 'block-size': '512', - 'comment': '', - 'creation-timestamp': '1354536362', - 'is-space-alloc-enabled': 'false', - 'is-space-reservation-enabled': 'true', - 'mapped': 'false', - 'multiprotocol-type': 'linux', - 'online': 'true', - 'path': '/vol/fakeLUN/fakeLUN', - 'prefix-size': '0', - 'qtree': '', - 'read-only': 'false', - 'serial-number': '2FfGI$APyN68', - 'share-state': 'none', - 'size': '20971520', - 'size-used': '0', - 'staging': 'false', - 'suffix-size': '0', - 'uuid': 'cec1f3d7-3d41-11e2-9cf4-123478563412', - 'volume': 'fakeLUN', - 'vserver': 'fake_vserver'}) + def test_clone_lun_no_space_reservation(self): + """Test for when space_reservation is not passed.""" self.library._get_lun_attr = mock.Mock(return_value={ 'Volume': 'fakeLUN', 'Path': '/vol/fake/fakeLUN'}) + self.library.lun_space_reservation = 'false' self.library.zapi_client = mock.Mock() - self.library.zapi_client.get_lun_by_args.return_value = [lun] + self.library.zapi_client.get_lun_by_args.return_value = [fake.FAKE_LUN] self.library._add_lun_to_table = mock.Mock() self.library._clone_lun('fakeLUN', 'newFakeLUN') self.library.zapi_client.clone_lun.assert_called_once_with( '/vol/fake/fakeLUN', '/vol/fake/newFakeLUN', 'fakeLUN', - 'newFakeLUN', 'true', block_count=0, dest_block=0, src_block=0) + 'newFakeLUN', 'false', block_count=0, dest_block=0, src_block=0) def test_clone_lun_qos_supplied(self): """Test for qos supplied in clone lun invocation.""" 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 626e0cd90..f2d4fcadc 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 @@ -382,6 +382,7 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): def test_do_setup_san_configured(self, mock_check_flags): self.library.configuration.netapp_lun_ostype = 'windows' self.library.configuration.netapp_host_type = 'solaris' + self.library.configuration.netapp_lun_space_reservation = 'disabled' self.library.do_setup(mock.Mock()) self.assertTrue(mock_check_flags.called) self.assertEqual('windows', self.library.lun_ostype) @@ -391,11 +392,32 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): def test_do_setup_san_unconfigured(self, mock_check_flags): self.library.configuration.netapp_lun_ostype = None self.library.configuration.netapp_host_type = None + self.library.configuration.netapp_lun_space_reservation = 'enabled' self.library.do_setup(mock.Mock()) self.assertTrue(mock_check_flags.called) self.assertEqual('linux', self.library.lun_ostype) self.assertEqual('linux', self.library.host_type) + def test_do_setup_space_reservation_disabled(self): + self.mock_object(na_utils, 'check_flags') + self.library.configuration.netapp_lun_ostype = None + self.library.configuration.netapp_host_type = None + self.library.configuration.netapp_lun_space_reservation = 'disabled' + + self.library.do_setup(mock.Mock()) + + self.assertEqual('false', self.library.lun_space_reservation) + + def test_do_setup_space_reservation_enabled(self): + self.mock_object(na_utils, 'check_flags') + self.library.configuration.netapp_lun_ostype = None + self.library.configuration.netapp_host_type = None + self.library.configuration.netapp_lun_space_reservation = 'enabled' + + self.library.do_setup(mock.Mock()) + + self.assertEqual('true', self.library.lun_space_reservation) + def test_get_existing_vol_manage_missing_id_path(self): self.assertRaises(exception.ManageExistingInvalidReference, self.library._get_existing_vol_with_manage_ref, @@ -675,6 +697,7 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): self.mock_object(self.library, 'extend_volume') self.mock_object(self.library, 'delete_volume') self.mock_object(self.library, '_mark_qos_policy_group_for_deletion') + self.library.lun_space_reservation = 'false' self.library._clone_source_to_destination(fake.CLONE_SOURCE, fake.CLONE_DESTINATION) @@ -685,7 +708,7 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): fake.CLONE_DESTINATION, fake.EXTRA_SPECS) self.library._clone_lun.assert_called_once_with( fake.CLONE_SOURCE_NAME, fake.CLONE_DESTINATION_NAME, - space_reserved='true', + space_reserved='false', qos_policy_group_name=fake.QOS_POLICY_GROUP_NAME) self.library.extend_volume.assert_called_once_with( fake.CLONE_DESTINATION, fake.CLONE_DESTINATION_SIZE, @@ -704,6 +727,7 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): side_effect=Exception)) self.mock_object(self.library, 'delete_volume') self.mock_object(self.library, '_mark_qos_policy_group_for_deletion') + self.library.lun_space_reservation = 'true' self.assertRaises(exception.VolumeBackendAPIException, self.library._clone_source_to_destination, 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 12568ad93..c610e6a70 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 @@ -164,30 +164,27 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase): self.library.zapi_client = mock.Mock() self.library.zapi_client.get_lun_by_args.return_value = [ mock.Mock(spec=netapp_api.NaElement)] - lun = netapp_api.NaElement.create_node_with_children( - 'lun-info', - **{'alignment': 'indeterminate', - 'block-size': '512', - 'comment': '', - 'creation-timestamp': '1354536362', - 'is-space-alloc-enabled': 'false', - 'is-space-reservation-enabled': 'true', - 'mapped': 'false', - 'multiprotocol-type': 'linux', - 'online': 'true', - 'path': '/vol/fakeLUN/lun1', - 'prefix-size': '0', - 'qtree': '', - 'read-only': 'false', - 'serial-number': '2FfGI$APyN68', - 'share-state': 'none', - 'size': '20971520', - 'size-used': '0', - 'staging': 'false', - 'suffix-size': '0', - 'uuid': 'cec1f3d7-3d41-11e2-9cf4-123478563412', - 'volume': 'fakeLUN', - 'vserver': 'fake_vserver'}) + lun = fake.FAKE_LUN + self.library._get_lun_by_args = mock.Mock(return_value=[lun]) + self.library._add_lun_to_table = mock.Mock() + self.library._update_stale_vols = mock.Mock() + + self.library._clone_lun('fakeLUN', 'newFakeLUN', 'false') + + self.library.zapi_client.clone_lun.assert_called_once_with( + 'fakeLUN', 'fakeLUN', 'newFakeLUN', 'false', block_count=0, + dest_block=0, src_block=0, qos_policy_group_name=None) + + def test_clone_lun_no_space_reservation(self): + """Test for when space_reservation is not passed.""" + + self.library._get_lun_attr = mock.Mock(return_value={'Volume': + 'fakeLUN'}) + self.library.zapi_client = mock.Mock() + self.library.lun_space_reservation = 'false' + self.library.zapi_client.get_lun_by_args.return_value = [ + mock.Mock(spec=netapp_api.NaElement)] + lun = fake.FAKE_LUN self.library._get_lun_by_args = mock.Mock(return_value=[lun]) self.library._add_lun_to_table = mock.Mock() self.library._update_stale_vols = mock.Mock() @@ -195,7 +192,7 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase): self.library._clone_lun('fakeLUN', 'newFakeLUN') self.library.zapi_client.clone_lun.assert_called_once_with( - 'fakeLUN', 'fakeLUN', 'newFakeLUN', 'true', block_count=0, + 'fakeLUN', 'fakeLUN', 'newFakeLUN', 'false', block_count=0, dest_block=0, src_block=0, qos_policy_group_name=None) def test_get_fc_target_wwpns(self): diff --git a/cinder/volume/drivers/netapp/dataontap/block_7mode.py b/cinder/volume/drivers/netapp/dataontap/block_7mode.py index ef7a22904..03ac32363 100644 --- a/cinder/volume/drivers/netapp/dataontap/block_7mode.py +++ b/cinder/volume/drivers/netapp/dataontap/block_7mode.py @@ -181,10 +181,12 @@ class NetAppBlockStorage7modeLibrary(block_base.NetAppBlockStorageLibrary): return True return False - def _clone_lun(self, name, new_name, space_reserved='true', + def _clone_lun(self, name, new_name, space_reserved=None, qos_policy_group_name=None, src_block=0, dest_block=0, block_count=0): """Clone LUN with the given handle to the new name.""" + if not space_reserved: + space_reserved = self.lun_space_reservation if qos_policy_group_name is not None: msg = _('Data ONTAP operating in 7-Mode does not support QoS ' 'policy groups.') diff --git a/cinder/volume/drivers/netapp/dataontap/block_base.py b/cinder/volume/drivers/netapp/dataontap/block_base.py index 5527f45de..d876adfa7 100644 --- a/cinder/volume/drivers/netapp/dataontap/block_base.py +++ b/cinder/volume/drivers/netapp/dataontap/block_base.py @@ -94,6 +94,7 @@ class NetAppBlockStorageLibrary(object): self.lun_table = {} self.lun_ostype = None self.host_type = None + self.lun_space_reservation = 'true' self.lookup_service = fczm_utils.create_lookup_service() self.app_version = kwargs.get("app_version", "unknown") @@ -111,6 +112,10 @@ class NetAppBlockStorageLibrary(object): or self.DEFAULT_LUN_OS) self.host_type = (self.configuration.netapp_host_type or self.DEFAULT_HOST_TYPE) + if self.configuration.netapp_lun_space_reservation == 'enabled': + self.lun_space_reservation = 'true' + else: + self.lun_space_reservation = 'false' def check_for_setup_error(self): """Check that the driver is working and can communicate. @@ -160,7 +165,7 @@ class NetAppBlockStorageLibrary(object): size = int(volume['size']) * units.Gi metadata = {'OsType': self.lun_ostype, - 'SpaceReserved': 'true', + 'SpaceReserved': self.lun_space_reservation, 'Path': '/vol/%s/%s' % (pool_name, lun_name)} qos_policy_group_info = self._setup_qos_for_volume(volume, extra_specs) @@ -268,7 +273,7 @@ class NetAppBlockStorageLibrary(object): try: self._clone_lun(source_name, destination_name, - space_reserved='true', + space_reserved=self.lun_space_reservation, qos_policy_group_name=qos_policy_group_name) if destination_size != source_size: diff --git a/cinder/volume/drivers/netapp/dataontap/block_cmode.py b/cinder/volume/drivers/netapp/dataontap/block_cmode.py index 0fd2263b6..3e8b0868d 100644 --- a/cinder/volume/drivers/netapp/dataontap/block_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/block_cmode.py @@ -115,10 +115,12 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary): return igroup_name, lun_map['lun-id'] return None, None - def _clone_lun(self, name, new_name, space_reserved='true', + def _clone_lun(self, name, new_name, space_reserved=None, qos_policy_group_name=None, src_block=0, dest_block=0, block_count=0): """Clone LUN with the given handle to the new name.""" + if not space_reserved: + space_reserved = self.lun_space_reservation metadata = self._get_lun_attr(name, 'metadata') volume = metadata['Volume'] self.zapi_client.clone_lun(volume, name, new_name, space_reserved, diff --git a/cinder/volume/drivers/netapp/options.py b/cinder/volume/drivers/netapp/options.py index cdc0fe0c8..f58a6475b 100644 --- a/cinder/volume/drivers/netapp/options.py +++ b/cinder/volume/drivers/netapp/options.py @@ -83,7 +83,14 @@ netapp_provisioning_opts = [ 'to restrict provisioning to the specified controller ' 'volumes. Specify the value of this option to be a ' 'comma separated list of NetApp controller volume names ' - 'to be used for provisioning.')), ] + 'to be used for provisioning.')), + cfg.StrOpt('netapp_lun_space_reservation', + default='enabled', + choices=['enabled', 'disabled'], + help=('This option determines if storage space is reserved ' + 'for LUN allocation. If enabled, LUNs are thick ' + 'provisioned. If space reservation is disabled, ' + 'storage space is allocated on demand.')), ] netapp_cluster_opts = [ cfg.StrOpt('netapp_vserver', -- 2.45.2