NetApp ONTAP: Fix extend volume for iSCSI/FCP

Due to a characteristic on ONTAP devices, the volume extend
operation has a max resize size limited by underlying LUN's
geometry, so the support for extend online volumes was
disabled.

This patch fixes it by allowing a volume (attached or not)
to be extended up to 16TB, which is the max LUN size
supported by ONTAP.

NFS online_extend_support is still disabled due to a bug [0]
found on the generic implementation for NFS driver, which
ONTAP NFS driver relies on.

Closes-Bug: #1874134

[0] https://bugs.launchpad.net/cinder/+bug/1870367

Change-Id: I2812d71b23f27fe8be4e9a757094867f71b1afa2
(cherry picked from commit 510613e135)
(cherry picked from commit 8c55df95dc)
(cherry picked from commit f13475f718)
This commit is contained in:
Arthur Nascimento Santos 2020-03-26 14:04:23 +00:00 committed by Lucio Seki
parent 463c17a3f9
commit 836cda0121
12 changed files with 330 additions and 59 deletions

View File

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

View File

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

View File

@ -421,6 +421,21 @@ CG_VOLUME_SNAPSHOT = {
'volume_id': CG_VOLUME_ID,
}
SYSTEM_GET_VERSION_RESPONSE = etree.XML("""
<results status="passed">
<build-timestamp>1395426307</build-timestamp>
<is-clustered>true</is-clustered>
<version>NetApp Release 9.6P2: Fri Jul 19 06:06:59 UTC 2019</version>
<version-tuple>
<system-version-tuple>
<generation>9</generation>
<major>6</major>
<minor>0</minor>
</system-version-tuple>
</version-tuple>
</results>
""")
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,

View File

@ -1134,7 +1134,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
@ -1143,6 +1144,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,
@ -1161,16 +1165,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
@ -1182,6 +1193,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,
@ -1196,20 +1210,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
@ -1218,6 +1239,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,
@ -1236,29 +1260,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)
@ -1272,21 +1310,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
@ -1296,6 +1348,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,
@ -1318,6 +1372,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):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -51,6 +51,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()

View File

@ -313,7 +313,7 @@ driver.linbit_drbd=complete
driver.linbit_linstor=complete
driver.lvm=complete
driver.nec=complete
driver.netapp_ontap=missing
driver.netapp_ontap=complete
driver.netapp_solidfire=complete
driver.nexenta=complete
driver.nfs=complete

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Fix bug `#1874134 <https://bugs.launchpad.net/cinder/+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.