[NetApp] LUN space allocation support
Space allocation is an important NetApp driver specific feature. This needs to be set when the cinder volume is created. This is not related to thin/thick provisioning feature of cinder volumes.It is independent of that. It enables ONTAP to reclaim space automatically when host deletes data.This helps ONTAP and host to see the actual space correctly when the host deletes data. It also helps to keep a LUN (cinder volume) online when the LUN (cinder volume) in ontap runs out of space and containing volume (in ONTAP) cannot automatically grow more space. User can configure it by using volume type extra spec. By default Space allocation value is disabled for ONTAP LUN netapp:space_allocation: "<is> True" # to enable space allocation netapp:space_allocation: "<is> False" # to disable space allocation Blueprint: netapp-space-allocation-support Change-Id: Ib7072f3093067ecd8ad84e396aaecec8f15c49ba
This commit is contained in:
parent
0425c08f3d
commit
48d922cf62
|
@ -2347,6 +2347,9 @@ LUN_GET_ITER_RESULT = [
|
||||||
'OsType': LUN_GET_ITER_REST['records'][0]['os_type'],
|
'OsType': LUN_GET_ITER_REST['records'][0]['os_type'],
|
||||||
'SpaceReserved':
|
'SpaceReserved':
|
||||||
LUN_GET_ITER_REST['records'][0]['space']['guarantee']['requested'],
|
LUN_GET_ITER_REST['records'][0]['space']['guarantee']['requested'],
|
||||||
|
'SpaceAllocated':
|
||||||
|
LUN_GET_ITER_REST['records'][0]['space']
|
||||||
|
['scsi_thin_provisioning_support_enabled'],
|
||||||
'UUID': LUN_GET_ITER_REST['records'][0]['uuid'],
|
'UUID': LUN_GET_ITER_REST['records'][0]['uuid'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2360,6 +2363,9 @@ LUN_GET_ITER_RESULT = [
|
||||||
'OsType': LUN_GET_ITER_REST['records'][1]['os_type'],
|
'OsType': LUN_GET_ITER_REST['records'][1]['os_type'],
|
||||||
'SpaceReserved':
|
'SpaceReserved':
|
||||||
LUN_GET_ITER_REST['records'][1]['space']['guarantee']['requested'],
|
LUN_GET_ITER_REST['records'][1]['space']['guarantee']['requested'],
|
||||||
|
'SpaceAllocated':
|
||||||
|
LUN_GET_ITER_REST['records'][1]['space']
|
||||||
|
['scsi_thin_provisioning_support_enabled'],
|
||||||
'UUID': LUN_GET_ITER_REST['records'][1]['uuid'],
|
'UUID': LUN_GET_ITER_REST['records'][1]['uuid'],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -55,7 +55,8 @@ class NetAppBaseClientTestCase(test.TestCase):
|
||||||
self.fake_volume = six.text_type(uuid.uuid4())
|
self.fake_volume = six.text_type(uuid.uuid4())
|
||||||
self.fake_lun = six.text_type(uuid.uuid4())
|
self.fake_lun = six.text_type(uuid.uuid4())
|
||||||
self.fake_size = '1024'
|
self.fake_size = '1024'
|
||||||
self.fake_metadata = {'OsType': 'linux', 'SpaceReserved': 'true'}
|
self.fake_metadata = {'OsType': 'linux', 'SpaceReserved': 'true',
|
||||||
|
'SpaceAllocated': 'true'}
|
||||||
self.mock_send_request = self.mock_object(
|
self.mock_send_request = self.mock_object(
|
||||||
self.client.connection, 'send_request')
|
self.client.connection, 'send_request')
|
||||||
|
|
||||||
|
@ -89,14 +90,23 @@ class NetAppBaseClientTestCase(test.TestCase):
|
||||||
self.assertIsNone(self.client.check_is_naelement(element))
|
self.assertIsNone(self.client.check_is_naelement(element))
|
||||||
self.assertRaises(ValueError, self.client.check_is_naelement, None)
|
self.assertRaises(ValueError, self.client.check_is_naelement, None)
|
||||||
|
|
||||||
@ddt.data({'ontap_version': (9, 4, 0), 'space_reservation': 'true'},
|
@ddt.data({'ontap_version': (9, 4, 0),
|
||||||
{'ontap_version': (9, 4, 0), 'space_reservation': 'false'},
|
'space_reservation': 'true',
|
||||||
{'ontap_version': (9, 6, 0), 'space_reservation': 'true'},
|
'space_alloc': 'true'},
|
||||||
{'ontap_version': (9, 6, 0), 'space_reservation': 'false'})
|
{'ontap_version': (9, 4, 0),
|
||||||
|
'space_reservation': 'false',
|
||||||
|
'space_alloc': 'false'},
|
||||||
|
{'ontap_version': (9, 6, 0),
|
||||||
|
'space_reservation': 'true',
|
||||||
|
'space_alloc': 'true'},
|
||||||
|
{'ontap_version': (9, 6, 0),
|
||||||
|
'space_reservation': 'false',
|
||||||
|
'space_alloc': 'false'}, )
|
||||||
@ddt.unpack
|
@ddt.unpack
|
||||||
def test_create_lun(self, ontap_version, space_reservation):
|
def test_create_lun(self, ontap_version, space_reservation, space_alloc):
|
||||||
expected_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
|
expected_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
|
||||||
self.fake_metadata['SpaceReserved'] = space_reservation
|
self.fake_metadata['SpaceReserved'] = space_reservation
|
||||||
|
self.fake_metadata['SpaceAllocated'] = space_alloc
|
||||||
expected_space_reservation = space_reservation
|
expected_space_reservation = space_reservation
|
||||||
self.mock_object(self.client, 'get_ontap_version',
|
self.mock_object(self.client, 'get_ontap_version',
|
||||||
return_value=ontap_version)
|
return_value=ontap_version)
|
||||||
|
@ -127,7 +137,9 @@ class NetAppBaseClientTestCase(test.TestCase):
|
||||||
'size': initial_size,
|
'size': initial_size,
|
||||||
'ostype': self.fake_metadata['OsType'],
|
'ostype': self.fake_metadata['OsType'],
|
||||||
'space-reservation-enabled':
|
'space-reservation-enabled':
|
||||||
expected_space_reservation})
|
expected_space_reservation,
|
||||||
|
'space-allocation-enabled':
|
||||||
|
space_alloc})
|
||||||
self.connection.invoke_successfully.assert_called_with(
|
self.connection.invoke_successfully.assert_called_with(
|
||||||
mock.ANY, True)
|
mock.ANY, True)
|
||||||
|
|
||||||
|
@ -141,15 +153,25 @@ class NetAppBaseClientTestCase(test.TestCase):
|
||||||
else:
|
else:
|
||||||
mock_set_space_reservation.assert_not_called()
|
mock_set_space_reservation.assert_not_called()
|
||||||
|
|
||||||
@ddt.data({'ontap_version': (9, 4, 0), 'space_reservation': 'true'},
|
@ddt.data({'ontap_version': (9, 4, 0),
|
||||||
{'ontap_version': (9, 4, 0), 'space_reservation': 'false'},
|
'space_reservation': 'true',
|
||||||
{'ontap_version': (9, 6, 0), 'space_reservation': 'true'},
|
'space_alloc': 'true'},
|
||||||
{'ontap_version': (9, 6, 0), 'space_reservation': 'false'})
|
{'ontap_version': (9, 4, 0),
|
||||||
|
'space_reservation': 'false',
|
||||||
|
'space_alloc': 'false'},
|
||||||
|
{'ontap_version': (9, 6, 0),
|
||||||
|
'space_reservation': 'true',
|
||||||
|
'space_alloc': 'true'},
|
||||||
|
{'ontap_version': (9, 6, 0),
|
||||||
|
'space_reservation': 'false',
|
||||||
|
'space_alloc': 'false'}, )
|
||||||
@ddt.unpack
|
@ddt.unpack
|
||||||
def test_create_lun_exact_size(self, ontap_version, space_reservation):
|
def test_create_lun_exact_size(self, ontap_version,
|
||||||
|
space_reservation, space_alloc):
|
||||||
expected_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
|
expected_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
|
||||||
self.connection.get_api_version.return_value = (1, 110)
|
self.connection.get_api_version.return_value = (1, 110)
|
||||||
self.fake_metadata['SpaceReserved'] = space_reservation
|
self.fake_metadata['SpaceReserved'] = space_reservation
|
||||||
|
self.fake_metadata['SpaceAllocated'] = space_alloc
|
||||||
expected_space_reservation = self.fake_metadata['SpaceReserved']
|
expected_space_reservation = self.fake_metadata['SpaceReserved']
|
||||||
self.mock_object(self.client, 'get_ontap_version',
|
self.mock_object(self.client, 'get_ontap_version',
|
||||||
return_value=ontap_version)
|
return_value=ontap_version)
|
||||||
|
@ -181,7 +203,9 @@ class NetAppBaseClientTestCase(test.TestCase):
|
||||||
'ostype': self.fake_metadata['OsType'],
|
'ostype': self.fake_metadata['OsType'],
|
||||||
'use-exact-size': 'true',
|
'use-exact-size': 'true',
|
||||||
'space-reservation-enabled':
|
'space-reservation-enabled':
|
||||||
expected_space_reservation})
|
expected_space_reservation,
|
||||||
|
'space-allocation-enabled':
|
||||||
|
space_alloc})
|
||||||
self.connection.invoke_successfully.assert_called_with(
|
self.connection.invoke_successfully.assert_called_with(
|
||||||
mock.ANY, True)
|
mock.ANY, True)
|
||||||
|
|
||||||
|
@ -195,18 +219,27 @@ class NetAppBaseClientTestCase(test.TestCase):
|
||||||
else:
|
else:
|
||||||
mock_set_space_reservation.assert_not_called()
|
mock_set_space_reservation.assert_not_called()
|
||||||
|
|
||||||
@ddt.data({'ontap_version': (9, 4, 0), 'space_reservation': 'true'},
|
@ddt.data({'ontap_version': (9, 4, 0),
|
||||||
{'ontap_version': (9, 4, 0), 'space_reservation': 'false'},
|
'space_reservation': 'true',
|
||||||
{'ontap_version': (9, 6, 0), 'space_reservation': 'true'},
|
'space_alloc': 'true'},
|
||||||
{'ontap_version': (9, 6, 0), 'space_reservation': 'false'})
|
{'ontap_version': (9, 4, 0),
|
||||||
|
'space_reservation': 'false',
|
||||||
|
'space_alloc': 'false'},
|
||||||
|
{'ontap_version': (9, 6, 0),
|
||||||
|
'space_reservation': 'true',
|
||||||
|
'space_alloc': 'true'},
|
||||||
|
{'ontap_version': (9, 6, 0),
|
||||||
|
'space_reservation': 'false',
|
||||||
|
'space_alloc': 'false'}, )
|
||||||
@ddt.unpack
|
@ddt.unpack
|
||||||
def test_create_lun_with_qos_policy_group_name(
|
def test_create_lun_with_qos_policy_group_name(
|
||||||
self, ontap_version, space_reservation):
|
self, ontap_version, space_reservation, space_alloc):
|
||||||
expected_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
|
expected_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
|
||||||
expected_qos_group_name = 'qos_1'
|
expected_qos_group_name = 'qos_1'
|
||||||
mock_request = mock.Mock()
|
mock_request = mock.Mock()
|
||||||
|
|
||||||
self.fake_metadata['SpaceReserved'] = space_reservation
|
self.fake_metadata['SpaceReserved'] = space_reservation
|
||||||
|
self.fake_metadata['SpaceAllocated'] = space_alloc
|
||||||
expected_space_reservation = self.fake_metadata['SpaceReserved']
|
expected_space_reservation = self.fake_metadata['SpaceReserved']
|
||||||
self.mock_object(self.client, 'get_ontap_version',
|
self.mock_object(self.client, 'get_ontap_version',
|
||||||
return_value=ontap_version)
|
return_value=ontap_version)
|
||||||
|
@ -237,9 +270,11 @@ class NetAppBaseClientTestCase(test.TestCase):
|
||||||
mock_create_node.assert_called_with(
|
mock_create_node.assert_called_with(
|
||||||
'lun-create-by-size',
|
'lun-create-by-size',
|
||||||
**{'path': expected_path, 'size': initial_size,
|
**{'path': expected_path, 'size': initial_size,
|
||||||
'ostype': self.fake_metadata['OsType'],
|
'ostype': self.fake_metadata['OsType'],
|
||||||
'space-reservation-enabled':
|
'space-reservation-enabled':
|
||||||
expected_space_reservation})
|
expected_space_reservation,
|
||||||
|
'space-allocation-enabled':
|
||||||
|
space_alloc})
|
||||||
mock_request.add_new_child.assert_called_with(
|
mock_request.add_new_child.assert_called_with(
|
||||||
'qos-policy-group', expected_qos_group_name)
|
'qos-policy-group', expected_qos_group_name)
|
||||||
self.connection.invoke_successfully.assert_called_with(
|
self.connection.invoke_successfully.assert_called_with(
|
||||||
|
|
|
@ -854,6 +854,7 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
|
||||||
'uuid': fake.UUID1,
|
'uuid': fake.UUID1,
|
||||||
'fields': 'svm.name,location.volume.name,space.size,'
|
'fields': 'svm.name,location.volume.name,space.size,'
|
||||||
'location.qtree.name,name,os_type,'
|
'location.qtree.name,name,os_type,'
|
||||||
|
'space.scsi_thin_provisioning_support_enabled,'
|
||||||
'space.guarantee.requested,uuid'
|
'space.guarantee.requested,uuid'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,6 +885,7 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
|
||||||
'name': path,
|
'name': path,
|
||||||
'fields': 'svm.name,location.volume.name,space.size,'
|
'fields': 'svm.name,location.volume.name,space.size,'
|
||||||
'location.qtree.name,name,os_type,'
|
'location.qtree.name,name,os_type,'
|
||||||
|
'space.scsi_thin_provisioning_support_enabled,'
|
||||||
'space.guarantee.requested,uuid'
|
'space.guarantee.requested,uuid'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1678,6 +1680,8 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
|
||||||
'space.size': str(initial_size),
|
'space.size': str(initial_size),
|
||||||
'os_type': metadata['OsType'],
|
'os_type': metadata['OsType'],
|
||||||
'space.guarantee.requested': metadata['SpaceReserved'],
|
'space.guarantee.requested': metadata['SpaceReserved'],
|
||||||
|
'space.scsi_thin_provisioning_support_enabled':
|
||||||
|
metadata['SpaceAllocated'],
|
||||||
'qos_policy.name': fake.QOS_POLICY_GROUP_NAME
|
'qos_policy.name': fake.QOS_POLICY_GROUP_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,10 +64,19 @@ JOB_UUID = 'fb132b04-6422-43ce-9451-ee819f0131a4'
|
||||||
LUN_METADATA = {
|
LUN_METADATA = {
|
||||||
'OsType': None,
|
'OsType': None,
|
||||||
'SpaceReserved': 'true',
|
'SpaceReserved': 'true',
|
||||||
|
'SpaceAllocated': 'false',
|
||||||
'Path': PATH,
|
'Path': PATH,
|
||||||
'Qtree': None,
|
'Qtree': None,
|
||||||
'Volume': POOL_NAME,
|
'Volume': POOL_NAME,
|
||||||
}
|
}
|
||||||
|
LUN_METADATA_WITH_SPACE_ALLOCATION = {
|
||||||
|
'OsType': None,
|
||||||
|
'SpaceReserved': 'true',
|
||||||
|
'Path': PATH,
|
||||||
|
'SpaceAllocated': 'true',
|
||||||
|
'Qtree': None,
|
||||||
|
'Volume': POOL_NAME,
|
||||||
|
}
|
||||||
NAMESPACE_METADATA = {
|
NAMESPACE_METADATA = {
|
||||||
'OsType': None,
|
'OsType': None,
|
||||||
'Path': PATH_NAMESPACE,
|
'Path': PATH_NAMESPACE,
|
||||||
|
@ -239,6 +248,7 @@ ISCSI_VOLUME = {
|
||||||
'name': 'fake_volume',
|
'name': 'fake_volume',
|
||||||
'id': 'fake_id',
|
'id': 'fake_id',
|
||||||
'provider_auth': 'fake provider auth',
|
'provider_auth': 'fake provider auth',
|
||||||
|
'provider_location': 'iscsi:/dummy_path'
|
||||||
}
|
}
|
||||||
|
|
||||||
ISCSI_LUN = {'name': ISCSI_VOLUME, 'lun_id': 42}
|
ISCSI_LUN = {'name': ISCSI_VOLUME, 'lun_id': 42}
|
||||||
|
@ -250,6 +260,7 @@ ISCSI_CONNECTION_PROPERTIES = {
|
||||||
'auth_method': 'fake_method',
|
'auth_method': 'fake_method',
|
||||||
'auth_password': 'auth',
|
'auth_password': 'auth',
|
||||||
'auth_username': 'provider',
|
'auth_username': 'provider',
|
||||||
|
'discard': True,
|
||||||
'discovery_auth_method': 'fake_method',
|
'discovery_auth_method': 'fake_method',
|
||||||
'discovery_auth_username': 'provider',
|
'discovery_auth_username': 'provider',
|
||||||
'discovery_auth_password': 'auth',
|
'discovery_auth_password': 'auth',
|
||||||
|
@ -289,7 +300,7 @@ IGROUP1 = {'initiator-group-os-type': 'linux',
|
||||||
'initiator-group-name': IGROUP1_NAME}
|
'initiator-group-name': IGROUP1_NAME}
|
||||||
|
|
||||||
QOS_SPECS = {}
|
QOS_SPECS = {}
|
||||||
EXTRA_SPECS = {}
|
EXTRA_SPECS = {'netapp:space_allocation': '<is> True'}
|
||||||
MAX_THROUGHPUT = '21734278B/s'
|
MAX_THROUGHPUT = '21734278B/s'
|
||||||
MIN_IOPS = '256iops'
|
MIN_IOPS = '256iops'
|
||||||
MAX_IOPS = '512iops'
|
MAX_IOPS = '512iops'
|
||||||
|
|
|
@ -136,22 +136,97 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase):
|
||||||
|
|
||||||
self.library._create_lun.assert_called_once_with(
|
self.library._create_lun.assert_called_once_with(
|
||||||
fake.POOL_NAME, fake.LUN_NAME, volume_size_in_bytes,
|
fake.POOL_NAME, fake.LUN_NAME, volume_size_in_bytes,
|
||||||
fake.LUN_METADATA, fake.QOS_POLICY_GROUP_NAME, False)
|
fake.LUN_METADATA,
|
||||||
|
fake.QOS_POLICY_GROUP_NAME, False)
|
||||||
self.library._get_volume_model_update.assert_called_once_with(
|
self.library._get_volume_model_update.assert_called_once_with(
|
||||||
fake.VOLUME)
|
fake.VOLUME)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
0, self.library. _mark_qos_policy_group_for_deletion.call_count)
|
0, self.library. _mark_qos_policy_group_for_deletion.call_count)
|
||||||
self.assertEqual(0, block_base.LOG.error.call_count)
|
self.assertEqual(0, block_base.LOG.error.call_count)
|
||||||
|
|
||||||
|
def test_create_volume_space_allocation_extra_spec_false(self):
|
||||||
|
volume_size_in_bytes = int(fake.SIZE) * units.Gi
|
||||||
|
self.mock_object(na_utils, 'get_volume_extra_specs',
|
||||||
|
return_value={
|
||||||
|
'netapp:space_allocation': '<is> False'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.mock_object(na_utils, 'log_extra_spec_warnings')
|
||||||
|
self.mock_object(block_base, 'LOG')
|
||||||
|
self.mock_object(volume_utils, 'extract_host',
|
||||||
|
return_value=fake.POOL_NAME)
|
||||||
|
self.mock_object(self.library, '_setup_qos_for_volume',
|
||||||
|
return_value=fake.QOS_POLICY_GROUP_INFO)
|
||||||
|
self.mock_object(self.library, '_create_lun')
|
||||||
|
self.mock_object(self.library, '_create_lun_handle')
|
||||||
|
self.mock_object(self.library, '_add_lun_to_table')
|
||||||
|
self.mock_object(self.library, '_mark_qos_policy_group_for_deletion')
|
||||||
|
self.mock_object(self.library, '_get_volume_model_update')
|
||||||
|
|
||||||
|
self.library.create_volume(fake.VOLUME)
|
||||||
|
|
||||||
|
self.library._create_lun.assert_called_once_with(
|
||||||
|
fake.POOL_NAME, fake.LUN_NAME, volume_size_in_bytes,
|
||||||
|
fake.LUN_METADATA,
|
||||||
|
fake.QOS_POLICY_GROUP_NAME, False)
|
||||||
|
self.library._get_volume_model_update.assert_called_once_with(
|
||||||
|
fake.VOLUME)
|
||||||
|
self.assertEqual(
|
||||||
|
0, self.library._mark_qos_policy_group_for_deletion.call_count)
|
||||||
|
self.assertEqual(0, block_base.LOG.error.call_count)
|
||||||
|
|
||||||
|
def test_create_volume_space_allocation_extra_spec_true(self):
|
||||||
|
volume_size_in_bytes = int(fake.SIZE) * units.Gi
|
||||||
|
self.mock_object(na_utils, 'get_volume_extra_specs',
|
||||||
|
return_value={
|
||||||
|
'netapp:space_allocation': '<is> True'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.mock_object(na_utils, 'log_extra_spec_warnings')
|
||||||
|
self.mock_object(block_base, 'LOG')
|
||||||
|
self.mock_object(volume_utils, 'extract_host',
|
||||||
|
return_value=fake.POOL_NAME)
|
||||||
|
self.mock_object(self.library, '_setup_qos_for_volume',
|
||||||
|
return_value=fake.QOS_POLICY_GROUP_INFO)
|
||||||
|
self.mock_object(self.library, '_create_lun')
|
||||||
|
self.mock_object(self.library, '_create_lun_handle')
|
||||||
|
self.mock_object(self.library, '_add_lun_to_table')
|
||||||
|
self.mock_object(self.library, '_mark_qos_policy_group_for_deletion')
|
||||||
|
self.mock_object(self.library, '_get_volume_model_update')
|
||||||
|
|
||||||
|
self.library.create_volume(fake.VOLUME)
|
||||||
|
|
||||||
|
self.library._create_lun.assert_called_once_with(
|
||||||
|
fake.POOL_NAME, fake.LUN_NAME, volume_size_in_bytes,
|
||||||
|
fake.LUN_METADATA_WITH_SPACE_ALLOCATION,
|
||||||
|
fake.QOS_POLICY_GROUP_NAME, False)
|
||||||
|
self.library._get_volume_model_update.assert_called_once_with(
|
||||||
|
fake.VOLUME)
|
||||||
|
self.assertEqual(
|
||||||
|
0, self.library._mark_qos_policy_group_for_deletion.call_count)
|
||||||
|
self.assertEqual(0, block_base.LOG.error.call_count)
|
||||||
|
|
||||||
def test_create_volume_no_pool(self):
|
def test_create_volume_no_pool(self):
|
||||||
self.mock_object(volume_utils, 'extract_host', return_value=None)
|
self.mock_object(volume_utils, 'extract_host', return_value=None)
|
||||||
|
|
||||||
self.assertRaises(exception.InvalidHost, self.library.create_volume,
|
self.assertRaises(exception.InvalidHost, self.library.create_volume,
|
||||||
fake.VOLUME)
|
fake.VOLUME)
|
||||||
|
|
||||||
|
def test_space_allocation_exception_path(self):
|
||||||
|
self.mock_object(block_base, 'LOG')
|
||||||
|
self.mock_object(na_utils, 'get_volume_extra_specs',
|
||||||
|
return_value={'netapp:space_allocation': 'xyz'})
|
||||||
|
self.mock_object(self.library, '_setup_qos_for_volume',
|
||||||
|
return_value=fake.QOS_POLICY_GROUP_INFO)
|
||||||
|
self.mock_object(self.library, '_create_lun', side_effect=Exception)
|
||||||
|
self.mock_object(self.library, '_mark_qos_policy_group_for_deletion')
|
||||||
|
self.assertRaises(exception.VolumeBackendAPIException,
|
||||||
|
self.library.create_volume, fake.VOLUME)
|
||||||
|
|
||||||
def test_create_volume_exception_path(self):
|
def test_create_volume_exception_path(self):
|
||||||
self.mock_object(block_base, 'LOG')
|
self.mock_object(block_base, 'LOG')
|
||||||
self.mock_object(na_utils, 'get_volume_extra_specs')
|
self.mock_object(na_utils, 'get_volume_extra_specs',
|
||||||
|
return_value={})
|
||||||
self.mock_object(self.library, '_setup_qos_for_volume',
|
self.mock_object(self.library, '_setup_qos_for_volume',
|
||||||
return_value=fake.QOS_POLICY_GROUP_INFO)
|
return_value=fake.QOS_POLICY_GROUP_INFO)
|
||||||
self.mock_object(self.library, '_create_lun', side_effect=Exception)
|
self.mock_object(self.library, '_create_lun', side_effect=Exception)
|
||||||
|
@ -767,6 +842,9 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase):
|
||||||
self.mock_object(block_base.NetAppBlockStorageLibrary,
|
self.mock_object(block_base.NetAppBlockStorageLibrary,
|
||||||
'_get_targets_from_list',
|
'_get_targets_from_list',
|
||||||
return_value=target_details_list)
|
return_value=target_details_list)
|
||||||
|
self.mock_object(block_base.NetAppBlockStorageLibrary,
|
||||||
|
'_is_space_alloc_enabled',
|
||||||
|
return_value=True)
|
||||||
self.zapi_client.get_iscsi_service_details.return_value = (
|
self.zapi_client.get_iscsi_service_details.return_value = (
|
||||||
fake.ISCSI_SERVICE_IQN)
|
fake.ISCSI_SERVICE_IQN)
|
||||||
self.mock_object(na_utils,
|
self.mock_object(na_utils,
|
||||||
|
@ -796,6 +874,10 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase):
|
||||||
fake.ISCSI_CONNECTION_PROPERTIES['data']
|
fake.ISCSI_CONNECTION_PROPERTIES['data']
|
||||||
['discovery_auth_username'],
|
['discovery_auth_username'],
|
||||||
target_info['data']['discovery_auth_username'])
|
target_info['data']['discovery_auth_username'])
|
||||||
|
self.assertEqual(
|
||||||
|
fake.ISCSI_CONNECTION_PROPERTIES['data']
|
||||||
|
['discard'],
|
||||||
|
target_info['data']['discard'])
|
||||||
|
|
||||||
self.assertEqual(fake.ISCSI_CONNECTION_PROPERTIES, target_info)
|
self.assertEqual(fake.ISCSI_CONNECTION_PROPERTIES, target_info)
|
||||||
block_base.NetAppBlockStorageLibrary._map_lun.assert_called_once_with(
|
block_base.NetAppBlockStorageLibrary._map_lun.assert_called_once_with(
|
||||||
|
@ -815,6 +897,9 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase):
|
||||||
self.zapi_client.get_iscsi_target_details.return_value = None
|
self.zapi_client.get_iscsi_target_details.return_value = None
|
||||||
self.mock_object(block_base.NetAppBlockStorageLibrary,
|
self.mock_object(block_base.NetAppBlockStorageLibrary,
|
||||||
'_get_targets_from_list')
|
'_get_targets_from_list')
|
||||||
|
self.mock_object(block_base.NetAppBlockStorageLibrary,
|
||||||
|
'_is_space_alloc_enabled',
|
||||||
|
return_value=True)
|
||||||
self.mock_object(na_utils,
|
self.mock_object(na_utils,
|
||||||
'get_iscsi_connection_properties',
|
'get_iscsi_connection_properties',
|
||||||
return_value=fake.ISCSI_CONNECTION_PROPERTIES)
|
return_value=fake.ISCSI_CONNECTION_PROPERTIES)
|
||||||
|
@ -826,6 +911,9 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
0, block_base.NetAppBlockStorageLibrary
|
0, block_base.NetAppBlockStorageLibrary
|
||||||
._get_targets_from_list.call_count)
|
._get_targets_from_list.call_count)
|
||||||
|
self.assertEqual(
|
||||||
|
0, block_base.NetAppBlockStorageLibrary
|
||||||
|
._is_space_alloc_enabled.call_count)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
0, self.zapi_client.get_iscsi_service_details.call_count)
|
0, self.zapi_client.get_iscsi_service_details.call_count)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -840,6 +928,9 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase):
|
||||||
self.mock_object(block_base.NetAppBlockStorageLibrary,
|
self.mock_object(block_base.NetAppBlockStorageLibrary,
|
||||||
'_get_targets_from_list',
|
'_get_targets_from_list',
|
||||||
return_value=None)
|
return_value=None)
|
||||||
|
self.mock_object(block_base.NetAppBlockStorageLibrary,
|
||||||
|
'_is_space_alloc_enabled',
|
||||||
|
return_value=True)
|
||||||
self.mock_object(na_utils, 'get_iscsi_connection_properties')
|
self.mock_object(na_utils, 'get_iscsi_connection_properties')
|
||||||
|
|
||||||
self.assertRaises(exception.VolumeBackendAPIException,
|
self.assertRaises(exception.VolumeBackendAPIException,
|
||||||
|
@ -848,6 +939,9 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase):
|
||||||
|
|
||||||
self.assertEqual(0, self.zapi_client
|
self.assertEqual(0, self.zapi_client
|
||||||
.get_iscsi_service_details.call_count)
|
.get_iscsi_service_details.call_count)
|
||||||
|
self.assertEqual(0,
|
||||||
|
block_base.NetAppBlockStorageLibrary
|
||||||
|
._is_space_alloc_enabled.call_count)
|
||||||
self.assertEqual(0, na_utils.get_iscsi_connection_properties
|
self.assertEqual(0, na_utils.get_iscsi_connection_properties
|
||||||
.call_count)
|
.call_count)
|
||||||
|
|
||||||
|
|
|
@ -224,12 +224,17 @@ class NetAppBlockStorageLibrary(object):
|
||||||
|
|
||||||
extra_specs = na_utils.get_volume_extra_specs(volume)
|
extra_specs = na_utils.get_volume_extra_specs(volume)
|
||||||
|
|
||||||
|
space_allocation = volume_utils.is_boolean_str(
|
||||||
|
extra_specs.get('netapp:space_allocation')
|
||||||
|
)
|
||||||
|
LOG.debug('create_volume space_allocation %r', space_allocation)
|
||||||
lun_name = volume['name']
|
lun_name = volume['name']
|
||||||
|
|
||||||
size = int(volume['size']) * units.Gi
|
size = int(volume['size']) * units.Gi
|
||||||
|
|
||||||
metadata = {'OsType': self.lun_ostype,
|
metadata = {'OsType': self.lun_ostype,
|
||||||
'SpaceReserved': self.lun_space_reservation,
|
'SpaceReserved': self.lun_space_reservation,
|
||||||
|
'SpaceAllocated': str(space_allocation).lower(),
|
||||||
'Path': '/vol/%s/%s' % (pool_name, lun_name)}
|
'Path': '/vol/%s/%s' % (pool_name, lun_name)}
|
||||||
|
|
||||||
qos_policy_group_info = self._setup_qos_for_volume(volume, extra_specs)
|
qos_policy_group_info = self._setup_qos_for_volume(volume, extra_specs)
|
||||||
|
@ -712,6 +717,16 @@ class NetAppBlockStorageLibrary(object):
|
||||||
" post clone resize LUN %s.", seg[-1])
|
" post clone resize LUN %s.", seg[-1])
|
||||||
LOG.error("Exception details: %s", e)
|
LOG.error("Exception details: %s", e)
|
||||||
|
|
||||||
|
def _is_space_alloc_enabled(self, path):
|
||||||
|
"""Gets space allocation details for the LUN."""
|
||||||
|
LOG.debug("Getting LUN space allocation enabled.")
|
||||||
|
lun_infos = self.zapi_client.get_lun_by_args(path=path)
|
||||||
|
if not lun_infos:
|
||||||
|
seg = path.split('/')
|
||||||
|
msg = _('Failure getting LUN info for %s' % seg[-1])
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
return lun_infos[0]['SpaceAllocated'] == "true"
|
||||||
|
|
||||||
def _get_lun_block_count(self, path):
|
def _get_lun_block_count(self, path):
|
||||||
"""Gets block counts for the LUN."""
|
"""Gets block counts for the LUN."""
|
||||||
LOG.debug("Getting LUN block count.")
|
LOG.debug("Getting LUN block count.")
|
||||||
|
@ -844,6 +859,7 @@ class NetAppBlockStorageLibrary(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
initiator_name = connector['initiator']
|
initiator_name = connector['initiator']
|
||||||
|
lun_path = volume['provider_location'].split(':')[1]
|
||||||
name = volume['name']
|
name = volume['name']
|
||||||
lun_id = self._map_lun(name, [initiator_name], 'iscsi', None)
|
lun_id = self._map_lun(name, [initiator_name], 'iscsi', None)
|
||||||
|
|
||||||
|
@ -874,7 +890,7 @@ class NetAppBlockStorageLibrary(object):
|
||||||
properties = na_utils.get_iscsi_connection_properties(lun_id, volume,
|
properties = na_utils.get_iscsi_connection_properties(lun_id, volume,
|
||||||
iqn, addresses,
|
iqn, addresses,
|
||||||
ports)
|
ports)
|
||||||
|
properties['discard'] = self._is_space_alloc_enabled(lun_path)
|
||||||
if self.configuration.use_chap_auth:
|
if self.configuration.use_chap_auth:
|
||||||
chap_username, chap_password = self._configure_chap(initiator_name)
|
chap_username, chap_password = self._configure_chap(initiator_name)
|
||||||
self._add_chap_properties(properties, chap_username, chap_password)
|
self._add_chap_properties(properties, chap_username, chap_password)
|
||||||
|
|
|
@ -130,6 +130,10 @@ class Client(object):
|
||||||
params = {'path': path, 'size': str(initial_size),
|
params = {'path': path, 'size': str(initial_size),
|
||||||
'ostype': metadata['OsType'],
|
'ostype': metadata['OsType'],
|
||||||
'space-reservation-enabled': space_reservation}
|
'space-reservation-enabled': space_reservation}
|
||||||
|
|
||||||
|
if "SpaceAllocated" in metadata:
|
||||||
|
params['space-allocation-enabled'] = metadata['SpaceAllocated']
|
||||||
|
|
||||||
version = self.get_ontapi_version()
|
version = self.get_ontapi_version()
|
||||||
if version >= (1, 110):
|
if version >= (1, 110):
|
||||||
params['use-exact-size'] = 'true'
|
params['use-exact-size'] = 'true'
|
||||||
|
|
|
@ -545,6 +545,8 @@ class Client(client_base.Client):
|
||||||
meta_dict['OsType'] = lun.get_child_content('multiprotocol-type')
|
meta_dict['OsType'] = lun.get_child_content('multiprotocol-type')
|
||||||
meta_dict['SpaceReserved'] = \
|
meta_dict['SpaceReserved'] = \
|
||||||
lun.get_child_content('is-space-reservation-enabled')
|
lun.get_child_content('is-space-reservation-enabled')
|
||||||
|
meta_dict['SpaceAllocated'] = \
|
||||||
|
lun.get_child_content('is-space-alloc-enabled')
|
||||||
meta_dict['UUID'] = lun.get_child_content('uuid')
|
meta_dict['UUID'] = lun.get_child_content('uuid')
|
||||||
meta_dict['BlockSize'] = lun.get_child_content('block-size')
|
meta_dict['BlockSize'] = lun.get_child_content('block-size')
|
||||||
return meta_dict
|
return meta_dict
|
||||||
|
|
|
@ -663,6 +663,7 @@ class RestClient(object):
|
||||||
'svm.name': self.vserver,
|
'svm.name': self.vserver,
|
||||||
'fields': 'svm.name,location.volume.name,space.size,'
|
'fields': 'svm.name,location.volume.name,space.size,'
|
||||||
'location.qtree.name,name,os_type,'
|
'location.qtree.name,name,os_type,'
|
||||||
|
'space.scsi_thin_provisioning_support_enabled,'
|
||||||
'space.guarantee.requested,uuid'
|
'space.guarantee.requested,uuid'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,6 +684,8 @@ class RestClient(object):
|
||||||
lun_info['Path'] = lun['name']
|
lun_info['Path'] = lun['name']
|
||||||
lun_info['OsType'] = lun['os_type']
|
lun_info['OsType'] = lun['os_type']
|
||||||
lun_info['SpaceReserved'] = lun['space']['guarantee']['requested']
|
lun_info['SpaceReserved'] = lun['space']['guarantee']['requested']
|
||||||
|
lun_info['SpaceAllocated'] = \
|
||||||
|
lun['space']['scsi_thin_provisioning_support_enabled']
|
||||||
lun_info['UUID'] = lun['uuid']
|
lun_info['UUID'] = lun['uuid']
|
||||||
|
|
||||||
lun_list.append(lun_info)
|
lun_list.append(lun_info)
|
||||||
|
@ -695,6 +698,7 @@ class RestClient(object):
|
||||||
query = {
|
query = {
|
||||||
'fields': 'svm.name,location.volume.name,space.size,'
|
'fields': 'svm.name,location.volume.name,space.size,'
|
||||||
'location.qtree.name,name,os_type,'
|
'location.qtree.name,name,os_type,'
|
||||||
|
'space.scsi_thin_provisioning_support_enabled,'
|
||||||
'space.guarantee.requested,uuid'
|
'space.guarantee.requested,uuid'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -723,6 +727,8 @@ class RestClient(object):
|
||||||
lun_info['Path'] = lun['name']
|
lun_info['Path'] = lun['name']
|
||||||
lun_info['OsType'] = lun['os_type']
|
lun_info['OsType'] = lun['os_type']
|
||||||
lun_info['SpaceReserved'] = lun['space']['guarantee']['requested']
|
lun_info['SpaceReserved'] = lun['space']['guarantee']['requested']
|
||||||
|
lun_info['SpaceAllocated'] = \
|
||||||
|
lun['space']['scsi_thin_provisioning_support_enabled']
|
||||||
lun_info['UUID'] = lun['uuid']
|
lun_info['UUID'] = lun['uuid']
|
||||||
|
|
||||||
# NOTE(nahimsouza): Currently, ONTAP REST API does not have the
|
# NOTE(nahimsouza): Currently, ONTAP REST API does not have the
|
||||||
|
@ -1305,13 +1311,15 @@ class RestClient(object):
|
||||||
|
|
||||||
path = f'/vol/{volume_name}/{lun_name}'
|
path = f'/vol/{volume_name}/{lun_name}'
|
||||||
space_reservation = metadata['SpaceReserved']
|
space_reservation = metadata['SpaceReserved']
|
||||||
|
space_allocation = metadata['SpaceAllocated']
|
||||||
initial_size = size
|
initial_size = size
|
||||||
|
|
||||||
body = {
|
body = {
|
||||||
'name': path,
|
'name': path,
|
||||||
'space.size': str(initial_size),
|
'space.size': str(initial_size),
|
||||||
'os_type': metadata['OsType'],
|
'os_type': metadata['OsType'],
|
||||||
'space.guarantee.requested': space_reservation
|
'space.guarantee.requested': space_reservation,
|
||||||
|
'space.scsi_thin_provisioning_support_enabled': space_allocation
|
||||||
}
|
}
|
||||||
|
|
||||||
if qos_policy_group_name:
|
if qos_policy_group_name:
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
NetApp iSCSI/FCP drivers: NetApp space allocation feature allows ONTAP
|
||||||
|
and host to see the actual space correctly when host deletes data.
|
||||||
|
It also notifies the host when the LUN cannot accept write data due
|
||||||
|
to lack of space on the volume, and makes the LUN read-only
|
||||||
|
(rather than going offline). This feature can be enabled or
|
||||||
|
disabled on cinder volumes by using volume type extra specs with
|
||||||
|
the ``netapp:space_allocation`` property.
|
Loading…
Reference in New Issue