NetApp ONTAP: Add core functions on REST client
This patch contains the implementation of the core functions of the NetApp drivers (NFS, iSCSI and FCP). The functions were migrated from ZAPI to REST API, but ZAPI client was not removed since it is being used as a fallback mechamism when a function does not have an equivalent in the REST API. In summary, the features implemented in this patch are related to: > Periodic tasks - methods used during driver initialization and executed periodically to get information about the volumes, performance metrics and stats > Basic volume operations - methods used to create, delete, attach, detach and extend volumes Co-authored-by: Fábio Oliveira <fabioaurelio1269@gmail.com> Co-authored-by: Fernando Ferraz <sfernand@netapp.com> Co-authored-by: Luisa Amaral <luisaa@netapp.com> Co-authored-by: Matheus Andrade <matheus.handrade15@gmail.com> Co-authored-by: Vinícius Angiolucci Reis <angiolucci@gmail.com> Change-Id: I67eb7f6264cf5eea94c0a364082b530b9cdc1ae3 partially-implements: blueprint netapp-ontap-rest-api-client
This commit is contained in:
parent
4775ca9370
commit
00481aed74
File diff suppressed because it is too large
Load Diff
@ -1441,6 +1441,7 @@ class NetAppCmodeClientTestCase(test.TestCase):
|
||||
<num-records>1</num-records>
|
||||
<attributes-list>
|
||||
<net-interface-info>
|
||||
<vserver>fake_vserver</vserver>
|
||||
</net-interface-info>
|
||||
</attributes-list>
|
||||
</results>"""))
|
||||
@ -4494,3 +4495,46 @@ class NetAppCmodeClientTestCase(test.TestCase):
|
||||
}
|
||||
self.client.connection.send_request.assert_called_once_with(
|
||||
'file-rename-file', api_args)
|
||||
|
||||
def test_check_api_permissions(self):
|
||||
|
||||
mock_log = self.mock_object(client_cmode.LOG, 'warning')
|
||||
self.mock_object(self.client, 'check_cluster_api', return_value=True)
|
||||
|
||||
self.client.check_api_permissions()
|
||||
|
||||
self.client.check_cluster_api.assert_has_calls(
|
||||
[mock.call(*key) for key in client_cmode.SSC_API_MAP.keys()])
|
||||
self.assertEqual(0, mock_log.call_count)
|
||||
|
||||
def test_check_api_permissions_failed_ssc_apis(self):
|
||||
|
||||
def check_cluster_api(object_name, operation_name, api):
|
||||
if api != 'volume-get-iter':
|
||||
return False
|
||||
return True
|
||||
|
||||
self.mock_object(self.client, 'check_cluster_api',
|
||||
side_effect=check_cluster_api)
|
||||
|
||||
mock_log = self.mock_object(client_cmode.LOG, 'warning')
|
||||
|
||||
self.client.check_api_permissions()
|
||||
|
||||
self.assertEqual(1, mock_log.call_count)
|
||||
|
||||
def test_check_api_permissions_failed_volume_api(self):
|
||||
|
||||
def check_cluster_api(object_name, operation_name, api):
|
||||
if api == 'volume-get-iter':
|
||||
return False
|
||||
return True
|
||||
|
||||
self.mock_object(self.client, 'check_cluster_api',
|
||||
side_effect=check_cluster_api)
|
||||
mock_log = self.mock_object(client_cmode.LOG, 'warning')
|
||||
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.client.check_api_permissions)
|
||||
|
||||
self.assertEqual(0, mock_log.call_count)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -275,8 +275,9 @@ IGROUP1 = {'initiator-group-os-type': 'linux',
|
||||
QOS_SPECS = {}
|
||||
EXTRA_SPECS = {}
|
||||
MAX_THROUGHPUT = '21734278B/s'
|
||||
MIN_IOPS = '256IOPS'
|
||||
MAX_IOPS = '512IOPS'
|
||||
MIN_IOPS = '256iops'
|
||||
MAX_IOPS = '512iops'
|
||||
MAX_BPS = '1000000B/s'
|
||||
QOS_POLICY_GROUP_NAME = 'fake_qos_policy_group_name'
|
||||
|
||||
QOS_POLICY_GROUP_INFO_LEGACY = {
|
||||
@ -290,6 +291,11 @@ QOS_POLICY_GROUP_SPEC = {
|
||||
'policy_name': QOS_POLICY_GROUP_NAME,
|
||||
}
|
||||
|
||||
QOS_POLICY_GROUP_SPEC_BPS = {
|
||||
'max_throughput': MAX_BPS,
|
||||
'policy_name': QOS_POLICY_GROUP_NAME,
|
||||
}
|
||||
|
||||
QOS_POLICY_GROUP_SPEC_MAX = {
|
||||
'max_throughput': MAX_THROUGHPUT,
|
||||
'policy_name': QOS_POLICY_GROUP_NAME,
|
||||
@ -417,6 +423,19 @@ FAKE_LUN = netapp_api.NaElement.create_node_with_children(
|
||||
'volume': 'fakeLUN',
|
||||
'vserver': 'fake_vserver'})
|
||||
|
||||
FAKE_LUN_GET_ITER_RESULT = [
|
||||
{
|
||||
'Vserver': 'fake_vserver',
|
||||
'Volume': 'fake_volume',
|
||||
'Size': 123,
|
||||
'Qtree': 'fake_qtree',
|
||||
'Path': 'fake_path',
|
||||
'OsType': 'fake_os',
|
||||
'SpaceReserved': 'true',
|
||||
'UUID': 'fake-uuid',
|
||||
},
|
||||
]
|
||||
|
||||
CG_VOLUME_NAME = 'fake_cg_volume'
|
||||
CG_GROUP_NAME = 'fake_consistency_group'
|
||||
CG_POOL_NAME = 'cdot'
|
||||
@ -740,12 +759,219 @@ def get_fake_net_interface_get_iter_response():
|
||||
|
||||
|
||||
def get_fake_ifs():
|
||||
list_of_ifs = [
|
||||
etree.XML("""<net-interface-info>
|
||||
<address>FAKE_IP</address></net-interface-info>"""),
|
||||
etree.XML("""<net-interface-info>
|
||||
<address>FAKE_IP2</address></net-interface-info>"""),
|
||||
etree.XML("""<net-interface-info>
|
||||
<address>FAKE_IP3</address></net-interface-info>"""),
|
||||
]
|
||||
return [netapp_api.NaElement(el) for el in list_of_ifs]
|
||||
return [{'vserver': VSERVER_NAME}]
|
||||
|
||||
|
||||
AFF_SYSTEM_NODE_GET_ITER_RESPONSE_REST = {
|
||||
"records": [
|
||||
{
|
||||
"uuid": "9eff6c76-fc13-11ea-8799-525400",
|
||||
"name": "aff-node1",
|
||||
"model": "AFFA400",
|
||||
"is_all_flash_optimized": True,
|
||||
"is_all_flash_select_optimized": False,
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/api/cluster/nodes/9eff6c76-fc13-11ea-8799-525400"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uuid": "9eff6c76-fc13-11ea-8799-52540006bba9",
|
||||
"name": "aff-node2",
|
||||
"model": "AFFA400",
|
||||
"is_all_flash_optimized": True,
|
||||
"is_all_flash_select_optimized": False,
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/api/cluster/nodes/9eff6c76-fc13-11ea-8799-525400"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"num_records": 2,
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/api/cluster/nodes?fields=model,name,"
|
||||
"is_all_flash_optimized,is_all_flash_select_optimized"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FAS_SYSTEM_NODE_GET_ITER_RESPONSE_REST = {
|
||||
"records": [
|
||||
{
|
||||
"uuid": "9eff6c76-fc13-11ea-8799-52540006bba9",
|
||||
"name": "fas-node1",
|
||||
"model": "FAS2554",
|
||||
"is_all_flash_optimized": False,
|
||||
"is_all_flash_select_optimized": False,
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/api/cluster/nodes/9eff6c76-fc13-11ea-8799-525400"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uuid": "9eff6c76-fc13-11ea-8799-52540006bba9",
|
||||
"name": "fas-node2",
|
||||
"model": "FAS2554",
|
||||
"is_all_flash_optimized": False,
|
||||
"is_all_flash_select_optimized": False,
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/api/cluster/nodes/9eff6c76-fc13-11ea-8799-525400"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"num_records": 2,
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/api/cluster/nodes?fields=model,name,"
|
||||
"is_all_flash_optimized,is_all_flash_select_optimized"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HYBRID_SYSTEM_NODE_GET_ITER_RESPONSE_REST = {
|
||||
"records": [
|
||||
{
|
||||
"uuid": "9eff6c76-fc13-11ea-8799-52540006bba9",
|
||||
"name": "select-node",
|
||||
"model": "FDvM300",
|
||||
"is_all_flash_optimized": False,
|
||||
"is_all_flash_select_optimized": True,
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/api/cluster/nodes/9eff6c76-fc13-11ea-8799-525400"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uuid": "9eff6c76-fc13-11ea-8799-52540006bba9",
|
||||
"name": "c190-node",
|
||||
"model": "AFF-C190",
|
||||
"is_all_flash_optimized": True,
|
||||
"is_all_flash_select_optimized": False,
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/api/cluster/nodes/9eff6c76-fc13-11ea-8799-525400"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"num_records": 2,
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/api/cluster/nodes?fields=model,name,"
|
||||
"is_all_flash_optimized,is_all_flash_select_optimized"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QOS_POLICY_BY_NAME_RESPONSE_REST = {
|
||||
"records": [
|
||||
{
|
||||
"uuid": "9eff6c76-fc13-11ea-8799-52540006bba9",
|
||||
"name": "openstack-cd-uuid",
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/api/storage/qos/policies/"
|
||||
"9eff6c76-fc13-11ea-8799-52540006bba9"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"num_records": 1,
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/api/storage/qos/policies?fields=name"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QOS_SPECS_REST = {}
|
||||
MAX_THROUGHPUT_REST = '21734278'
|
||||
MIN_IOPS_REST = '256'
|
||||
MAX_IOPS_REST = '512'
|
||||
MAX_BPS_REST = '1'
|
||||
|
||||
QOS_POLICY_GROUP_INFO_LEGACY_REST = {
|
||||
'legacy': 'legacy-' + QOS_POLICY_GROUP_NAME,
|
||||
'spec': None,
|
||||
}
|
||||
|
||||
QOS_POLICY_GROUP_SPEC_REST = {
|
||||
'min_throughput': MIN_IOPS_REST,
|
||||
'max_throughput': MAX_IOPS_REST,
|
||||
'policy_name': QOS_POLICY_GROUP_NAME,
|
||||
}
|
||||
|
||||
QOS_POLICY_GROUP_API_ARGS_REST = {
|
||||
'name': QOS_POLICY_GROUP_NAME,
|
||||
'svm': {
|
||||
'name': VSERVER_NAME
|
||||
},
|
||||
'fixed': {
|
||||
'max_throughput_iops': int(MAX_IOPS_REST),
|
||||
'min_throughput_iops': int(MIN_IOPS_REST)
|
||||
}
|
||||
}
|
||||
|
||||
QOS_POLICY_GROUP_API_ARGS_REST_BPS = {
|
||||
'name': QOS_POLICY_GROUP_NAME,
|
||||
'svm': {
|
||||
'name': VSERVER_NAME
|
||||
},
|
||||
'fixed': {
|
||||
'max_throughput_mbps': int(MAX_BPS_REST),
|
||||
}
|
||||
}
|
||||
|
||||
QOS_POLICY_GROUP_SPEC_MAX_REST = {
|
||||
'max_throughput': MAX_THROUGHPUT_REST,
|
||||
'policy_name': QOS_POLICY_GROUP_NAME,
|
||||
}
|
||||
|
||||
EXPECTED_IOPS_PER_GB_REST = '128'
|
||||
PEAK_IOPS_PER_GB_REST = '512'
|
||||
PEAK_IOPS_ALLOCATION_REST = 'used-space'
|
||||
EXPECTED_IOPS_ALLOCATION_REST = 'used-space'
|
||||
ABSOLUTE_MIN_IOPS_REST = '75'
|
||||
BLOCK_SIZE_REST = 'ANY'
|
||||
ADAPTIVE_QOS_SPEC_REST = {
|
||||
'policy_name': QOS_POLICY_GROUP_NAME,
|
||||
'expected_iops': EXPECTED_IOPS_PER_GB_REST,
|
||||
'expected_iops_allocation': EXPECTED_IOPS_ALLOCATION_REST,
|
||||
'peak_iops': PEAK_IOPS_PER_GB_REST,
|
||||
'peak_iops_allocation': PEAK_IOPS_ALLOCATION_REST,
|
||||
'absolute_min_iops': ABSOLUTE_MIN_IOPS_REST,
|
||||
'block_size': BLOCK_SIZE_REST,
|
||||
}
|
||||
|
||||
ADAPTIVE_QOS_API_ARGS_REST = {
|
||||
'name': QOS_POLICY_GROUP_NAME,
|
||||
'svm': {
|
||||
'name': VSERVER_NAME
|
||||
},
|
||||
'adaptive': {
|
||||
'absolute_min_iops': int(ABSOLUTE_MIN_IOPS_REST),
|
||||
'expected_iops': int(EXPECTED_IOPS_PER_GB_REST),
|
||||
'expected_iops_allocation': EXPECTED_IOPS_ALLOCATION_REST,
|
||||
'peak_iops': int(PEAK_IOPS_PER_GB_REST),
|
||||
'peak_iops_allocation': PEAK_IOPS_ALLOCATION_REST,
|
||||
'block_size': BLOCK_SIZE_REST,
|
||||
}
|
||||
}
|
||||
|
||||
QOS_POLICY_GROUP_INFO_REST = {
|
||||
'legacy': None, 'spec': QOS_POLICY_GROUP_SPEC_REST}
|
||||
QOS_POLICY_GROUP_INFO_MAX_REST = {
|
||||
'legacy': None, 'spec': QOS_POLICY_GROUP_SPEC_MAX_REST}
|
||||
ADAPTIVE_QOS_POLICY_GROUP_INFO_REST = {
|
||||
'legacy': None,
|
||||
'spec': ADAPTIVE_QOS_SPEC_REST,
|
||||
}
|
||||
|
||||
REST_FIELDS = 'uuid,name,style'
|
||||
|
@ -19,7 +19,6 @@
|
||||
from unittest import mock
|
||||
|
||||
import ddt
|
||||
import six
|
||||
|
||||
from cinder import exception
|
||||
from cinder.objects import fields
|
||||
@ -279,10 +278,8 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
|
||||
self.library._get_lun_attr = mock.Mock(return_value={'Volume':
|
||||
'fakeLUN'})
|
||||
self.library.zapi_client = mock.Mock()
|
||||
self.library.zapi_client.get_lun_by_args.return_value = [
|
||||
mock.Mock(spec=netapp_api.NaElement)]
|
||||
lun = fake.FAKE_LUN
|
||||
self.library._get_lun_by_args = mock.Mock(return_value=[lun])
|
||||
lun = fake.FAKE_LUN_GET_ITER_RESULT
|
||||
self.library.zapi_client.get_lun_by_args.return_value = lun
|
||||
self.library._add_lun_to_table = mock.Mock()
|
||||
|
||||
self.library._clone_lun('fakeLUN', 'newFakeLUN', 'false')
|
||||
@ -302,10 +299,8 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
|
||||
self.library._get_lun_attr = mock.Mock(return_value={'Volume':
|
||||
'fakeLUN'})
|
||||
self.library.zapi_client = mock.Mock()
|
||||
self.library.zapi_client.get_lun_by_args.return_value = [
|
||||
mock.Mock(spec=netapp_api.NaElement)]
|
||||
lun = fake.FAKE_LUN
|
||||
self.library._get_lun_by_args = mock.Mock(return_value=[lun])
|
||||
lun = fake.FAKE_LUN_GET_ITER_RESULT
|
||||
self.library.zapi_client.get_lun_by_args.return_value = lun
|
||||
self.library._add_lun_to_table = mock.Mock()
|
||||
|
||||
self.library._clone_lun('fakeLUN', 'newFakeLUN', 'false',
|
||||
@ -326,10 +321,8 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
|
||||
'fakeLUN'})
|
||||
self.library.zapi_client = mock.Mock()
|
||||
self.library.lun_space_reservation = 'false'
|
||||
self.library.zapi_client.get_lun_by_args.return_value = [
|
||||
mock.Mock(spec=netapp_api.NaElement)]
|
||||
lun = fake.FAKE_LUN
|
||||
self.library._get_lun_by_args = mock.Mock(return_value=[lun])
|
||||
lun = fake.FAKE_LUN_GET_ITER_RESULT
|
||||
self.library.zapi_client.get_lun_by_args.return_value = lun
|
||||
self.library._add_lun_to_table = mock.Mock()
|
||||
|
||||
self.library._clone_lun('fakeLUN', 'newFakeLUN', is_snapshot=True)
|
||||
@ -1537,27 +1530,22 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
|
||||
fake.LUN_WITH_METADATA['metadata'])
|
||||
new_snap_name = 'new-%s' % fake.SNAPSHOT['name']
|
||||
snapshot_path = lun_obj.metadata['Path']
|
||||
flexvol_name = lun_obj.metadata['Volume']
|
||||
block_count = 40960
|
||||
|
||||
mock__get_lun_from_table = self.mock_object(
|
||||
self.library, '_get_lun_from_table', return_value=lun_obj)
|
||||
mock__get_lun_block_count = self.mock_object(
|
||||
self.library, '_get_lun_block_count', return_value=block_count)
|
||||
mock_create_lun = self.mock_object(self.library.zapi_client,
|
||||
'create_lun')
|
||||
mock__clone_lun = self.mock_object(self.library, '_clone_lun')
|
||||
|
||||
self.library._clone_snapshot(fake.SNAPSHOT['name'])
|
||||
|
||||
mock__get_lun_from_table.assert_called_once_with(fake.SNAPSHOT['name'])
|
||||
mock__get_lun_block_count.assert_called_once_with(snapshot_path)
|
||||
mock_create_lun.assert_called_once_with(flexvol_name, new_snap_name,
|
||||
six.text_type(lun_obj.size),
|
||||
lun_obj.metadata)
|
||||
mock__clone_lun.assert_called_once_with(fake.SNAPSHOT['name'],
|
||||
new_snap_name,
|
||||
block_count=block_count)
|
||||
space_reserved='false',
|
||||
is_snapshot=True)
|
||||
|
||||
def test__clone_snapshot_invalid_block_count(self):
|
||||
lun_obj = block_base.NetAppLun(fake.LUN_WITH_METADATA['handle'],
|
||||
@ -1589,8 +1577,6 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
|
||||
self.library, '_get_lun_from_table', return_value=lun_obj)
|
||||
mock__get_lun_block_count = self.mock_object(
|
||||
self.library, '_get_lun_block_count', return_value=block_count)
|
||||
mock_create_lun = self.mock_object(self.library.zapi_client,
|
||||
'create_lun')
|
||||
side_effect = exception.VolumeBackendAPIException(data='data')
|
||||
mock__clone_lun = self.mock_object(self.library, '_clone_lun',
|
||||
side_effect=side_effect)
|
||||
@ -1603,12 +1589,10 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
|
||||
|
||||
mock__get_lun_from_table.assert_called_once_with(fake.SNAPSHOT['name'])
|
||||
mock__get_lun_block_count.assert_called_once_with(snapshot_path)
|
||||
mock_create_lun.assert_called_once_with(flexvol_name, new_snap_name,
|
||||
six.text_type(lun_obj.size),
|
||||
lun_obj.metadata)
|
||||
mock__clone_lun.assert_called_once_with(fake.SNAPSHOT['name'],
|
||||
new_snap_name,
|
||||
block_count=block_count)
|
||||
space_reserved='false',
|
||||
is_snapshot=True)
|
||||
mock_destroy_lun.assert_called_once_with(new_lun_path)
|
||||
|
||||
def test__swap_luns(self):
|
||||
|
@ -452,7 +452,7 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
|
||||
|
||||
vserver = self.driver._get_vserver_for_ip('FAKE_IP')
|
||||
|
||||
self.assertIsNone(vserver)
|
||||
self.assertEqual(fake.VSERVER_NAME, vserver)
|
||||
|
||||
def test_check_for_setup_error(self):
|
||||
mock_add_looping_tasks = self.mock_object(
|
||||
@ -891,9 +891,8 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
|
||||
is_snapshot=is_snapshot)
|
||||
|
||||
def test__clone_backing_file_for_volume(self):
|
||||
body = fake.get_fake_net_interface_get_iter_response()
|
||||
self.driver.zapi_client.get_if_info_by_ip = mock.Mock(
|
||||
return_value=[netapp_api.NaElement(body)])
|
||||
return_value=[{'ip': 'fake_ip'}])
|
||||
self.driver.zapi_client.get_vol_by_junc_vserver = mock.Mock(
|
||||
return_value='nfsvol')
|
||||
self.mock_object(self.driver, '_get_export_ip_path',
|
||||
|
@ -20,7 +20,6 @@ from unittest import mock
|
||||
import ddt
|
||||
import six
|
||||
|
||||
from cinder import exception
|
||||
from cinder.tests.unit import test
|
||||
from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
|
||||
fakes as fake_client)
|
||||
@ -46,45 +45,6 @@ class CapabilitiesLibraryTestCase(test.TestCase):
|
||||
config.volume_backend_name = 'fake_backend'
|
||||
return config
|
||||
|
||||
def test_check_api_permissions(self):
|
||||
|
||||
mock_log = self.mock_object(capabilities.LOG, 'warning')
|
||||
|
||||
self.ssc_library.check_api_permissions()
|
||||
|
||||
self.zapi_client.check_cluster_api.assert_has_calls(
|
||||
[mock.call(*key) for key in capabilities.SSC_API_MAP.keys()])
|
||||
self.assertEqual(0, mock_log.call_count)
|
||||
|
||||
def test_check_api_permissions_failed_ssc_apis(self):
|
||||
|
||||
def check_cluster_api(object_name, operation_name, api):
|
||||
if api != 'volume-get-iter':
|
||||
return False
|
||||
return True
|
||||
|
||||
self.zapi_client.check_cluster_api.side_effect = check_cluster_api
|
||||
mock_log = self.mock_object(capabilities.LOG, 'warning')
|
||||
|
||||
self.ssc_library.check_api_permissions()
|
||||
|
||||
self.assertEqual(1, mock_log.call_count)
|
||||
|
||||
def test_check_api_permissions_failed_volume_api(self):
|
||||
|
||||
def check_cluster_api(object_name, operation_name, api):
|
||||
if api == 'volume-get-iter':
|
||||
return False
|
||||
return True
|
||||
|
||||
self.zapi_client.check_cluster_api.side_effect = check_cluster_api
|
||||
mock_log = self.mock_object(capabilities.LOG, 'warning')
|
||||
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.ssc_library.check_api_permissions)
|
||||
|
||||
self.assertEqual(0, mock_log.call_count)
|
||||
|
||||
def test_get_ssc(self):
|
||||
|
||||
result = self.ssc_library.get_ssc()
|
||||
|
@ -410,12 +410,11 @@ class NetAppBlockStorageLibrary(object):
|
||||
def _extract_lun_info(self, lun):
|
||||
"""Extracts the LUNs from API and populates the LUN table."""
|
||||
|
||||
meta_dict = self._create_lun_meta(lun)
|
||||
path = lun.get_child_content('path')
|
||||
path = lun['Path']
|
||||
(_rest, _splitter, name) = path.rpartition('/')
|
||||
handle = self._create_lun_handle(meta_dict)
|
||||
size = lun.get_child_content('size')
|
||||
return NetAppLun(handle, name, size, meta_dict)
|
||||
handle = self._create_lun_handle(lun)
|
||||
size = lun['Size']
|
||||
return NetAppLun(handle, name, size, lun)
|
||||
|
||||
def _extract_and_populate_luns(self, api_luns):
|
||||
"""Extracts the LUNs from API and populates the LUN table."""
|
||||
@ -547,9 +546,6 @@ class NetAppBlockStorageLibrary(object):
|
||||
LOG.error("Error getting LUN attribute. Exception: %s", e)
|
||||
return None
|
||||
|
||||
def _create_lun_meta(self, lun):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _get_fc_target_wwpns(self, include_partner=True):
|
||||
raise NotImplementedError()
|
||||
|
||||
@ -725,8 +721,8 @@ class NetAppBlockStorageLibrary(object):
|
||||
msg = _('Failure getting LUN info for %s.')
|
||||
raise exception.VolumeBackendAPIException(data=msg % seg[-1])
|
||||
lun_info = lun_infos[-1]
|
||||
bs = int(lun_info.get_child_content('block-size'))
|
||||
ls = int(lun_info.get_child_content('size'))
|
||||
bs = int(lun_info['BlockSize'])
|
||||
ls = int(lun_info['Size'])
|
||||
block_count = ls / bs
|
||||
return block_count
|
||||
|
||||
|
@ -236,27 +236,14 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary,
|
||||
if len(lun) == 0:
|
||||
msg = _("No cloned LUN named %s found on the filer")
|
||||
raise exception.VolumeBackendAPIException(data=msg % new_name)
|
||||
clone_meta = self._create_lun_meta(lun[0])
|
||||
self._add_lun_to_table(
|
||||
block_base.NetAppLun('%s:%s' % (clone_meta['Vserver'],
|
||||
clone_meta['Path']),
|
||||
new_name,
|
||||
lun[0].get_child_content('size'),
|
||||
clone_meta))
|
||||
|
||||
def _create_lun_meta(self, lun):
|
||||
"""Creates LUN metadata dictionary."""
|
||||
self.zapi_client.check_is_naelement(lun)
|
||||
meta_dict = {}
|
||||
meta_dict['Vserver'] = lun.get_child_content('vserver')
|
||||
meta_dict['Volume'] = lun.get_child_content('volume')
|
||||
meta_dict['Qtree'] = lun.get_child_content('qtree')
|
||||
meta_dict['Path'] = lun.get_child_content('path')
|
||||
meta_dict['OsType'] = lun.get_child_content('multiprotocol-type')
|
||||
meta_dict['SpaceReserved'] = \
|
||||
lun.get_child_content('is-space-reservation-enabled')
|
||||
meta_dict['UUID'] = lun.get_child_content('uuid')
|
||||
return meta_dict
|
||||
clone_lun = lun[0]
|
||||
self._add_lun_to_table(
|
||||
block_base.NetAppLun('%s:%s' % (clone_lun['Vserver'],
|
||||
clone_lun['Path']),
|
||||
new_name,
|
||||
clone_lun['Size'],
|
||||
clone_lun))
|
||||
|
||||
def _get_fc_target_wwpns(self, include_partner=True):
|
||||
return self.zapi_client.get_fc_target_wwpns()
|
||||
@ -879,8 +866,6 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary,
|
||||
LOG.info("Cloning LUN %s from snapshot %s in volume %s.", lun_name,
|
||||
snapshot_name, flexvol_name)
|
||||
|
||||
metadata = snapshot_lun.metadata
|
||||
|
||||
block_count = self._get_lun_block_count(snapshot_path)
|
||||
if block_count == 0:
|
||||
msg = _("%s cannot be reverted using clone operation"
|
||||
@ -889,12 +874,9 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary,
|
||||
|
||||
new_snap_name = "new-%s" % snapshot_name
|
||||
|
||||
self.zapi_client.create_lun(
|
||||
flexvol_name, new_snap_name,
|
||||
six.text_type(snapshot_lun.size), metadata)
|
||||
try:
|
||||
self._clone_lun(snapshot_name, new_snap_name,
|
||||
block_count=block_count)
|
||||
space_reserved='false', is_snapshot=True)
|
||||
return new_snap_name
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
|
@ -644,6 +644,13 @@ class SSHUtil(object):
|
||||
|
||||
# REST API error codes.
|
||||
REST_UNAUTHORIZED = '6'
|
||||
REST_API_NOT_FOUND = '3'
|
||||
REST_UPDATE_SNAPMIRROR_FAILED = '13303844'
|
||||
REST_ERELATION_EXISTS = '6619637'
|
||||
REST_SNAPMIRROR_IN_PROGRESS = '13303810'
|
||||
REST_UPDATE_SNAPMIRROR_FAILED = '13303844'
|
||||
REST_NO_SUCH_LUN_MAP = '5374922'
|
||||
REST_NO_SUCH_FILE = '6684674'
|
||||
|
||||
|
||||
class RestNaServer(object):
|
||||
|
@ -37,6 +37,28 @@ DEFAULT_MAX_PAGE_LENGTH = 50
|
||||
ONTAP_SELECT_MODEL = 'FDvM300'
|
||||
ONTAP_C190 = 'C190'
|
||||
|
||||
# NOTE(cknight): The keys in this map are tuples that contain arguments needed
|
||||
# for efficient use of the system-user-capability-get-iter cDOT API. The
|
||||
# values are SSC extra specs associated with the APIs listed in the keys.
|
||||
SSC_API_MAP = {
|
||||
('storage.aggregate', 'show', 'aggr-options-list-info'): [
|
||||
'netapp_raid_type',
|
||||
],
|
||||
('storage.disk', 'show', 'storage-disk-get-iter'): [
|
||||
'netapp_disk_type',
|
||||
],
|
||||
('snapmirror', 'show', 'snapmirror-get-iter'): [
|
||||
'netapp_mirrored',
|
||||
],
|
||||
('volume.efficiency', 'show', 'sis-get-iter'): [
|
||||
'netapp_dedup',
|
||||
'netapp_compression',
|
||||
],
|
||||
('volume', '*show', 'volume-get-iter'): [
|
||||
'netapp_flexvol_encryption',
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@six.add_metaclass(volume_utils.TraceWrapperMetaclass)
|
||||
class Client(client_base.Client):
|
||||
@ -182,6 +204,32 @@ class Client(client_base.Client):
|
||||
result.get_child_by_name('next-tag').set_content('')
|
||||
return result
|
||||
|
||||
def check_api_permissions(self):
|
||||
"""Check which APIs that support SSC functionality are available."""
|
||||
|
||||
inaccessible_apis = []
|
||||
invalid_extra_specs = []
|
||||
|
||||
for api_tuple, extra_specs in SSC_API_MAP.items():
|
||||
object_name, operation_name, api = api_tuple
|
||||
if not self.check_cluster_api(object_name,
|
||||
operation_name,
|
||||
api):
|
||||
inaccessible_apis.append(api)
|
||||
invalid_extra_specs.extend(extra_specs)
|
||||
|
||||
if inaccessible_apis:
|
||||
if 'volume-get-iter' in inaccessible_apis:
|
||||
msg = _('User not permitted to query Data ONTAP volumes.')
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
else:
|
||||
LOG.warning('The configured user account does not have '
|
||||
'sufficient privileges to use all needed '
|
||||
'APIs. The following extra specs will fail '
|
||||
'or be ignored: %s.', invalid_extra_specs)
|
||||
|
||||
return invalid_extra_specs
|
||||
|
||||
def _get_cluster_nodes_info(self):
|
||||
"""Return a list of models of the nodes in the cluster"""
|
||||
api_args = {
|
||||
@ -481,7 +529,25 @@ class Client(client_base.Client):
|
||||
tag = result.get_child_content('next-tag')
|
||||
if tag is None:
|
||||
break
|
||||
return luns
|
||||
|
||||
lun_list = [self._create_lun_meta(lun) for lun in luns]
|
||||
return lun_list
|
||||
|
||||
def _create_lun_meta(self, lun):
|
||||
"""Creates LUN metadata dictionary."""
|
||||
self.check_is_naelement(lun)
|
||||
meta_dict = {}
|
||||
meta_dict['Vserver'] = lun.get_child_content('vserver')
|
||||
meta_dict['Volume'] = lun.get_child_content('volume')
|
||||
meta_dict['Size'] = lun.get_child_content('size')
|
||||
meta_dict['Qtree'] = lun.get_child_content('qtree')
|
||||
meta_dict['Path'] = lun.get_child_content('path')
|
||||
meta_dict['OsType'] = lun.get_child_content('multiprotocol-type')
|
||||
meta_dict['SpaceReserved'] = \
|
||||
lun.get_child_content('is-space-reservation-enabled')
|
||||
meta_dict['UUID'] = lun.get_child_content('uuid')
|
||||
meta_dict['BlockSize'] = lun.get_child_content('block-size')
|
||||
return meta_dict
|
||||
|
||||
def get_lun_map(self, path):
|
||||
"""Gets the LUN map by LUN path."""
|
||||
@ -853,7 +919,10 @@ class Client(client_base.Client):
|
||||
attr_list = luns.get_child_by_name('attributes-list')
|
||||
if not attr_list:
|
||||
return []
|
||||
return attr_list.get_children()
|
||||
|
||||
lun_list = [self._create_lun_meta(lun)
|
||||
for lun in attr_list.get_children()]
|
||||
return lun_list
|
||||
|
||||
def file_assign_qos(self, flex_vol, qos_policy_group_name,
|
||||
qos_policy_group_is_adaptive, file_path):
|
||||
@ -1061,7 +1130,8 @@ class Client(client_base.Client):
|
||||
num_records = result.get_child_content('num-records')
|
||||
if num_records and int(num_records) >= 1:
|
||||
attr_list = result.get_child_by_name('attributes-list')
|
||||
return attr_list.get_children()
|
||||
return [{'vserver': attr.get_child_content('vserver')}
|
||||
for attr in attr_list.get_children()]
|
||||
raise exception.NotFound(
|
||||
_('No interface found on cluster for ip %s') % ip)
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -314,7 +314,7 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
|
||||
"""Gets the vserver and export volume for share."""
|
||||
(host_ip, export_path) = self._get_export_ip_path(volume_id, share)
|
||||
ifs = self.zapi_client.get_if_info_by_ip(host_ip)
|
||||
vserver = ifs[0].get_child_content('vserver')
|
||||
vserver = ifs[0].get('vserver')
|
||||
exp_volume = self.zapi_client.get_vol_by_junc_vserver(vserver,
|
||||
export_path)
|
||||
return vserver, exp_volume
|
||||
@ -512,7 +512,7 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
|
||||
"""Get vserver for the mentioned ip."""
|
||||
try:
|
||||
ifs = self.zapi_client.get_if_info_by_ip(ip)
|
||||
vserver = ifs[0].get_child_content('vserver')
|
||||
vserver = ifs[0].get('vserver')
|
||||
return vserver
|
||||
except Exception:
|
||||
return None
|
||||
|
@ -22,34 +22,9 @@ import re
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
# NOTE(cknight): The keys in this map are tuples that contain arguments needed
|
||||
# for efficient use of the system-user-capability-get-iter cDOT API. The
|
||||
# values are SSC extra specs associated with the APIs listed in the keys.
|
||||
SSC_API_MAP = {
|
||||
('storage.aggregate', 'show', 'aggr-options-list-info'): [
|
||||
'netapp_raid_type',
|
||||
],
|
||||
('storage.disk', 'show', 'storage-disk-get-iter'): [
|
||||
'netapp_disk_type',
|
||||
],
|
||||
('snapmirror', 'show', 'snapmirror-get-iter'): [
|
||||
'netapp_mirrored',
|
||||
],
|
||||
('volume.efficiency', 'show', 'sis-get-iter'): [
|
||||
'netapp_dedup',
|
||||
'netapp_compression',
|
||||
],
|
||||
('volume', '*show', 'volume-get-iter'): [
|
||||
'netapp_flexvol_encryption',
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class CapabilitiesLibrary(object):
|
||||
|
||||
@ -64,30 +39,7 @@ class CapabilitiesLibrary(object):
|
||||
self.invalid_extra_specs = []
|
||||
|
||||
def check_api_permissions(self):
|
||||
"""Check which APIs that support SSC functionality are available."""
|
||||
|
||||
inaccessible_apis = []
|
||||
invalid_extra_specs = []
|
||||
|
||||
for api_tuple, extra_specs in SSC_API_MAP.items():
|
||||
object_name, operation_name, api = api_tuple
|
||||
if not self.zapi_client.check_cluster_api(object_name,
|
||||
operation_name,
|
||||
api):
|
||||
inaccessible_apis.append(api)
|
||||
invalid_extra_specs.extend(extra_specs)
|
||||
|
||||
if inaccessible_apis:
|
||||
if 'volume-get-iter' in inaccessible_apis:
|
||||
msg = _('User not permitted to query Data ONTAP volumes.')
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
else:
|
||||
LOG.warning('The configured user account does not have '
|
||||
'sufficient privileges to use all needed '
|
||||
'APIs. The following extra specs will fail '
|
||||
'or be ignored: %s.', invalid_extra_specs)
|
||||
|
||||
self.invalid_extra_specs = invalid_extra_specs
|
||||
self.invalid_extra_specs = self.zapi_client.check_api_permissions()
|
||||
|
||||
def cluster_user_supported(self):
|
||||
return not self.invalid_extra_specs
|
||||
|
Loading…
Reference in New Issue
Block a user