From 7f6c42c0be8854ed0182660b2e12cb7f7468046f Mon Sep 17 00:00:00 2001 From: Michael Arndt Date: Wed, 1 Apr 2020 17:59:31 +0000 Subject: [PATCH] [NetApp] Adding support for Adaptive QoS in NetApp driver. Added support for Adaptive QoS policies that have been pre-created on the storage system, with the NetApp driver and clustered ONTAP version 9.4 or higher. To use this feature, configure a Cinder volume type with the following extra-specs:: netapp:qos_policy_group= netapp:qos_policy_group_is_adaptive=" True" Note that a cluster scoped account must be used in the driver configuration in order to use QoS in clustered ONTAP. Partially-implements: bp netapp-adaptive-qos-support Co-Authored-By: Lucio Seki Change-Id: Idcdbd042bb10f7381048fe4cb9fb870c3eebb6ce --- .../dataontap/client/test_client_cmode.py | 4 ++-- .../netapp/dataontap/test_block_base.py | 11 +++++---- .../netapp/dataontap/test_block_cmode.py | 7 ++++-- .../netapp/dataontap/test_nfs_cmode.py | 16 ++++++------- .../drivers/netapp/dataontap/block_base.py | 15 ++++++++---- .../drivers/netapp/dataontap/block_cmode.py | 23 +++++++++++-------- .../netapp/dataontap/client/client_base.py | 10 ++++++-- .../netapp/dataontap/client/client_cmode.py | 19 +++++++++++---- .../drivers/netapp/dataontap/nfs_cmode.py | 9 ++++++-- .../manual/cinder-netapp_cdot_extraspecs.inc | 6 +++++ ...ort-for-adaptive-qos-0b76dadf7c044cd8.yaml | 14 +++++++++++ 11 files changed, 96 insertions(+), 38 deletions(-) create mode 100644 releasenotes/notes/netapp-add-support-for-adaptive-qos-0b76dadf7c044cd8.yaml 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 2c5062cf1ec..14c6bd76cee 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 @@ -808,8 +808,8 @@ class NetAppCmodeClientTestCase(test.TestCase): 'vserver': self.vserver } - self.client.file_assign_qos( - fake.FLEXVOL, fake.QOS_POLICY_GROUP_NAME, fake.NFS_FILE_PATH) + self.client.file_assign_qos(fake.FLEXVOL, fake.QOS_POLICY_GROUP_NAME, + False, fake.NFS_FILE_PATH) self.mock_send_request.assert_has_calls([ mock.call('file-assign-qos', api_args, False)]) 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 b444e7cca3f..b8bf4be28d5 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 @@ -117,7 +117,8 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): def test_create_volume(self): volume_size_in_bytes = int(fake.SIZE) * units.Gi - self.mock_object(na_utils, 'get_volume_extra_specs') + self.mock_object(na_utils, 'get_volume_extra_specs', + return_value={}) self.mock_object(na_utils, 'log_extra_spec_warnings') self.mock_object(block_base, 'LOG') self.mock_object(volume_utils, 'extract_host', @@ -134,7 +135,7 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): self.library._create_lun.assert_called_once_with( fake.POOL_NAME, fake.LUN_NAME, volume_size_in_bytes, - fake.LUN_METADATA, None) + fake.LUN_METADATA, None, False) self.library._get_volume_model_update.assert_called_once_with( fake.VOLUME) self.assertEqual( @@ -1062,7 +1063,8 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): self.library._clone_lun.assert_called_once_with( fake.CLONE_SOURCE_NAME, fake.CLONE_DESTINATION_NAME, space_reserved='false', - qos_policy_group_name=fake.QOS_POLICY_GROUP_NAME) + qos_policy_group_name=fake.QOS_POLICY_GROUP_NAME, + qos_policy_group_is_adaptive=False) self.library._extend_volume.assert_called_once_with( fake.CLONE_DESTINATION, fake.CLONE_DESTINATION_SIZE, fake.QOS_POLICY_GROUP_NAME) @@ -1092,7 +1094,8 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): self.library._clone_lun.assert_called_once_with( fake.CLONE_SOURCE_NAME, fake.CLONE_DESTINATION_NAME, space_reserved='true', - qos_policy_group_name=fake.QOS_POLICY_GROUP_NAME) + qos_policy_group_name=fake.QOS_POLICY_GROUP_NAME, + qos_policy_group_is_adaptive=False) self.library._extend_volume.assert_called_once_with( fake.CLONE_DESTINATION, fake.CLONE_DESTINATION_SIZE, fake.QOS_POLICY_GROUP_NAME) 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 fe3f708645c..c7fb8d5e969 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 @@ -273,7 +273,8 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase): 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, - source_snapshot=None, is_snapshot=False) + qos_policy_group_is_adaptive=False, source_snapshot=None, + is_snapshot=False) def test_clone_lun_blocks(self): """Test for when clone lun is passed block information.""" @@ -298,6 +299,7 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase): 'fakeLUN', 'fakeLUN', 'newFakeLUN', 'false', block_count=block_count, dest_block=dest_block, src_block=src_block, qos_policy_group_name=None, + qos_policy_group_is_adaptive=False, source_snapshot=None, is_snapshot=False) def test_clone_lun_no_space_reservation(self): @@ -318,6 +320,7 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase): 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, + qos_policy_group_is_adaptive=False, source_snapshot=None, is_snapshot=True) def test_get_fc_target_wwpns(self): @@ -335,7 +338,7 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase): self.library.zapi_client.create_lun.assert_called_once_with( fake.VOLUME_ID, fake.LUN_ID, fake.LUN_SIZE, fake.LUN_METADATA, - None) + None, False) @ddt.data({'replication_backends': [], 'cluster_credentials': False}, {'replication_backends': ['target_1', 'target_2'], diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_cmode.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_cmode.py index 4ae9a3eb229..8edf868b294 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_cmode.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_cmode.py @@ -635,7 +635,7 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase): mock_provision_qos.assert_has_calls([ mock.call(fake.QOS_POLICY_GROUP_INFO)]) mock_set_policy.assert_has_calls([ - mock.call(fake.NFS_VOLUME, fake.QOS_POLICY_GROUP_INFO)]) + mock.call(fake.NFS_VOLUME, fake.QOS_POLICY_GROUP_INFO, False)]) self.assertEqual(0, mock_error_log.call_count) self.assertEqual(0, mock_debug_log.call_count) self.assertEqual(0, mock_cleanup.call_count) @@ -663,7 +663,7 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase): mock_provision_qos.assert_has_calls([ mock.call(fake.QOS_POLICY_GROUP_INFO)]) mock_set_policy.assert_has_calls([ - mock.call(fake.NFS_VOLUME, fake.QOS_POLICY_GROUP_INFO)]) + mock.call(fake.NFS_VOLUME, fake.QOS_POLICY_GROUP_INFO, False)]) self.assertEqual(1, mock_error_log.call_count) self.assertEqual(1, mock_debug_log.call_count) mock_cleanup.assert_has_calls([ @@ -708,8 +708,8 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase): mock_file_assign_qos = self.driver.zapi_client.file_assign_qos - self.driver._set_qos_policy_group_on_volume(fake.NFS_VOLUME, - fake.QOS_POLICY_GROUP_INFO) + self.driver._set_qos_policy_group_on_volume( + fake.NFS_VOLUME, fake.QOS_POLICY_GROUP_INFO, False) mock_get_name_from_info.assert_has_calls([ mock.call(fake.QOS_POLICY_GROUP_INFO)]) @@ -719,7 +719,7 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase): mock.call(fake.VSERVER_NAME, fake.EXPORT_PATH)]) mock_file_assign_qos.assert_has_calls([ mock.call(fake.FLEXVOL, fake.QOS_POLICY_GROUP_NAME, - fake.NFS_VOLUME['name'])]) + False, fake.NFS_VOLUME['name'])]) def test_set_qos_policy_group_on_volume_no_info(self): @@ -734,7 +734,7 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase): mock_file_assign_qos = self.driver.zapi_client.file_assign_qos self.driver._set_qos_policy_group_on_volume(fake.NFS_VOLUME, - None) + None, False) self.assertEqual(0, mock_get_name_from_info.call_count) self.assertEqual(0, mock_extract_host.call_count) @@ -754,8 +754,8 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase): mock_file_assign_qos = self.driver.zapi_client.file_assign_qos - self.driver._set_qos_policy_group_on_volume(fake.NFS_VOLUME, - fake.QOS_POLICY_GROUP_INFO) + self.driver._set_qos_policy_group_on_volume( + fake.NFS_VOLUME, fake.QOS_POLICY_GROUP_INFO, False) mock_get_name_from_info.assert_has_calls([ mock.call(fake.QOS_POLICY_GROUP_INFO)]) diff --git a/cinder/volume/drivers/netapp/dataontap/block_base.py b/cinder/volume/drivers/netapp/dataontap/block_base.py index e66b8d5f04f..96c81d46d0f 100644 --- a/cinder/volume/drivers/netapp/dataontap/block_base.py +++ b/cinder/volume/drivers/netapp/dataontap/block_base.py @@ -237,10 +237,13 @@ class NetAppBlockStorageLibrary(object): qos_policy_group_name = ( na_utils.get_qos_policy_group_name_from_info( qos_policy_group_info)) + qos_policy_group_is_adaptive = volume_utils.is_boolean_str( + extra_specs.get('netapp:qos_policy_group_is_adaptive')) try: self._create_lun(pool_name, lun_name, size, metadata, - qos_policy_group_name) + qos_policy_group_name, + qos_policy_group_is_adaptive) except Exception: LOG.exception("Exception creating LUN %(name)s in pool %(pool)s.", {'name': lun_name, 'pool': pool_name}) @@ -357,11 +360,15 @@ class NetAppBlockStorageLibrary(object): qos_policy_group_name = ( na_utils.get_qos_policy_group_name_from_info( qos_policy_group_info)) + qos_policy_group_is_adaptive = volume_utils.is_boolean_str( + extra_specs.get('netapp:qos_policy_group_is_adaptive')) try: - self._clone_lun(source_name, destination_name, - space_reserved=self.lun_space_reservation, - qos_policy_group_name=qos_policy_group_name) + self._clone_lun( + source_name, destination_name, + space_reserved=self.lun_space_reservation, + qos_policy_group_name=qos_policy_group_name, + qos_policy_group_is_adaptive=qos_policy_group_is_adaptive) 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 4fcf6c86a2c..d5395405b30 100644 --- a/cinder/volume/drivers/netapp/dataontap/block_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/block_cmode.py @@ -165,11 +165,13 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary, self.zapi_client.send_ems_log_message(pool_ems_message) def _create_lun(self, volume_name, lun_name, size, - metadata, qos_policy_group_name=None): + metadata, qos_policy_group_name=None, + qos_policy_group_is_adaptive=False): """Creates a LUN, handling Data ONTAP differences as needed.""" self.zapi_client.create_lun( - volume_name, lun_name, size, metadata, qos_policy_group_name) + volume_name, lun_name, size, metadata, qos_policy_group_name, + qos_policy_group_is_adaptive) def _create_lun_handle(self, metadata, vserver=None): """Returns LUN handle based on filer type.""" @@ -192,19 +194,22 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary, def _clone_lun(self, name, new_name, space_reserved=None, qos_policy_group_name=None, src_block=0, dest_block=0, - block_count=0, source_snapshot=None, is_snapshot=False): + block_count=0, source_snapshot=None, is_snapshot=False, + qos_policy_group_is_adaptive=False): """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, - qos_policy_group_name=qos_policy_group_name, - src_block=src_block, dest_block=dest_block, - block_count=block_count, - source_snapshot=source_snapshot, - is_snapshot=is_snapshot) + self.zapi_client.clone_lun( + volume, name, new_name, space_reserved, + qos_policy_group_name=qos_policy_group_name, + src_block=src_block, dest_block=dest_block, + block_count=block_count, + source_snapshot=source_snapshot, + is_snapshot=is_snapshot, + qos_policy_group_is_adaptive=qos_policy_group_is_adaptive) LOG.debug("Cloned LUN with new name %s", new_name) lun = self.zapi_client.get_lun_by_args(vserver=self.vserver, diff --git a/cinder/volume/drivers/netapp/dataontap/client/client_base.py b/cinder/volume/drivers/netapp/dataontap/client/client_base.py index e9ffb5b43fc..0a94d204ec0 100644 --- a/cinder/volume/drivers/netapp/dataontap/client/client_base.py +++ b/cinder/volume/drivers/netapp/dataontap/client/client_base.py @@ -105,7 +105,8 @@ class Client(object): raise ValueError('Expects NaElement') def create_lun(self, volume_name, lun_name, size, metadata, - qos_policy_group_name=None): + qos_policy_group_name=None, + qos_policy_group_is_adaptive=False): """Issues API request for creating LUN on volume.""" path = '/vol/%s/%s' % (volume_name, lun_name) @@ -133,7 +134,12 @@ class Client(object): 'lun-create-by-size', **params) if qos_policy_group_name: - lun_create.add_new_child('qos-policy-group', qos_policy_group_name) + if qos_policy_group_is_adaptive: + lun_create.add_new_child( + 'qos-adaptive-policy-group', qos_policy_group_name) + else: + lun_create.add_new_child( + 'qos-policy-group', qos_policy_group_name) try: self.connection.invoke_successfully(lun_create, True) diff --git a/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py b/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py index 6ca075182a9..43c715ae38d 100644 --- a/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py @@ -453,7 +453,8 @@ class Client(client_base.Client): def clone_lun(self, volume, name, new_name, space_reserved='true', qos_policy_group_name=None, src_block=0, dest_block=0, - block_count=0, source_snapshot=None, is_snapshot=False): + block_count=0, source_snapshot=None, is_snapshot=False, + qos_policy_group_is_adaptive=False): # ONTAP handles only 128 MB per call as of v9.1 bc_limit = 2 ** 18 # 2^18 blocks * 512 bytes/block = 128 MB z_calls = int(math.ceil(block_count / float(bc_limit))) @@ -480,8 +481,13 @@ class Client(client_base.Client): clone_create = netapp_api.NaElement.create_node_with_children( 'clone-create', **zapi_args) if qos_policy_group_name is not None: - clone_create.add_new_child('qos-policy-group-name', - qos_policy_group_name) + if qos_policy_group_is_adaptive: + clone_create.add_new_child( + 'qos-adaptive-policy-group-name', + qos_policy_group_name) + else: + clone_create.add_new_child('qos-policy-group-name', + qos_policy_group_name) if block_count > 0: block_ranges = netapp_api.NaElement("block-ranges") segments = int(math.ceil(block_count / float(bc_limit))) @@ -520,11 +526,14 @@ class Client(client_base.Client): return [] return attr_list.get_children() - def file_assign_qos(self, flex_vol, qos_policy_group_name, file_path): + def file_assign_qos(self, flex_vol, qos_policy_group_name, + qos_policy_group_is_adaptive, file_path): """Assigns the named QoS policy-group to a file.""" + qos_arg_name = "qos-%spolicy-group-name" % ( + "adaptive-" if qos_policy_group_is_adaptive else "") api_args = { 'volume': flex_vol, - 'qos-policy-group-name': qos_policy_group_name, + qos_arg_name: qos_policy_group_name, 'file': file_path, 'vserver': self.vserver, } diff --git a/cinder/volume/drivers/netapp/dataontap/nfs_cmode.py b/cinder/volume/drivers/netapp/dataontap/nfs_cmode.py index fe50eda7575..5751932b2a2 100644 --- a/cinder/volume/drivers/netapp/dataontap/nfs_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/nfs_cmode.py @@ -161,11 +161,14 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver, self.ssc_library.get_ssc_flexvol_names()) def _do_qos_for_volume(self, volume, extra_specs, cleanup=True): + qos_policy_group_is_adaptive = volume_utils.is_boolean_str( + extra_specs.get('netapp:qos_policy_group_is_adaptive')) try: qos_policy_group_info = na_utils.get_valid_qos_policy_group_info( volume, extra_specs) self.zapi_client.provision_qos_policy_group(qos_policy_group_info) - self._set_qos_policy_group_on_volume(volume, qos_policy_group_info) + self._set_qos_policy_group_on_volume(volume, qos_policy_group_info, + qos_policy_group_is_adaptive) except Exception: with excutils.save_and_reraise_exception(): LOG.error("Setting QoS for %s failed", volume['id']) @@ -178,7 +181,8 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver, if self.replication_enabled: return {'replication_status': fields.ReplicationStatus.ENABLED} - def _set_qos_policy_group_on_volume(self, volume, qos_policy_group_info): + def _set_qos_policy_group_on_volume(self, volume, qos_policy_group_info, + qos_policy_group_is_adaptive): if qos_policy_group_info is None: return qos_policy_group_name = na_utils.get_qos_policy_group_name_from_info( @@ -192,6 +196,7 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver, export_path) self.zapi_client.file_assign_qos(flex_vol_name, qos_policy_group_name, + qos_policy_group_is_adaptive, target_path) def _clone_backing_file_for_volume(self, volume_name, clone_name, diff --git a/doc/source/configuration/tables/manual/cinder-netapp_cdot_extraspecs.inc b/doc/source/configuration/tables/manual/cinder-netapp_cdot_extraspecs.inc index dbe600c03d3..4303299cc9f 100644 --- a/doc/source/configuration/tables/manual/cinder-netapp_cdot_extraspecs.inc +++ b/doc/source/configuration/tables/manual/cinder-netapp_cdot_extraspecs.inc @@ -21,6 +21,12 @@ object within Data ONTAP should be defined before an OpenStack Block Storage volume is created, and that the QoS policy group is not associated with the destination FlexVol volume. + * - ``netapp:qos_policy_group_is_adaptive`` + - Boolean + - Set to " True" in order to instruct the driver to use an Adaptive + QoS policy group for the netapp:qos_policy_group setting. Leave this + unset or set to " False" in order to use a standard QoS policy + group for the netapp:qos_policy_group setting. * - ``netapp_mirrored`` - Boolean - Limit the candidate volume list to only the ones that are mirrored on diff --git a/releasenotes/notes/netapp-add-support-for-adaptive-qos-0b76dadf7c044cd8.yaml b/releasenotes/notes/netapp-add-support-for-adaptive-qos-0b76dadf7c044cd8.yaml new file mode 100644 index 00000000000..c6e37d58ee3 --- /dev/null +++ b/releasenotes/notes/netapp-add-support-for-adaptive-qos-0b76dadf7c044cd8.yaml @@ -0,0 +1,14 @@ +--- +features: + - | + NetApp ONTAP: + Added support for Adaptive QoS policies that have been pre-created on + the storage system, with the NetApp driver and clustered ONTAP version + 9.4 or higher. To use this feature, configure a Cinder volume type with + the following extra-specs:: + + netapp:qos_policy_group= + netapp:qos_policy_group_is_adaptive=" True" + + Note that a cluster scoped account must be used in the driver + configuration in order to use QoS in clustered ONTAP.