Merge "[NetApp] Adding support for Adaptive QoS in NetApp driver."

This commit is contained in:
Zuul 2020-09-17 04:49:39 +00:00 committed by Gerrit Code Review
commit fb7edff4d0
11 changed files with 96 additions and 38 deletions

View File

@ -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)])

View File

@ -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)

View File

@ -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'],

View File

@ -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)])

View File

@ -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:

View File

@ -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,

View File

@ -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)

View File

@ -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,
}

View File

@ -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,

View File

@ -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 "<is> 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 "<is> 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

View File

@ -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=<name_of_precreated_aqos_policy>
netapp:qos_policy_group_is_adaptive="<is> True"
Note that a cluster scoped account must be used in the driver
configuration in order to use QoS in clustered ONTAP.