NetApp ONTAP iSCSI: Force exception on online extend

The Netapp ONTAP iSCSI driver does not support online volume extend. It
may work if the requested size does not exceed the LUN max geometry,
otherwise it will require the LUN to be detached. In such case, the
backend currently detaches and leaves the volume in an inconsistent
state.

This patch forces the ONTAP iSCSI driver to raise an exception whenever
an online extend is requested and it detects it would exceed the LUN max
geometry.

Change-Id: Ie3dddbc05c6cd32e27168d68f4cb819364b0438c
Closes-Bug: #1712651
This commit is contained in:
Lucio Seki 2018-04-18 17:35:56 -03:00 committed by Lucio Seki
parent 5dd1d80299
commit 2b60912d56
4 changed files with 108 additions and 14 deletions

View File

@ -45,6 +45,8 @@ NFS_SHARE_PATH = '/export'
NFS_EXPORT_1 = '%s:%s' % (NFS_HOST, NFS_SHARE_PATH)
NFS_EXPORT_2 = 'nfs-host2:/export'
MOUNT_POINT = '/mnt/nfs'
ATTACHED = 'attached'
DETACHED = 'detached'
LUN_METADATA = {
'OsType': None,
'SpaceReserved': 'true',
@ -57,6 +59,7 @@ VOLUME = {
'size': SIZE,
'id': VOLUME_ID,
'host': HOST_STRING,
'attach_status': DETACHED,
}
NFS_VOLUME = {
'name': NFS_FILE_PATH,

View File

@ -165,12 +165,12 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase):
self.assertEqual(1, block_base.LOG.exception.call_count)
def test_create_volume_no_pool_provided_by_scheduler(self):
fake_volume = copy.deepcopy(fake.VOLUME)
volume_copy = copy.deepcopy(fake.VOLUME)
# Set up fake volume whose 'host' field is missing pool information.
fake_volume['host'] = '%s@%s' % (fake.HOST_NAME, fake.BACKEND_NAME)
volume_copy['host'] = '%s@%s' % (fake.HOST_NAME, fake.BACKEND_NAME)
self.assertRaises(exception.InvalidHost, self.library.create_volume,
fake_volume)
volume_copy)
@mock.patch.object(block_base.NetAppBlockStorageLibrary,
'_get_lun_attr')
@ -1114,8 +1114,6 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase):
new_size_bytes = new_size * units.Gi
max_size = fake.LUN_SIZE * 10
max_size_bytes = max_size * units.Gi
fake_volume = copy.copy(fake.VOLUME)
fake_volume['size'] = new_size
fake_lun = block_base.NetAppLun(fake.LUN_HANDLE,
fake.LUN_ID,
@ -1144,6 +1142,45 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase):
self.assertEqual(six.text_type(new_size_bytes),
self.library.lun_table[fake.VOLUME['name']].size)
def test__extend_attached_volume_direct(self):
current_size = fake.LUN_SIZE
current_size_bytes = current_size * units.Gi
new_size = fake.LUN_SIZE * 2
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['size'] = new_size
volume_copy['attach_status'] = fake.ATTACHED
fake_lun = block_base.NetAppLun(fake.LUN_HANDLE,
fake.LUN_ID,
current_size_bytes,
fake.LUN_METADATA)
mock_get_lun_from_table = self.mock_object(
self.library, '_get_lun_from_table', return_value=fake_lun)
fake_lun_geometry = {'max_resize': six.text_type(max_size_bytes)}
mock_get_lun_geometry = self.mock_object(
self.library.zapi_client, 'get_lun_geometry',
return_value=fake_lun_geometry)
mock_do_direct_resize = self.mock_object(self.library.zapi_client,
'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._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_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):
current_size = fake.LUN_SIZE
@ -1152,8 +1189,6 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase):
new_size_bytes = new_size * units.Gi
max_size = fake.LUN_SIZE * 10
max_size_bytes = max_size * units.Gi
fake_volume = copy.copy(fake.VOLUME)
fake_volume['size'] = new_size
fake_lun = block_base.NetAppLun(fake.LUN_HANDLE,
fake.LUN_ID,
@ -1183,15 +1218,15 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase):
self.assertEqual(six.text_type(new_size_bytes),
self.library.lun_table[fake.VOLUME['name']].size)
def test__extend_volume_no_change(self):
def test__extend_attached_volume_clone_error(self):
current_size = fake.LUN_SIZE
current_size_bytes = current_size * units.Gi
new_size = fake.LUN_SIZE
new_size = fake.LUN_SIZE * 20
max_size = fake.LUN_SIZE * 10
max_size_bytes = max_size * units.Gi
fake_volume = copy.copy(fake.VOLUME)
fake_volume['size'] = new_size
volume_copy = copy.copy(fake.VOLUME)
volume_copy['attach_status'] = fake.ATTACHED
fake_lun = block_base.NetAppLun(fake.LUN_HANDLE,
fake.LUN_ID,
@ -1207,11 +1242,51 @@ 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 = {fake_volume['name']: fake_lun}
self.library.lun_table = {volume_copy['name']: fake_lun}
self.library._extend_volume(fake_volume, new_size, 'fake_qos_policy')
self.assertRaises(exception.VolumeBackendAPIException,
self.library._extend_volume,
volume_copy,
new_size,
fake.QOS_POLICY_GROUP_NAME)
mock_get_lun_from_table.assert_called_once_with(fake_volume['name'])
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)
def test__extend_volume_no_change(self):
current_size = fake.LUN_SIZE
current_size_bytes = current_size * units.Gi
new_size = fake.LUN_SIZE
max_size = fake.LUN_SIZE * 10
max_size_bytes = max_size * units.Gi
volume_copy = copy.copy(fake.VOLUME)
volume_copy['size'] = new_size
fake_lun = block_base.NetAppLun(fake.LUN_HANDLE,
fake.LUN_ID,
current_size_bytes,
fake.LUN_METADATA)
mock_get_lun_from_table = self.mock_object(
self.library, '_get_lun_from_table', return_value=fake_lun)
fake_lun_geometry = {'max_resize': six.text_type(max_size_bytes)}
mock_get_lun_geometry = self.mock_object(
self.library.zapi_client, 'get_lun_geometry',
return_value=fake_lun_geometry)
mock_do_direct_resize = self.mock_object(self.library.zapi_client,
'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._extend_volume(volume_copy, new_size, 'fake_qos_policy')
mock_get_lun_from_table.assert_called_once_with(volume_copy['name'])
self.assertFalse(mock_get_lun_geometry.called)
self.assertFalse(mock_do_direct_resize.called)
self.assertFalse(mock_do_sub_clone_resize.called)

View File

@ -589,6 +589,16 @@ class NetAppBlockStorageLibrary(object):
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.')
raise exception.VolumeBackendAPIException(data=msg % {
'vol_id': name,
'old_size': curr_size_bytes,
'new_size': new_size_bytes,
'max_geo': lun_geometry.get("max_resize")})
self._do_sub_clone_resize(
path, new_size_bytes,
qos_policy_group_name=qos_policy_group_name)

View File

@ -0,0 +1,6 @@
---
fixes:
- |
NetApp ONTAP iSCSI (bug 1712651): Fix ONTAP NetApp iSCSI driver not
raising a proper exception when trying to extend an attached volume
beyond its max geometry.