[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=<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. Partially-implements: bp netapp-adaptive-qos-support Co-Authored-By: Lucio Seki <lucioseki@gmail.com> Change-Id: Idcdbd042bb10f7381048fe4cb9fb870c3eebb6ce
This commit is contained in:
parent
cdfee5608b
commit
7f6c42c0be
@ -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)])
|
||||
|
@ -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)
|
||||
|
@ -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'],
|
||||
|
@ -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)])
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user