from cinder import exception
from cinder import test
+from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
+ fake_api as netapp_api)
from cinder.volume import configuration as conf
from cinder.volume.drivers.netapp import common
+from cinder.volume.drivers.netapp.dataontap.client import client_7mode
from cinder.volume.drivers.netapp.dataontap.client import client_base
from cinder.volume.drivers.netapp.dataontap.client import client_cmode
from cinder.volume.drivers.netapp.dataontap import ssc_cmode
from cinder.volume.drivers.netapp import utils
+FAKE_CONNECTION_HTTP = {
+ 'transport_type': 'http',
+ 'username': 'admin',
+ 'password': 'pass',
+ 'hostname': '127.0.0.1',
+ 'port': None,
+ 'vserver': 'openstack',
+}
+
+
def create_configuration():
configuration = conf.Configuration(None)
configuration.append_config_values(options.netapp_connection_opts)
ssc_cmode, 'refresh_cluster_ssc',
lambda a, b, c, synchronous: None)
self.mock_object(utils, 'OpenStackInfo')
+
+ # Inject fake netapp_lib module classes.
+ netapp_api.mock_netapp_lib([common, client_cmode, client_base])
+ self.mock_object(common.na_utils, 'check_netapp_lib')
+
configuration = self._set_config(create_configuration())
driver = common.NetAppDriver(configuration=configuration)
self.stubs.Set(http_client, 'HTTPConnection',
self.mock_object(utils, 'OpenStackInfo')
configuration = self._set_config(create_configuration())
driver = common.NetAppDriver(configuration=configuration)
+ mock_client = self.mock_object(client_cmode, 'Client')
driver.do_setup(context='')
- na_server = driver.library.zapi_client.get_connection()
- self.assertEqual('80', na_server.get_port())
- self.assertEqual('http', na_server.get_transport_type())
+ mock_client.assert_called_with(**FAKE_CONNECTION_HTTP)
@mock.patch.object(client_base.Client, 'get_ontapi_version',
mock.Mock(return_value=(1, 20)))
configuration = self._set_config(create_configuration())
configuration.netapp_transport_type = 'http'
driver = common.NetAppDriver(configuration=configuration)
+ mock_client = self.mock_object(client_cmode, 'Client')
driver.do_setup(context='')
- na_server = driver.library.zapi_client.get_connection()
- self.assertEqual('80', na_server.get_port())
- self.assertEqual('http', na_server.get_transport_type())
+ mock_client.assert_called_with(**FAKE_CONNECTION_HTTP)
@mock.patch.object(client_base.Client, 'get_ontapi_version',
mock.Mock(return_value=(1, 20)))
configuration.netapp_transport_type = 'https'
driver = common.NetAppDriver(configuration=configuration)
driver.library._get_root_volume_name = mock.Mock()
+ mock_client = self.mock_object(client_cmode, 'Client')
driver.do_setup(context='')
- na_server = driver.library.zapi_client.get_connection()
- self.assertEqual('443', na_server.get_port())
- self.assertEqual('https', na_server.get_transport_type())
+ FAKE_CONNECTION_HTTPS = dict(FAKE_CONNECTION_HTTP,
+ transport_type='https')
+ mock_client.assert_called_with(**FAKE_CONNECTION_HTTPS)
@mock.patch.object(client_base.Client, 'get_ontapi_version',
mock.Mock(return_value=(1, 20)))
configuration = self._set_config(create_configuration())
configuration.netapp_server_port = 81
driver = common.NetAppDriver(configuration=configuration)
+ mock_client = self.mock_object(client_cmode, 'Client')
driver.do_setup(context='')
- na_server = driver.library.zapi_client.get_connection()
- self.assertEqual('81', na_server.get_port())
- self.assertEqual('http', na_server.get_transport_type())
+ FAKE_CONNECTION_HTTP_PORT = dict(FAKE_CONNECTION_HTTP, port=81)
+ mock_client.assert_called_with(**FAKE_CONNECTION_HTTP_PORT)
@mock.patch.object(client_base.Client, 'get_ontapi_version',
mock.Mock(return_value=(1, 20)))
configuration.netapp_server_port = 446
driver = common.NetAppDriver(configuration=configuration)
driver.library._get_root_volume_name = mock.Mock()
+ mock_client = self.mock_object(client_cmode, 'Client')
driver.do_setup(context='')
- na_server = driver.library.zapi_client.get_connection()
- self.assertEqual('446', na_server.get_port())
- self.assertEqual('https', na_server.get_transport_type())
+ FAKE_CONNECTION_HTTPS_PORT = dict(FAKE_CONNECTION_HTTP, port=446,
+ transport_type='https')
+ mock_client.assert_called_with(**FAKE_CONNECTION_HTTPS_PORT)
def test_create_destroy(self):
self.driver.create_volume(self.volume)
def test_create_vol_snapshot_destroy(self):
self.driver.create_volume(self.volume)
+ self.mock_object(client_7mode.Client, '_check_clone_status')
+ self.mock_object(self.driver.library, '_clone_lun')
self.driver.create_snapshot(self.snapshot)
self.driver.create_volume_from_snapshot(self.volume_sec, self.snapshot)
self.driver.delete_snapshot(self.snapshot)
self.driver.delete_volume(self.volume)
def test_map_unmap(self):
+ self.mock_object(client_cmode.Client, 'get_igroup_by_initiators')
+ self.mock_object(client_cmode.Client, 'get_iscsi_target_details')
+ self.mock_object(client_cmode.Client, 'get_iscsi_service_details')
+ self.mock_object(self.driver.library, '_get_or_create_igroup')
+ self.mock_object(self.driver.library, '_map_lun')
+ self.mock_object(self.driver.library, '_unmap_lun')
+ FAKE_PREFERRED_TARGET = {'address': 'http://host:8080', 'port': 80}
+ FAKE_CONN_PROPERTIES = {'driver_volume_type': 'iscsi', 'data': 'test'}
+ self.mock_object(self.driver.library,
+ '_get_preferred_target_from_list',
+ mock.Mock(return_value=FAKE_PREFERRED_TARGET))
+ self.mock_object(common.na_utils, 'get_iscsi_connection_properties',
+ mock.Mock(return_value=FAKE_CONN_PROPERTIES))
self.mock_object(client_cmode.Client,
'get_operational_network_interface_addresses',
mock.Mock(return_value=[]))
def test_cloned_volume_destroy(self):
self.driver.create_volume(self.volume)
+ self.mock_object(self.driver.library, '_clone_lun')
self.driver.create_cloned_volume(self.snapshot, self.volume)
self.driver.delete_volume(self.snapshot)
self.driver.delete_volume(self.volume)
def test_map_by_creating_igroup(self):
+ FAKE_IGROUP_INFO = {'initiator-group-name': 'debian',
+ 'initiator-group-os-type': 'linux',
+ 'initiator-group-type': 'igroup'}
+ FAKE_PREFERRED_TARGET = {'address': 'http://host:8080', 'port': 80}
+ FAKE_CONN_PROPERTIES = {'driver_volume_type': 'iscsi', 'data': 'test'}
+ self.mock_object(client_cmode.Client, 'get_igroup_by_initiators',
+ mock.Mock(return_value=[FAKE_IGROUP_INFO]))
self.mock_object(client_cmode.Client,
'get_operational_network_interface_addresses',
mock.Mock(return_value=[]))
+ self.mock_object(client_cmode.Client, 'get_iscsi_target_details')
+ self.mock_object(client_cmode.Client, 'get_iscsi_service_details')
+ self.mock_object(self.driver.library,
+ '_get_preferred_target_from_list',
+ mock.Mock(return_value=FAKE_PREFERRED_TARGET))
+ self.mock_object(common.na_utils, 'get_iscsi_connection_properties',
+ mock.Mock(return_value=FAKE_CONN_PROPERTIES))
self.driver.create_volume(self.volume)
updates = self.driver.create_export(None, self.volume, {})
self.assertTrue(updates['provider_location'])
def test_vol_stats(self):
self.mock_object(client_base.Client, 'provide_ems')
- stats = self.driver.get_volume_stats(refresh=True)
- self.assertEqual('NetApp', stats['vendor_name'])
+ mock_update_vol_stats = self.mock_object(self.driver.library,
+ '_update_volume_stats')
+ self.driver.get_volume_stats(refresh=True)
+ self.assertEqual(mock_update_vol_stats.call_count, 1)
def test_create_vol_snapshot_diff_size_resize(self):
self.driver.create_volume(self.volume)
+ self.mock_object(self.driver.library, '_clone_source_to_destination')
+ self.mock_object(self.driver.library, '_clone_lun')
self.driver.create_snapshot(self.snapshot)
self.driver.create_volume_from_snapshot(
self.volume_clone, self.snapshot)
def test_create_vol_snapshot_diff_size_subclone(self):
self.driver.create_volume(self.volume)
+ self.mock_object(self.driver.library, '_clone_lun')
+ self.mock_object(self.driver.library, '_clone_source_to_destination')
self.driver.create_snapshot(self.snapshot)
self.driver.create_volume_from_snapshot(
self.volume_clone_large, self.snapshot)
self.driver.extend_volume(self.volume, self.volume['size'])
def test_extend_vol_direct_resize(self):
+ self.mock_object(self.driver.library.zapi_client,
+ 'get_lun_geometry', mock.Mock(return_value=None))
+ self.mock_object(self.driver.library, '_do_sub_clone_resize')
self.driver.create_volume(self.volume)
self.driver.extend_volume(self.volume, 3)
def test_extend_vol_sub_lun_clone(self):
+ self.mock_object(self.driver.library.zapi_client,
+ 'get_lun_geometry', mock.Mock(return_value=None))
+ self.mock_object(self.driver.library, '_do_sub_clone_resize')
self.driver.create_volume(self.volume)
self.driver.extend_volume(self.volume, 4)
def setUp(self):
super(NetAppDriverNegativeTestCase, self).setUp()
+ # Inject fake netapp_lib module classes.
+ netapp_api.mock_netapp_lib([common])
+ self.mock_object(common.na_utils, 'check_netapp_lib')
+
def test_incorrect_family(self):
self.mock_object(utils, 'OpenStackInfo')
configuration = create_configuration()
return self.sock.result
-class NetAppDirect7modeISCSIDriverTestCase_NV(
- NetAppDirectCmodeISCSIDriverTestCase):
+class NetAppDirect7modeISCSIDriverTestCase_NV(test.TestCase):
"""Test case for NetAppISCSIDriver without vfiler"""
+ volume = {
+ 'name': 'lun1',
+ 'size': 2,
+ 'volume_name': 'lun1',
+ 'os_type': 'linux',
+ 'provider_location': 'lun1',
+ 'id': 'lun1',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'display_name': None,
+ 'display_description': 'lun1',
+ 'volume_type_id': None,
+ 'host': 'hostname@backend#vol1',
+ }
+
def setUp(self):
super(NetAppDirect7modeISCSIDriverTestCase_NV, self).setUp()
+ self._custom_setup()
def _custom_setup(self):
self.mock_object(utils, 'OpenStackInfo')
+
+ # Inject fake netapp_lib module classes.
+ netapp_api.mock_netapp_lib([common, client_base, client_7mode])
+ self.mock_object(common.na_utils, 'check_netapp_lib')
+
configuration = self._set_config(create_configuration())
driver = common.NetAppDriver(configuration=configuration)
self.stubs.Set(http_client, 'HTTPConnection',
FakeDirect7modeHTTPConnection)
+ self.mock_object(driver.library, '_get_root_volume_name', mock.Mock(
+ return_value='root'))
driver.do_setup(context='')
driver.root_volume_name = 'root'
self.driver = driver
def test_check_for_setup_error_version(self):
drv = self.driver
- drv.zapi_client = mock.Mock()
- drv.zapi_client.get_ontapi_version.return_value = None
-
+ self.mock_object(client_base.Client, 'get_ontapi_version',
+ mock.Mock(return_value=None))
# check exception raises when version not found
self.assertRaises(exception.VolumeBackendAPIException,
drv.check_for_setup_error)
- drv.zapi_client.get_ontapi_version.return_value = (1, 8)
+ self.mock_object(client_base.Client, 'get_ontapi_version',
+ mock.Mock(return_value=(1, 8)))
# check exception raises when not supported version
self.assertRaises(exception.VolumeBackendAPIException,
def _custom_setup(self):
self.mock_object(utils, 'OpenStackInfo')
+
+ # Inject fake netapp_lib module classes.
+ netapp_api.mock_netapp_lib([common, client_base, client_7mode])
+ self.mock_object(common.na_utils, 'check_netapp_lib')
+
configuration = self._set_config(create_configuration())
driver = common.NetAppDriver(configuration=configuration)
self.stubs.Set(http_client, 'HTTPConnection',
FakeDirect7modeHTTPConnection)
+ self.mock_object(driver.library, '_get_root_volume_name',
+ mock.Mock(return_value='root'))
driver.do_setup(context='')
self.driver = driver
self.driver.root_volume_name = 'root'
import mock
import requests
-from six.moves import urllib
from cinder import exception
from cinder import test
+from cinder.tests.unit.volume.drivers.netapp.eseries import fakes
from cinder.volume import configuration as conf
from cinder.volume.drivers.netapp import common
from cinder.volume.drivers.netapp.eseries import client
def _custom_setup(self):
self.mock_object(na_utils, 'OpenStackInfo')
+
+ # Inject fake netapp_lib module classes.
+ fakes.mock_netapp_lib([client])
+ self.mock_object(common.na_utils, 'check_netapp_lib')
+
configuration = self._set_config(create_configuration())
self.driver = common.NetAppDriver(configuration=configuration)
self.library = self.driver.library
self.mock_object(requests, 'Session', FakeEseriesHTTPSession)
+ self.mock_object(self.library,
+ '_check_mode_get_or_register_storage_system')
self.driver.do_setup(context='context')
- self.driver.check_for_setup_error()
+ self.driver.library._client._endpoint = fakes.FAKE_ENDPOINT_HTTP
def _set_config(self, configuration):
configuration.netapp_storage_family = 'eseries'
configuration = self._set_config(create_configuration())
configuration.netapp_controller_ips = '127.0.0.1,127.0.0.3'
driver = common.NetAppDriver(configuration=configuration)
+ self.mock_object(client.RestClient, 'list_storage_systems', mock.Mock(
+ return_value=[fakes.STORAGE_SYSTEM]))
driver.do_setup(context='context')
self.assertEqual('1fa6efb5-f07b-4de4-9f0e-52e5f7ff5d1b',
driver.library._client.get_system_id())
result = self.library._check_storage_system()
self.assertTrue(result)
- def test_connect(self):
- self.driver.check_for_setup_error()
-
def test_create_destroy(self):
+ FAKE_POOLS = [{'label': 'DDP', 'volumeGroupRef': 'test'}]
+ self.library._get_storage_pools = mock.Mock(return_value=FAKE_POOLS)
+ self.mock_object(self.library._client, '_get_resource_url', mock.Mock(
+ return_value=fakes.FAKE_ENDPOINT_HTTP))
+ self.mock_object(self.library._client, '_eval_response')
+ self.mock_object(self.library._client, 'list_volumes', mock.Mock(
+ return_value=FAKE_POOLS))
self.driver.create_volume(self.volume)
self.driver.delete_volume(self.volume)
configuration = self._set_config(create_configuration())
driver = common.NetAppDriver(configuration=configuration)
driver.library._check_mode_get_or_register_storage_system = mock.Mock()
+ mock_invoke = self.mock_object(client, 'RestClient')
driver.do_setup(context='context')
- url = urllib.parse.urlparse(driver.library._client._endpoint)
- port = url.port
- scheme = url.scheme
- self.assertEqual(8080, port)
- self.assertEqual('http', scheme)
+ mock_invoke.assert_called_with(**fakes.FAKE_CLIENT_PARAMS)
def test_do_setup_http_default_port(self):
configuration = self._set_config(create_configuration())
configuration.netapp_transport_type = 'http'
driver = common.NetAppDriver(configuration=configuration)
driver.library._check_mode_get_or_register_storage_system = mock.Mock()
+ mock_invoke = self.mock_object(client, 'RestClient')
driver.do_setup(context='context')
- url = urllib.parse.urlparse(driver.library._client._endpoint)
- port = url.port
- scheme = url.scheme
- self.assertEqual(8080, port)
- self.assertEqual('http', scheme)
+ mock_invoke.assert_called_with(**fakes.FAKE_CLIENT_PARAMS)
def test_do_setup_https_default_port(self):
configuration = self._set_config(create_configuration())
configuration.netapp_transport_type = 'https'
driver = common.NetAppDriver(configuration=configuration)
driver.library._check_mode_get_or_register_storage_system = mock.Mock()
+ mock_invoke = self.mock_object(client, 'RestClient')
driver.do_setup(context='context')
- url = urllib.parse.urlparse(driver.library._client._endpoint)
- port = url.port
- scheme = url.scheme
- self.assertEqual(8443, port)
- self.assertEqual('https', scheme)
+ FAKE_EXPECTED_PARAMS = dict(fakes.FAKE_CLIENT_PARAMS, port=8443,
+ scheme='https')
+ mock_invoke.assert_called_with(**FAKE_EXPECTED_PARAMS)
def test_do_setup_http_non_default_port(self):
configuration = self._set_config(create_configuration())
configuration.netapp_server_port = 81
driver = common.NetAppDriver(configuration=configuration)
driver.library._check_mode_get_or_register_storage_system = mock.Mock()
+ mock_invoke = self.mock_object(client, 'RestClient')
driver.do_setup(context='context')
- url = urllib.parse.urlparse(driver.library._client._endpoint)
- port = url.port
- scheme = url.scheme
- self.assertEqual(81, port)
- self.assertEqual('http', scheme)
+ FAKE_EXPECTED_PARAMS = dict(fakes.FAKE_CLIENT_PARAMS, port=81)
+ mock_invoke.assert_called_with(**FAKE_EXPECTED_PARAMS)
def test_do_setup_https_non_default_port(self):
configuration = self._set_config(create_configuration())
configuration.netapp_server_port = 446
driver = common.NetAppDriver(configuration=configuration)
driver.library._check_mode_get_or_register_storage_system = mock.Mock()
+ mock_invoke = self.mock_object(client, 'RestClient')
driver.do_setup(context='context')
- url = urllib.parse.urlparse(driver.library._client._endpoint)
- port = url.port
- scheme = url.scheme
- self.assertEqual(446, port)
- self.assertEqual('https', scheme)
+ FAKE_EXPECTED_PARAMS = dict(fakes.FAKE_CLIENT_PARAMS, port=446,
+ scheme='https')
+ mock_invoke.assert_called_with(**FAKE_EXPECTED_PARAMS)
def test_setup_good_controller_ip(self):
configuration = self._set_config(create_configuration())
from cinder import exception
from cinder.image import image_utils
from cinder import test
+from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
+ fake_api as netapp_api)
from cinder import utils as cinder_utils
from cinder.volume import configuration as conf
from cinder.volume.drivers.netapp import common
-from cinder.volume.drivers.netapp.dataontap.client import api
+from cinder.volume.drivers.netapp.dataontap import (nfs_7mode
+ as netapp_nfs_7mode)
+from cinder.volume.drivers.netapp.dataontap import (nfs_cmode
+ as netapp_nfs_cmode)
from cinder.volume.drivers.netapp.dataontap.client import client_7mode
from cinder.volume.drivers.netapp.dataontap.client import client_base
from cinder.volume.drivers.netapp.dataontap.client import client_cmode
-from cinder.volume.drivers.netapp.dataontap import nfs_7mode \
- as netapp_nfs_7mode
from cinder.volume.drivers.netapp.dataontap import nfs_base
-from cinder.volume.drivers.netapp.dataontap import nfs_cmode \
- as netapp_nfs_cmode
from cinder.volume.drivers.netapp.dataontap import ssc_cmode
from cinder.volume.drivers.netapp import utils
CONF = cfg.CONF
-CONNECTION_INFO = {'hostname': 'fake_host',
- 'transport_type': 'https',
- 'port': 443,
- 'username': 'admin',
- 'password': 'passw0rd'}
+CONNECTION_INFO = {
+ 'hostname': 'fake_host',
+ 'transport_type': 'https',
+ 'port': 443,
+ 'username': 'admin',
+ 'password': 'passw0rd',
+}
+
+FAKE_CONNECTION_INFO_HTTP = {
+ 'hostname': '127.0.0.1',
+ 'transport_type': 'http',
+ 'port': None,
+ 'username': 'admin',
+ 'password': 'pass',
+ 'vserver': 'openstack',
+}
+
+FAKE_CONNECTION_INFO_HTTPS = dict(FAKE_CONNECTION_INFO_HTTP,
+ transport_type='https')
+
+FAKE_7MODE_CONNECTION_INFO_HTTP = dict(FAKE_CONNECTION_INFO_HTTP)
+FAKE_7MODE_CONNECTION_INFO_HTTP.pop('vserver')
+FAKE_7MODE_CONNECTION_INFO_HTTP['vfiler'] = 'test_vfiler'
+
+FAKE_7MODE_CONNECTION_INFO_HTTPS = dict(FAKE_7MODE_CONNECTION_INFO_HTTP,
+ transport_type='https')
+
SEVEN_MODE_CONNECTION_INFO = dict(
itertools.chain(CONNECTION_INFO.items(),
{'vfiler': 'test_vfiler'}.items()))
+
FAKE_VSERVER = 'fake_vserver'
kwargs = {}
kwargs['netapp_mode'] = 'proxy'
kwargs['configuration'] = create_configuration()
+
+ # Inject fake netapp_lib module classes.
+ netapp_api.mock_netapp_lib([client_cmode, client_base])
+ self.mock_object(common.na_utils, 'check_netapp_lib')
+
+ self.mock_object(nfs_base, 'LOG')
self._driver = netapp_nfs_cmode.NetAppCmodeNfsDriver(**kwargs)
self._driver.zapi_client = mock.Mock()
config = self._driver.configuration
<vserver>openstack</vserver>
</net-interface-info></attributes-list>"""
response_el = etree.XML(res)
- return api.NaElement(response_el).get_children()
+ return netapp_api.NaElement(response_el).get_children()
def test_clone_backing_file_for_volume(self):
drv = self._driver
def test_do_setup_all_default(self):
configuration = self._set_config(create_configuration())
driver = common.NetAppDriver(configuration=configuration)
+ mock_invoke = self.mock_object(client_cmode, 'Client')
driver.do_setup(context='')
- na_server = driver.zapi_client.get_connection()
- self.assertEqual('80', na_server.get_port())
- self.assertEqual('http', na_server.get_transport_type())
+ mock_invoke.assert_called_with(**FAKE_CONNECTION_INFO_HTTP)
@mock.patch.object(client_base.Client, 'get_ontapi_version',
mock.Mock(return_value=(1, 20)))
configuration = self._set_config(create_configuration())
configuration.netapp_transport_type = 'http'
driver = common.NetAppDriver(configuration=configuration)
+ mock_invoke = self.mock_object(client_cmode, 'Client')
driver.do_setup(context='')
- na_server = driver.zapi_client.get_connection()
- self.assertEqual('80', na_server.get_port())
- self.assertEqual('http', na_server.get_transport_type())
+ mock_invoke.assert_called_with(**FAKE_CONNECTION_INFO_HTTP)
@mock.patch.object(client_base.Client, 'get_ontapi_version',
mock.Mock(return_value=(1, 20)))
configuration = self._set_config(create_configuration())
configuration.netapp_transport_type = 'https'
driver = common.NetAppDriver(configuration=configuration)
+ mock_invoke = self.mock_object(client_cmode, 'Client')
driver.do_setup(context='')
- na_server = driver.zapi_client.get_connection()
- self.assertEqual('443', na_server.get_port())
- self.assertEqual('https', na_server.get_transport_type())
+ mock_invoke.assert_called_with(**FAKE_CONNECTION_INFO_HTTPS)
@mock.patch.object(client_base.Client, 'get_ontapi_version',
mock.Mock(return_value=(1, 20)))
configuration = self._set_config(create_configuration())
configuration.netapp_server_port = 81
driver = common.NetAppDriver(configuration=configuration)
+ mock_invoke = self.mock_object(client_cmode, 'Client')
driver.do_setup(context='')
- na_server = driver.zapi_client.get_connection()
- self.assertEqual('81', na_server.get_port())
- self.assertEqual('http', na_server.get_transport_type())
+ FAKE_CONN_INFO_PORT_HTTP = dict(FAKE_CONNECTION_INFO_HTTP, port=81)
+ mock_invoke.assert_called_with(**FAKE_CONN_INFO_PORT_HTTP)
@mock.patch.object(client_base.Client, 'get_ontapi_version',
mock.Mock(return_value=(1, 20)))
configuration.netapp_transport_type = 'https'
configuration.netapp_server_port = 446
driver = common.NetAppDriver(configuration=configuration)
+ mock_invoke = self.mock_object(client_cmode, 'Client')
driver.do_setup(context='')
- na_server = driver.zapi_client.get_connection()
- self.assertEqual('446', na_server.get_port())
- self.assertEqual('https', na_server.get_transport_type())
+ FAKE_CONN_INFO_PORT_HTTPS = dict(FAKE_CONNECTION_INFO_HTTPS, port=446)
+ mock_invoke.assert_called_with(**FAKE_CONN_INFO_PORT_HTTPS)
@mock.patch.object(utils, 'resolve_hostname', return_value='10.12.142.11')
def test_convert_vol_ref_share_name_to_share_ip(self, mock_hostname):
self._driver.ssc_enabled = True
self._driver.configuration.netapp_copyoffload_tool_path = 'cof_path'
self._driver.zapi_client = mock.Mock()
+ self.mock_object(netapp_nfs_cmode, 'LOG')
self._fake_empty_qos_policy_group_info = {
'legacy': None,
'spec': None,
class NetApp7modeNfsDriverTestCase(NetAppCmodeNfsDriverTestCase):
- """Test direct NetApp C Mode driver."""
+ """Test direct NetApp 7 Mode driver."""
def _custom_setup(self):
self.mock_object(utils, 'OpenStackInfo')
+
+ # Inject fake netapp_lib module classes.
+ netapp_api.mock_netapp_lib([client_cmode, client_base])
+ self.mock_object(common.na_utils, 'check_netapp_lib')
+
+ self.mock_object(common.na_utils, 'LOG')
+ self.mock_object(nfs_base, 'LOG')
self._driver = netapp_nfs_7mode.NetApp7modeNfsDriver(
configuration=create_configuration())
self._driver.zapi_client = mock.Mock()
mock_client_init.assert_called_once_with(**SEVEN_MODE_CONNECTION_INFO)
mock_super_do_setup.assert_called_once_with(context)
+ @mock.patch.object(client_base.Client, 'get_ontapi_version',
+ mock.Mock(return_value=(1, 20)))
+ @mock.patch.object(nfs_base.NetAppNfsDriver, 'do_setup', mock.Mock())
+ def test_do_setup_all_default(self):
+ configuration = self._set_config(create_configuration())
+ driver = common.NetAppDriver(configuration=configuration)
+ mock_invoke = self.mock_object(client_7mode, 'Client')
+ driver.do_setup(context='')
+ mock_invoke.assert_called_with(**FAKE_7MODE_CONNECTION_INFO_HTTP)
+
+ @mock.patch.object(client_base.Client, 'get_ontapi_version',
+ mock.Mock(return_value=(1, 20)))
+ @mock.patch.object(nfs_base.NetAppNfsDriver, 'do_setup', mock.Mock())
+ def test_do_setup_http_default_port(self):
+ configuration = self._set_config(create_configuration())
+ configuration.netapp_transport_type = 'http'
+ driver = common.NetAppDriver(configuration=configuration)
+ mock_invoke = self.mock_object(client_7mode, 'Client')
+ driver.do_setup(context='')
+ mock_invoke.assert_called_with(**FAKE_7MODE_CONNECTION_INFO_HTTP)
+
+ @mock.patch.object(client_base.Client, 'get_ontapi_version',
+ mock.Mock(return_value=(1, 20)))
+ @mock.patch.object(nfs_base.NetAppNfsDriver, 'do_setup', mock.Mock())
+ def test_do_setup_https_default_port(self):
+ configuration = self._set_config(create_configuration())
+ configuration.netapp_transport_type = 'https'
+ driver = common.NetAppDriver(configuration=configuration)
+ mock_invoke = self.mock_object(client_7mode, 'Client')
+ driver.do_setup(context='')
+ mock_invoke.assert_called_with(**FAKE_7MODE_CONNECTION_INFO_HTTPS)
+
+ @mock.patch.object(client_base.Client, 'get_ontapi_version',
+ mock.Mock(return_value=(1, 20)))
+ @mock.patch.object(nfs_base.NetAppNfsDriver, 'do_setup', mock.Mock())
+ def test_do_setup_http_non_default_port(self):
+ configuration = self._set_config(create_configuration())
+ configuration.netapp_server_port = 81
+ driver = common.NetAppDriver(configuration=configuration)
+ mock_invoke = self.mock_object(client_7mode, 'Client')
+ driver.do_setup(context='')
+ FAKE_CONN_INFO_PORT_HTTP = dict(FAKE_7MODE_CONNECTION_INFO_HTTP,
+ port=81)
+ mock_invoke.assert_called_with(**FAKE_CONN_INFO_PORT_HTTP)
+
+ @mock.patch.object(client_base.Client, 'get_ontapi_version',
+ mock.Mock(return_value=(1, 20)))
+ @mock.patch.object(nfs_base.NetAppNfsDriver, 'do_setup', mock.Mock())
+ def test_do_setup_https_non_default_port(self):
+ configuration = self._set_config(create_configuration())
+ configuration.netapp_transport_type = 'https'
+ configuration.netapp_server_port = 446
+ driver = common.NetAppDriver(configuration=configuration)
+ mock_invoke = self.mock_object(client_7mode, 'Client')
+ driver.do_setup(context='')
+ FAKE_CONN_INFO_PORT_HTTPS = dict(FAKE_7MODE_CONNECTION_INFO_HTTPS,
+ port=446)
+ mock_invoke.assert_called_with(**FAKE_CONN_INFO_PORT_HTTPS)
+
@mock.patch.object(nfs_base.NetAppNfsDriver, 'check_for_setup_error')
def test_check_for_setup_error(self, mock_super_check_for_setup_error):
self._driver.zapi_client.get_ontapi_version.return_value = (1, 20)
drv._clone_backing_file_for_volume(volume_name, clone_name,
volume_id)
except Exception as e:
- if isinstance(e, api.NaApiError):
+ if isinstance(e, netapp_api.NaApiError):
pass
else:
raise
import copy
from lxml import etree
+import mock
from mox3 import mox
import six
from six.moves import BaseHTTPServer
from cinder import exception
from cinder import test
-from cinder.volume.drivers.netapp.dataontap.client import api
+from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
+ fake_api as netapp_api)
from cinder.volume.drivers.netapp.dataontap import ssc_cmode
def setUp(self):
super(SscUtilsTestCase, self).setUp()
+ netapp_api.mock_netapp_lib([ssc_cmode])
self.stubs.Set(http_client, 'HTTPConnection',
FakeDirectCmodeHTTPConnection)
def test_cl_vols_ssc_all(self):
"""Test cluster ssc for all vols."""
- na_server = api.NaServer('127.0.0.1')
+ na_server = netapp_api.NaServer('127.0.0.1')
vserver = 'openstack'
test_vols = set([copy.deepcopy(self.vol1),
copy.deepcopy(self.vol2), copy.deepcopy(self.vol3)])
def test_cl_vols_ssc_single(self):
"""Test cluster ssc for single vol."""
- na_server = api.NaServer('127.0.0.1')
+ na_server = netapp_api.NaServer('127.0.0.1')
vserver = 'openstack'
test_vols = set([copy.deepcopy(self.vol1)])
sis = {'vola': {'dedup': False, 'compression': False}}
def test_get_cluster_ssc(self):
"""Test get cluster ssc map."""
- na_server = api.NaServer('127.0.0.1')
+ na_server = netapp_api.NaServer('127.0.0.1')
vserver = 'openstack'
test_vols = set(
[self.vol1, self.vol2, self.vol3, self.vol4, self.vol5])
self.assertEqual(expected, result)
def test_query_cl_vols_for_ssc(self):
- na_server = api.NaServer('127.0.0.1')
- na_server.set_api_version(1, 15)
+ na_server = netapp_api.NaServer('127.0.0.1')
+ body = etree.XML("""<results status="passed"><attributes-list>
+ <volume-attributes>
+ <volume-id-attributes>
+ <name>iscsi</name>
+ <owning-vserver-name>Openstack</owning-vserver-name>
+ <containing-aggregate-name>aggr0
+ </containing-aggregate-name>
+ <junction-path>/iscsi</junction-path>
+ <type>rw</type>
+ </volume-id-attributes>
+ <volume-space-attributes>
+ <size-available>214748364</size-available>
+ <size-total>224748364</size-total>
+ <space-guarantee-enabled>enabled</space-guarantee-enabled>
+ <space-guarantee>file</space-guarantee>
+ </volume-space-attributes>
+ <volume-state-attributes>
+ <is-cluster-volume>true
+ </is-cluster-volume>
+ <is-vserver-root>false</is-vserver-root>
+ <state>online</state>
+ <is-inconsistent>false</is-inconsistent>
+ <is-invalid>false</is-invalid>
+ <is-junction-active>true</is-junction-active>
+ </volume-state-attributes>
+ </volume-attributes>
+ <volume-attributes>
+ <volume-id-attributes>
+ <name>nfsvol</name>
+ <owning-vserver-name>Openstack
+ </owning-vserver-name>
+ <containing-aggregate-name>aggr0
+ </containing-aggregate-name>
+ <junction-path>/nfs</junction-path>
+ <type>rw</type>
+ </volume-id-attributes>
+ <volume-space-attributes>
+ <size-available>14748364</size-available>
+ <size-total>24748364</size-total>
+ <space-guarantee-enabled>enabled
+ </space-guarantee-enabled>
+ <space-guarantee>volume</space-guarantee>
+ </volume-space-attributes>
+ <volume-state-attributes>
+ <is-cluster-volume>true
+ </is-cluster-volume>
+ <is-vserver-root>false</is-vserver-root>
+ <state>online</state>
+ <is-inconsistent>false</is-inconsistent>
+ <is-invalid>false</is-invalid>
+ <is-junction-active>true</is-junction-active>
+ </volume-state-attributes>
+ </volume-attributes>
+ <volume-attributes>
+ <volume-id-attributes>
+ <name>nfsvol2</name>
+ <owning-vserver-name>Openstack
+ </owning-vserver-name>
+ <containing-aggregate-name>aggr0
+ </containing-aggregate-name>
+ <junction-path>/nfs2</junction-path>
+ <type>rw</type>
+ </volume-id-attributes>
+ <volume-space-attributes>
+ <size-available>14748364</size-available>
+ <size-total>24748364</size-total>
+ <space-guarantee-enabled>enabled
+ </space-guarantee-enabled>
+ <space-guarantee>volume</space-guarantee>
+ </volume-space-attributes>
+ <volume-state-attributes>
+ <is-cluster-volume>true
+ </is-cluster-volume>
+ <is-vserver-root>false</is-vserver-root>
+ <state>online</state>
+ <is-inconsistent>true</is-inconsistent>
+ <is-invalid>true</is-invalid>
+ <is-junction-active>true</is-junction-active>
+ </volume-state-attributes>
+ </volume-attributes>
+ <volume-attributes>
+ <volume-id-attributes>
+ <name>nfsvol3</name>
+ <owning-vserver-name>Openstack
+ </owning-vserver-name>
+ <containing-aggregate-name>aggr0
+ </containing-aggregate-name>
+ <junction-path>/nfs3</junction-path>
+ <type>rw</type>
+ </volume-id-attributes>
+ <volume-space-attributes>
+ <space-guarantee-enabled>enabled
+ </space-guarantee-enabled>
+ <space-guarantee>volume
+ </space-guarantee>
+ </volume-space-attributes>
+ <volume-state-attributes>
+ <is-cluster-volume>true
+ </is-cluster-volume>
+ <is-vserver-root>false</is-vserver-root>
+ <state>online</state>
+ <is-inconsistent>false</is-inconsistent>
+ <is-invalid>false</is-invalid>
+ <is-junction-active>true</is-junction-active>
+ </volume-state-attributes>
+ </volume-attributes>
+ </attributes-list>
+ <num-records>4</num-records></results>""")
+
+ self.mock_object(ssc_cmode.netapp_api, 'invoke_api', mock.Mock(
+ return_value=[netapp_api.NaElement(body)]))
+
vols = ssc_cmode.query_cluster_vols_for_ssc(na_server, 'Openstack')
self.assertEqual(2, len(vols))
for vol in vols:
raise exception.InvalidVolume('Invalid volume returned.')
def test_query_aggr_options(self):
- na_server = api.NaServer('127.0.0.1')
+ na_server = netapp_api.NaServer('127.0.0.1')
+ body = etree.XML("""<results status="passed">
+ <options>
+ <aggr-option-info>
+ <name>ha_policy</name>
+ <value>cfo</value>
+ </aggr-option-info>
+ <aggr-option-info>
+ <name>raidtype</name>
+ <value>raid_dp</value>
+ </aggr-option-info>
+ </options>
+ </results>""")
+
+ self.mock_object(ssc_cmode.netapp_api, 'invoke_api', mock.Mock(
+ return_value=[netapp_api.NaElement(body)]))
+
aggr_attribs = ssc_cmode.query_aggr_options(na_server, 'aggr0')
if aggr_attribs:
self.assertEqual('cfo', aggr_attribs['ha_policy'])
raise exception.InvalidParameterValue("Incorrect aggr options")
def test_query_aggr_storage_disk(self):
- na_server = api.NaServer('127.0.0.1')
+ na_server = netapp_api.NaServer('127.0.0.1')
+ body = etree.XML("""<results status="passed">
+ <attributes-list>
+ <storage-disk-info>
+ <disk-raid-info>
+ <effective-disk-type>SATA</effective-disk-type>
+ </disk-raid-info>
+ </storage-disk-info>
+ </attributes-list>
+ </results>""")
+
+ self.mock_object(ssc_cmode.netapp_api, 'invoke_api',
+ mock.Mock(return_value=[netapp_api.NaElement(body)]))
+
eff_disk_type = ssc_cmode.query_aggr_storage_disk(na_server, 'aggr0')
self.assertEqual('SATA', eff_disk_type)
--- /dev/null
+# Copyright (c) 2015 Clinton Knight. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import sys
+
+from lxml import etree
+import mock
+import six
+
+from cinder import exception
+
+
+EONTAPI_EINVAL = '22'
+EAPIERROR = '13001'
+EAPINOTFOUND = '13005'
+ESNAPSHOTNOTALLOWED = '13023'
+EVOLUMEOFFLINE = '13042'
+EINTERNALERROR = '13114'
+EDUPLICATEENTRY = '13130'
+EVOLNOTCLONE = '13170'
+EVOL_NOT_MOUNTED = '14716'
+ESIS_CLONE_NOT_LICENSED = '14956'
+EOBJECTNOTFOUND = '15661'
+E_VIFMGR_PORT_ALREADY_ASSIGNED_TO_BROADCAST_DOMAIN = '18605'
+
+
+def mock_netapp_lib(modules):
+ """Inject fake netapp_lib module classes."""
+
+ netapp_lib = mock.Mock()
+ netapp_lib.api.zapi.zapi.NaElement = NaElement
+ netapp_lib.api.zapi.zapi.NaApiError = NaApiError
+ netapp_lib.api.zapi.zapi.NaServer = mock.Mock()
+ netapp_lib.api.zapi.errors = sys.modules[__name__]
+ for module in modules:
+ setattr(module, 'netapp_api', netapp_lib.api.zapi.zapi)
+ setattr(module, 'netapp_error', netapp_lib.api.zapi.errors)
+
+
+class NaApiError(exception.CinderException):
+ """Fake NetApi API invocation error."""
+
+ def __init__(self, code=None, message=None):
+ if not code:
+ code = 'unknown'
+ if not message:
+ message = 'unknown'
+ self.code = code
+ self.message = message
+ super(NaApiError, self).__init__(message=message)
+
+
+class NaServer(object):
+ """Fake XML wrapper class for NetApp Server"""
+ def __init__(self, host):
+ self._host = host
+
+
+class NaElement(object):
+ """Fake XML wrapper class for NetApp API."""
+
+ def __init__(self, name):
+ """Name of the element or etree.Element."""
+ if isinstance(name, etree._Element):
+ self._element = name
+ else:
+ self._element = etree.Element(name)
+
+ def get_name(self):
+ """Returns the tag name of the element."""
+ return self._element.tag
+
+ def set_content(self, text):
+ """Set the text string for the element."""
+ self._element.text = text
+
+ def get_content(self):
+ """Get the text for the element."""
+ return self._element.text
+
+ def add_attr(self, name, value):
+ """Add the attribute to the element."""
+ self._element.set(name, value)
+
+ def add_attrs(self, **attrs):
+ """Add multiple attributes to the element."""
+ for attr in attrs.keys():
+ self._element.set(attr, attrs.get(attr))
+
+ def add_child_elem(self, na_element):
+ """Add the child element to the element."""
+ if isinstance(na_element, NaElement):
+ self._element.append(na_element._element)
+ return
+ raise
+
+ def get_child_by_name(self, name):
+ """Get the child element by the tag name."""
+ for child in self._element.iterchildren():
+ if child.tag == name or etree.QName(child.tag).localname == name:
+ return NaElement(child)
+ return None
+
+ def get_child_content(self, name):
+ """Get the content of the child."""
+ for child in self._element.iterchildren():
+ if child.tag == name or etree.QName(child.tag).localname == name:
+ return child.text
+ return None
+
+ def get_children(self):
+ """Get the children for the element."""
+ return [NaElement(el) for el in self._element.iterchildren()]
+
+ def has_attr(self, name):
+ """Checks whether element has attribute."""
+ attributes = self._element.attrib or {}
+ return name in attributes.keys()
+
+ def get_attr(self, name):
+ """Get the attribute with the given name."""
+ attributes = self._element.attrib or {}
+ return attributes.get(name)
+
+ def get_attr_names(self):
+ """Returns the list of attribute names."""
+ attributes = self._element.attrib or {}
+ return attributes.keys()
+
+ def add_new_child(self, name, content, convert=False):
+ """Add child with tag name and context.
+
+ Convert replaces entity refs to chars.
+ """
+ child = NaElement(name)
+ if convert:
+ content = NaElement._convert_entity_refs(content)
+ child.set_content(content)
+ self.add_child_elem(child)
+
+ @staticmethod
+ def _convert_entity_refs(text):
+ """Converts entity refs to chars to handle etree auto conversions."""
+ text = text.replace("<", "<")
+ text = text.replace(">", ">")
+ return text
+
+ @staticmethod
+ def create_node_with_children(node, **children):
+ """Creates and returns named node with children."""
+ parent = NaElement(node)
+ for child in children.keys():
+ parent.add_new_child(child, children.get(child, None))
+ return parent
+
+ def add_node_with_children(self, node, **children):
+ """Creates named node with children."""
+ parent = NaElement.create_node_with_children(node, **children)
+ self.add_child_elem(parent)
+
+ def to_string(self, pretty=False, method='xml', encoding='UTF-8'):
+ """Prints the element to string."""
+ return etree.tostring(self._element, method=method, encoding=encoding,
+ pretty_print=pretty)
+
+ def __getitem__(self, key):
+ """Dict getter method for NaElement.
+
+ Returns NaElement list if present,
+ text value in case no NaElement node
+ children or attribute value if present.
+ """
+
+ child = self.get_child_by_name(key)
+ if child:
+ if child.get_children():
+ return child
+ else:
+ return child.get_content()
+ elif self.has_attr(key):
+ return self.get_attr(key)
+ raise KeyError('No element by given name %s.' % key)
+
+ def __setitem__(self, key, value):
+ """Dict setter method for NaElement.
+
+ Accepts dict, list, tuple, str, int, float and long as valid value.
+ """
+ if key:
+ if value:
+ if isinstance(value, NaElement):
+ child = NaElement(key)
+ child.add_child_elem(value)
+ self.add_child_elem(child)
+ elif isinstance(value, (str, int, float, long)):
+ self.add_new_child(key, six.text_type(value))
+ elif isinstance(value, (list, tuple, dict)):
+ child = NaElement(key)
+ child.translate_struct(value)
+ self.add_child_elem(child)
+ else:
+ raise TypeError('Not a valid value for NaElement.')
+ else:
+ self.add_child_elem(NaElement(key))
+ else:
+ raise KeyError('NaElement name cannot be null.')
+
+ def translate_struct(self, data_struct):
+ """Convert list, tuple, dict to NaElement and appends."""
+
+ if isinstance(data_struct, (list, tuple)):
+ for el in data_struct:
+ if isinstance(el, (list, tuple, dict)):
+ self.translate_struct(el)
+ else:
+ self.add_child_elem(NaElement(el))
+ elif isinstance(data_struct, dict):
+ for k in data_struct.keys():
+ child = NaElement(k)
+ if isinstance(data_struct[k], (dict, list, tuple)):
+ child.translate_struct(data_struct[k])
+ else:
+ if data_struct[k]:
+ child.set_content(six.text_type(data_struct[k]))
+ self.add_child_elem(child)
+ else:
+ raise ValueError('Type cannot be converted into NaElement.')
+++ /dev/null
-# Copyright (c) 2014 Ben Swartzlander. All rights reserved.
-# Copyright (c) 2014 Navneet Singh. All rights reserved.
-# Copyright (c) 2014 Clinton Knight. All rights reserved.
-# Copyright (c) 2014 Alex Meade. All rights reserved.
-# Copyright (c) 2014 Bob Callaway. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-"""
-Tests for NetApp API layer
-"""
-
-
-from cinder.i18n import _
-from cinder import test
-from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
-
-
-class NetAppApiElementTransTests(test.TestCase):
- """Test case for NetApp API element translations."""
-
- def setUp(self):
- super(NetAppApiElementTransTests, self).setUp()
-
- def test_translate_struct_dict_unique_key(self):
- """Tests if dict gets properly converted to NaElements."""
- root = netapp_api.NaElement('root')
- child = {'e1': 'v1', 'e2': 'v2', 'e3': 'v3'}
- root.translate_struct(child)
- self.assertEqual(3, len(root.get_children()))
- self.assertEqual('v1', root.get_child_content('e1'))
- self.assertEqual('v2', root.get_child_content('e2'))
- self.assertEqual('v3', root.get_child_content('e3'))
-
- def test_translate_struct_dict_nonunique_key(self):
- """Tests if list/dict gets properly converted to NaElements."""
- root = netapp_api.NaElement('root')
- child = [{'e1': 'v1', 'e2': 'v2'}, {'e1': 'v3'}]
- root.translate_struct(child)
- self.assertEqual(3, len(root.get_children()))
- children = root.get_children()
- for c in children:
- if c.get_name() == 'e1':
- self.assertIn(c.get_content(), ['v1', 'v3'])
- else:
- self.assertEqual('v2', c.get_content())
-
- def test_translate_struct_list(self):
- """Tests if list gets properly converted to NaElements."""
- root = netapp_api.NaElement('root')
- child = ['e1', 'e2']
- root.translate_struct(child)
- self.assertEqual(2, len(root.get_children()))
- self.assertIsNone(root.get_child_content('e1'))
- self.assertIsNone(root.get_child_content('e2'))
-
- def test_translate_struct_tuple(self):
- """Tests if tuple gets properly converted to NaElements."""
- root = netapp_api.NaElement('root')
- child = ('e1', 'e2')
- root.translate_struct(child)
- self.assertEqual(2, len(root.get_children()))
- self.assertIsNone(root.get_child_content('e1'))
- self.assertIsNone(root.get_child_content('e2'))
-
- def test_translate_invalid_struct(self):
- """Tests if invalid data structure raises exception."""
- root = netapp_api.NaElement('root')
- child = 'random child element'
- self.assertRaises(ValueError, root.translate_struct, child)
-
- def test_setter_builtin_types(self):
- """Tests str, int, float get converted to NaElement."""
- root = netapp_api.NaElement('root')
- root['e1'] = 'v1'
- root['e2'] = 1
- root['e3'] = 2.0
- root['e4'] = 8l
- self.assertEqual(4, len(root.get_children()))
- self.assertEqual('v1', root.get_child_content('e1'))
- self.assertEqual('1', root.get_child_content('e2'))
- self.assertEqual('2.0', root.get_child_content('e3'))
- self.assertEqual('8', root.get_child_content('e4'))
-
- def test_setter_na_element(self):
- """Tests na_element gets appended as child."""
- root = netapp_api.NaElement('root')
- root['e1'] = netapp_api.NaElement('nested')
- self.assertEqual(1, len(root.get_children()))
- e1 = root.get_child_by_name('e1')
- self.assertIsInstance(e1, netapp_api.NaElement)
- self.assertIsInstance(e1.get_child_by_name('nested'),
- netapp_api.NaElement)
-
- def test_setter_child_dict(self):
- """Tests dict is appended as child to root."""
- root = netapp_api.NaElement('root')
- root['d'] = {'e1': 'v1', 'e2': 'v2'}
- e1 = root.get_child_by_name('d')
- self.assertIsInstance(e1, netapp_api.NaElement)
- sub_ch = e1.get_children()
- self.assertEqual(2, len(sub_ch))
- for c in sub_ch:
- self.assertIn(c.get_name(), ['e1', 'e2'])
- if c.get_name() == 'e1':
- self.assertEqual('v1', c.get_content())
- else:
- self.assertEqual('v2', c.get_content())
-
- def test_setter_child_list_tuple(self):
- """Tests list/tuple are appended as child to root."""
- root = netapp_api.NaElement('root')
- root['l'] = ['l1', 'l2']
- root['t'] = ('t1', 't2')
- l = root.get_child_by_name('l')
- self.assertIsInstance(l, netapp_api.NaElement)
- t = root.get_child_by_name('t')
- self.assertIsInstance(t, netapp_api.NaElement)
- for le in l.get_children():
- self.assertIn(le.get_name(), ['l1', 'l2'])
- for te in t.get_children():
- self.assertIn(te.get_name(), ['t1', 't2'])
-
- def test_setter_no_value(self):
- """Tests key with None value."""
- root = netapp_api.NaElement('root')
- root['k'] = None
- self.assertIsNone(root.get_child_content('k'))
-
- def test_setter_invalid_value(self):
- """Tests invalid value raises exception."""
- root = netapp_api.NaElement('root')
- try:
- root['k'] = netapp_api.NaServer('localhost')
- except Exception as e:
- if not isinstance(e, TypeError):
- self.fail(_('Error not a TypeError.'))
-
- def test_setter_invalid_key(self):
- """Tests invalid value raises exception."""
- root = netapp_api.NaElement('root')
- try:
- root[None] = 'value'
- except Exception as e:
- if not isinstance(e, KeyError):
- self.fail(_('Error not a KeyError.'))
import six
from cinder import test
+from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
+ fake_api as netapp_api)
from cinder.tests.unit.volume.drivers.netapp.dataontap import fakes as fake
-from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
from cinder.volume.drivers.netapp.dataontap.client import client_7mode
+from cinder.volume.drivers.netapp.dataontap.client import client_base
from cinder.volume.drivers.netapp import utils as netapp_utils
CONNECTION_INFO = {'hostname': 'hostname',
self.fake_volume = six.text_type(uuid.uuid4())
+ # Inject fake netapp_lib module classes.
+ netapp_api.mock_netapp_lib([client_7mode, netapp_utils, client_base])
+
with mock.patch.object(client_7mode.Client,
'get_ontapi_version',
return_value=(1, 20)):
import six
from cinder import test
+from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
+ fake_api as netapp_api)
import cinder.tests.unit.volume.drivers.netapp.dataontap.fakes as fake
-from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
from cinder.volume.drivers.netapp.dataontap.client import client_base
def setUp(self):
super(NetAppBaseClientTestCase, self).setUp()
+
+ # Inject fake netapp_lib module classes.
+ netapp_api.mock_netapp_lib([client_base])
+
+ self.mock_object(client_base, 'LOG')
self.client = client_base.Client(**CONNECTION_INFO)
self.client.connection = mock.MagicMock()
self.connection = self.client.connection
def test_create_lun_raises_on_failure(self):
self.connection.invoke_successfully = mock.Mock(
side_effect=netapp_api.NaApiError)
+
self.assertRaises(netapp_api.NaApiError,
self.client.create_lun,
self.fake_volume,
from cinder import exception
from cinder import test
-
+from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
+ fake_api as netapp_api)
from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
fakes as fake_client)
from cinder.tests.unit.volume.drivers.netapp.dataontap import fakes as fake
-from cinder.volume.drivers.netapp.dataontap.client import (
- api as netapp_api)
from cinder.volume.drivers.netapp.dataontap.client import client_cmode
from cinder.volume.drivers.netapp import utils as netapp_utils
def setUp(self):
super(NetAppCmodeClientTestCase, self).setUp()
+ # Inject fake netapp_lib module classes.
+ netapp_api.mock_netapp_lib([client_cmode])
+
with mock.patch.object(client_cmode.Client,
'get_ontapi_version',
return_value=(1, 20)):
# License for the specific language governing permissions and limitations
# under the License.
-from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
+from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
+ fake_api as netapp_api)
VOLUME_ID = 'f10d1a84-9b7b-427e-8fec-63c48b509a56'
LUN_ID = 'ee6b4cc7-477b-4016-aa0c-7127b4e3af86'
from cinder import exception
from cinder import test
+from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
+ fake_api as netapp_api)
import cinder.tests.unit.volume.drivers.netapp.dataontap.fakes as fake
import cinder.tests.unit.volume.drivers.netapp.fakes as na_fakes
from cinder.volume.drivers.netapp.dataontap import block_7mode
from cinder.volume.drivers.netapp.dataontap import block_base
-from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
from cinder.volume.drivers.netapp.dataontap.client import client_base
from cinder.volume.drivers.netapp import utils as na_utils
def setUp(self):
super(NetAppBlockStorage7modeLibraryTestCase, self).setUp()
+ # Inject fake netapp_lib module classes.
+ netapp_api.mock_netapp_lib([block_7mode])
+
kwargs = {'configuration': self.get_config_7mode()}
self.library = block_7mode.NetAppBlockStorage7modeLibrary(
'driver', 'protocol', **kwargs)
from cinder import exception
from cinder.i18n import _
from cinder import test
+from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
+ fake_api as netapp_api)
from cinder.tests.unit.volume.drivers.netapp.dataontap import fakes as fake
from cinder.volume.drivers.netapp.dataontap import block_base
-from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
from cinder.volume.drivers.netapp import utils as na_utils
from cinder.volume import utils as volume_utils
def setUp(self):
super(NetAppBlockStorageLibraryTestCase, self).setUp()
+ # Inject fake netapp_lib module classes.
+ netapp_api.mock_netapp_lib([block_base])
+
kwargs = {'configuration': mock.Mock()}
self.library = block_base.NetAppBlockStorageLibrary(
'driver', 'protocol', **kwargs)
from cinder import exception
from cinder import test
+from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
+ fake_api as netapp_api)
import cinder.tests.unit.volume.drivers.netapp.dataontap.fakes as fake
import cinder.tests.unit.volume.drivers.netapp.fakes as na_fakes
from cinder.volume.drivers.netapp.dataontap import block_base
from cinder.volume.drivers.netapp.dataontap import block_cmode
-from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
from cinder.volume.drivers.netapp.dataontap.client import client_base
from cinder.volume.drivers.netapp.dataontap import ssc_cmode
from cinder.volume.drivers.netapp import utils as na_utils
def setUp(self):
super(NetAppBlockStorageCmodeLibraryTestCase, self).setUp()
+ # Inject fake netapp_lib module classes.
+ netapp_api.mock_netapp_lib([block_cmode])
+
kwargs = {'configuration': self.get_config_cmode()}
self.library = block_cmode.NetAppBlockStorageCmodeLibrary(
'driver', 'protocol', **kwargs)
from cinder import exception
from cinder import test
+from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
+ fake_api as netapp_api)
from cinder.tests.unit.volume.drivers.netapp.dataontap import fakes as fake
from cinder.tests.unit.volume.drivers.netapp import fakes as na_fakes
from cinder import utils
-from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
from cinder.volume.drivers.netapp.dataontap.client import client_cmode
from cinder.volume.drivers.netapp.dataontap import nfs_base
from cinder.volume.drivers.netapp.dataontap import nfs_cmode
import cinder.volume.drivers.netapp.options as na_opts
+def mock_netapp_lib(modules):
+ """Inject fake netapp_lib module classes."""
+ netapp_lib = mock.Mock()
+ netapp_lib.api.rest.rest.WebserviceClient = mock.Mock()
+ for module in modules:
+ setattr(module, 'netapp_restclient', netapp_lib.api.rest.rest)
+
MULTIATTACH_HOST_GROUP = {
'clusterRef': '8500000060080E500023C7340036035F515B78FC',
'label': utils.MULTI_ATTACH_HOST_GROUP_NAME,
"targetVolume": "0200000060080E500023C73400000A8C52D10675",
}
+FAKE_ENDPOINT_HTTP = 'http://host:80/endpoint'
+
+FAKE_ENDPOINT_HTTPS = 'https://host:8443/endpoint'
+
+FAKE_CLIENT_PARAMS = {
+ 'scheme': 'http',
+ 'host': '127.0.0.1',
+ 'port': 8080,
+ 'service_path': '/devmgr/vn',
+ 'username': 'rw',
+ 'password': 'rw',
+}
+
def create_configuration_eseries():
config = conf.Configuration(None)
self.mock_log = mock.Mock()
self.mock_object(client, 'LOG', self.mock_log)
self.fake_password = 'mysecret'
+
+ # Inject fake netapp_lib module classes.
+ eseries_fake.mock_netapp_lib([client])
+
self.my_client = client.RestClient('http', 'host', '80', '/test',
'user', self.fake_password,
system_id='fake_sys_id')
+ self.my_client.client._endpoint = eseries_fake.FAKE_ENDPOINT_HTTP
+ self.mock_object(self.my_client, '_eval_response')
+
fake_response = mock.Mock()
fake_response.status_code = 200
self.my_client.invoke_service = mock.Mock(return_value=fake_response)
mock.Mock(return_value='fake_info'))
mock_create_driver = self.mock_object(na_common.NetAppDriver,
'create_driver')
+ mock_check_netapp_lib = self.mock_object(na_utils, 'check_netapp_lib')
config = na_fakes.create_configuration()
config.netapp_storage_family = 'fake_family'
kwargs['app_version'] = 'fake_info'
mock_create_driver.assert_called_with('fake_family', 'fake_protocol',
*(), **kwargs)
+ mock_check_netapp_lib.assert_called_once_with()
def test_new_missing_config(self):
import mock
from oslo_concurrency import processutils as putils
+from oslo_utils import importutils
from cinder import context
from cinder import exception
setattr(configuration, 'flag2', 'value2')
self.assertIsNone(na_utils.check_flags(required_flags, configuration))
+ def test_check_netapp_lib(self):
+ mock_try_import = self.mock_object(importutils, 'try_import')
+
+ na_utils.check_netapp_lib()
+
+ mock_try_import.assert_called_once_with('netapp_lib')
+
+ def test_check_netapp_lib_not_found(self):
+ self.mock_object(importutils,
+ 'try_import',
+ mock.Mock(return_value=None))
+
+ self.assertRaises(exception.NetAppDriverException,
+ na_utils.check_netapp_lib)
+
def test_to_bool(self):
self.assertTrue(na_utils.to_bool(True))
self.assertTrue(na_utils.to_bool('true'))
config.append_config_values(options.netapp_proxy_opts)
na_utils.check_flags(NetAppDriver.REQUIRED_FLAGS, config)
+ na_utils.check_netapp_lib()
app_version = na_utils.OpenStackInfo().info()
LOG.info(_LI('OpenStack OS Version Info: %(info)s'),
from oslo_log import log as logging
from oslo_utils import excutils
+from oslo_utils import importutils
from oslo_utils import units
import six
from cinder import exception
from cinder.i18n import _, _LE, _LI, _LW
from cinder import utils
-from cinder.volume.drivers.netapp.dataontap.client import api as na_api
from cinder.volume.drivers.netapp import options as na_opts
from cinder.volume.drivers.netapp import utils as na_utils
from cinder.volume import utils as volume_utils
from cinder.zonemanager import utils as fczm_utils
+netapp_lib = importutils.try_import('netapp_lib')
+if netapp_lib:
+ from netapp_lib.api.zapi import zapi as netapp_api
+
LOG = logging.getLogger(__name__)
{'ig_nm': igroup_name, 'ig_os': ig_host_os})
try:
return self.zapi_client.map_lun(path, igroup_name, lun_id=lun_id)
- except na_api.NaApiError:
+ except netapp_api.NaApiError:
exc_info = sys.exc_info()
(_igroup, lun_id) = self._find_mapped_lun_igroup(path,
initiator_list)
+++ /dev/null
-# Copyright (c) 2012 NetApp, Inc. All rights reserved.
-# Copyright (c) 2014 Navneet Singh. All rights reserved.
-# Copyright (c) 2014 Glenn Gobeli. All rights reserved.
-# Copyright (c) 2014 Clinton Knight. All rights reserved.
-# Copyright (c) 2015 Alex Meade. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-"""
-NetApp API for Data ONTAP and OnCommand DFM.
-
-Contains classes required to issue API calls to Data ONTAP and OnCommand DFM.
-"""
-
-import copy
-
-from lxml import etree
-from oslo_log import log as logging
-import six
-from six.moves import urllib
-
-from cinder import exception
-from cinder.i18n import _
-from cinder import utils
-
-LOG = logging.getLogger(__name__)
-
-ESIS_CLONE_NOT_LICENSED = '14956'
-
-
-class NaServer(object):
- """Encapsulates server connection logic."""
-
- TRANSPORT_TYPE_HTTP = 'http'
- TRANSPORT_TYPE_HTTPS = 'https'
- SERVER_TYPE_FILER = 'filer'
- SERVER_TYPE_DFM = 'dfm'
- URL_FILER = 'servlets/netapp.servlets.admin.XMLrequest_filer'
- URL_DFM = 'apis/XMLrequest'
- NETAPP_NS = 'http://www.netapp.com/filer/admin'
- STYLE_LOGIN_PASSWORD = 'basic_auth'
- STYLE_CERTIFICATE = 'certificate_auth'
-
- def __init__(self, host, server_type=SERVER_TYPE_FILER,
- transport_type=TRANSPORT_TYPE_HTTP,
- style=STYLE_LOGIN_PASSWORD, username=None,
- password=None, port=None):
- self._host = host
- self.set_server_type(server_type)
- self.set_transport_type(transport_type)
- self.set_style(style)
- if port:
- self.set_port(port)
- self._username = username
- self._password = password
- self._refresh_conn = True
-
- LOG.debug('Using NetApp controller: %s', self._host)
-
- def get_transport_type(self):
- """Get the transport type protocol."""
- return self._protocol
-
- def set_transport_type(self, transport_type):
- """Set the transport type protocol for API.
-
- Supports http and https transport types.
- """
- if transport_type.lower() not in (
- NaServer.TRANSPORT_TYPE_HTTP,
- NaServer.TRANSPORT_TYPE_HTTPS):
- raise ValueError('Unsupported transport type')
- self._protocol = transport_type.lower()
- if self._protocol == NaServer.TRANSPORT_TYPE_HTTP:
- if self._server_type == NaServer.SERVER_TYPE_FILER:
- self.set_port(80)
- else:
- self.set_port(8088)
- else:
- if self._server_type == NaServer.SERVER_TYPE_FILER:
- self.set_port(443)
- else:
- self.set_port(8488)
- self._refresh_conn = True
-
- def get_style(self):
- """Get the authorization style for communicating with the server."""
- return self._auth_style
-
- def set_style(self, style):
- """Set the authorization style for communicating with the server.
-
- Supports basic_auth for now. Certificate_auth mode to be done.
- """
- if style.lower() not in (NaServer.STYLE_LOGIN_PASSWORD,
- NaServer.STYLE_CERTIFICATE):
- raise ValueError('Unsupported authentication style')
- self._auth_style = style.lower()
-
- def get_server_type(self):
- """Get the target server type."""
- return self._server_type
-
- def set_server_type(self, server_type):
- """Set the target server type.
-
- Supports filer and dfm server types.
- """
- if server_type.lower() not in (NaServer.SERVER_TYPE_FILER,
- NaServer.SERVER_TYPE_DFM):
- raise ValueError('Unsupported server type')
- self._server_type = server_type.lower()
- if self._server_type == NaServer.SERVER_TYPE_FILER:
- self._url = NaServer.URL_FILER
- else:
- self._url = NaServer.URL_DFM
- self._ns = NaServer.NETAPP_NS
- self._refresh_conn = True
-
- def set_api_version(self, major, minor):
- """Set the API version."""
- try:
- self._api_major_version = int(major)
- self._api_minor_version = int(minor)
- self._api_version = six.text_type(major) + "." + \
- six.text_type(minor)
- except ValueError:
- raise ValueError('Major and minor versions must be integers')
- self._refresh_conn = True
-
- def get_api_version(self):
- """Gets the API version tuple."""
- if hasattr(self, '_api_version'):
- return (self._api_major_version, self._api_minor_version)
- return None
-
- def set_port(self, port):
- """Set the server communication port."""
- try:
- int(port)
- except ValueError:
- raise ValueError('Port must be integer')
- self._port = six.text_type(port)
- self._refresh_conn = True
-
- def get_port(self):
- """Get the server communication port."""
- return self._port
-
- def set_timeout(self, seconds):
- """Sets the timeout in seconds."""
- try:
- self._timeout = int(seconds)
- except ValueError:
- raise ValueError('timeout in seconds must be integer')
-
- def get_timeout(self):
- """Gets the timeout in seconds if set."""
- if hasattr(self, '_timeout'):
- return self._timeout
- return None
-
- def get_vfiler(self):
- """Get the vfiler to use in tunneling."""
- return self._vfiler
-
- def set_vfiler(self, vfiler):
- """Set the vfiler to use if tunneling gets enabled."""
- self._vfiler = vfiler
-
- def get_vserver(self):
- """Get the vserver to use in tunneling."""
- return self._vserver
-
- def set_vserver(self, vserver):
- """Set the vserver to use if tunneling gets enabled."""
- self._vserver = vserver
-
- def set_username(self, username):
- """Set the user name for authentication."""
- self._username = username
- self._refresh_conn = True
-
- def set_password(self, password):
- """Set the password for authentication."""
- self._password = password
- self._refresh_conn = True
-
- def invoke_elem(self, na_element, enable_tunneling=False):
- """Invoke the API on the server."""
- return self.send_http_request(na_element, enable_tunneling)
-
- @utils.trace_api
- def send_http_request(self, na_element, enable_tunneling=False):
- """Invoke the API on the server."""
- if na_element and not isinstance(na_element, NaElement):
- ValueError('NaElement must be supplied to invoke API')
-
- request, request_element = self._create_request(na_element,
- enable_tunneling)
-
- if not hasattr(self, '_opener') or not self._opener \
- or self._refresh_conn:
- self._build_opener()
- try:
- if hasattr(self, '_timeout'):
- response = self._opener.open(request, timeout=self._timeout)
- else:
- response = self._opener.open(request)
- except urllib.error.HTTPError as e:
- raise NaApiError(e.code, e.msg)
- except Exception as e:
- raise NaApiError('Unexpected error', e)
-
- response_xml = response.read()
- response_element = self._get_result(response_xml)
-
- return response_element
-
- def invoke_successfully(self, na_element, enable_tunneling=False):
- """Invokes API and checks execution status as success.
-
- Need to set enable_tunneling to True explicitly to achieve it.
- This helps to use same connection instance to enable or disable
- tunneling. The vserver or vfiler should be set before this call
- otherwise tunneling remains disabled.
- """
- result = self.invoke_elem(na_element, enable_tunneling)
- if result.has_attr('status') and result.get_attr('status') == 'passed':
- return result
- code = result.get_attr('errno')\
- or result.get_child_content('errorno')\
- or 'ESTATUSFAILED'
- if code == ESIS_CLONE_NOT_LICENSED:
- msg = 'Clone operation failed: FlexClone not licensed.'
- else:
- msg = result.get_attr('reason')\
- or result.get_child_content('reason')\
- or 'Execution status is failed due to unknown reason'
- raise NaApiError(code, msg)
-
- def _create_request(self, na_element, enable_tunneling=False):
- """Creates request in the desired format."""
- netapp_elem = NaElement('netapp')
- netapp_elem.add_attr('xmlns', self._ns)
- if hasattr(self, '_api_version'):
- netapp_elem.add_attr('version', self._api_version)
- if enable_tunneling:
- self._enable_tunnel_request(netapp_elem)
- netapp_elem.add_child_elem(na_element)
- request_d = netapp_elem.to_string()
- request = urllib.request.Request(
- self._get_url(), data=request_d,
- headers={'Content-Type': 'text/xml', 'charset': 'utf-8'})
- return request, netapp_elem
-
- def _enable_tunnel_request(self, netapp_elem):
- """Enables vserver or vfiler tunneling."""
- if hasattr(self, '_vfiler') and self._vfiler:
- if hasattr(self, '_api_major_version') and \
- hasattr(self, '_api_minor_version') and \
- self._api_major_version >= 1 and \
- self._api_minor_version >= 7:
- netapp_elem.add_attr('vfiler', self._vfiler)
- else:
- raise ValueError('ontapi version has to be atleast 1.7'
- ' to send request to vfiler')
- if hasattr(self, '_vserver') and self._vserver:
- if hasattr(self, '_api_major_version') and \
- hasattr(self, '_api_minor_version') and \
- self._api_major_version >= 1 and \
- self._api_minor_version >= 15:
- netapp_elem.add_attr('vfiler', self._vserver)
- else:
- raise ValueError('ontapi version has to be atleast 1.15'
- ' to send request to vserver')
-
- def _parse_response(self, response):
- """Get the NaElement for the response."""
- if not response:
- raise NaApiError('No response received')
- xml = etree.XML(response)
- return NaElement(xml)
-
- def _get_result(self, response):
- """Gets the call result."""
- processed_response = self._parse_response(response)
- return processed_response.get_child_by_name('results')
-
- def _get_url(self):
- return '%s://%s:%s/%s' % (self._protocol, self._host, self._port,
- self._url)
-
- def _build_opener(self):
- if self._auth_style == NaServer.STYLE_LOGIN_PASSWORD:
- auth_handler = self._create_basic_auth_handler()
- else:
- auth_handler = self._create_certificate_auth_handler()
- opener = urllib.request.build_opener(auth_handler)
- self._opener = opener
-
- def _create_basic_auth_handler(self):
- password_man = urllib.request.HTTPPasswordMgrWithDefaultRealm()
- password_man.add_password(None, self._get_url(), self._username,
- self._password)
- auth_handler = urllib.request.HTTPBasicAuthHandler(password_man)
- return auth_handler
-
- def _create_certificate_auth_handler(self):
- raise NotImplementedError()
-
- def __str__(self):
- return "server: %s" % (self._host)
-
-
-class NaElement(object):
- """Class wraps basic building block for NetApp API request."""
-
- def __init__(self, name):
- """Name of the element or etree.Element."""
- if isinstance(name, etree._Element):
- self._element = name
- else:
- self._element = etree.Element(name)
-
- def get_name(self):
- """Returns the tag name of the element."""
- return self._element.tag
-
- def set_content(self, text):
- """Set the text string for the element."""
- self._element.text = text
-
- def get_content(self):
- """Get the text for the element."""
- return self._element.text
-
- def add_attr(self, name, value):
- """Add the attribute to the element."""
- self._element.set(name, value)
-
- def add_attrs(self, **attrs):
- """Add multiple attributes to the element."""
- for attr in attrs.keys():
- self._element.set(attr, attrs.get(attr))
-
- def add_child_elem(self, na_element):
- """Add the child element to the element."""
- if isinstance(na_element, NaElement):
- self._element.append(na_element._element)
- return
- raise
-
- def get_child_by_name(self, name):
- """Get the child element by the tag name."""
- for child in self._element.iterchildren():
- if child.tag == name or etree.QName(child.tag).localname == name:
- return NaElement(child)
- return None
-
- def get_child_content(self, name):
- """Get the content of the child."""
- for child in self._element.iterchildren():
- if child.tag == name or etree.QName(child.tag).localname == name:
- return child.text
- return None
-
- def get_children(self):
- """Get the children for the element."""
- return [NaElement(el) for el in self._element.iterchildren()]
-
- def has_attr(self, name):
- """Checks whether element has attribute."""
- attributes = self._element.attrib or {}
- return name in attributes.keys()
-
- def get_attr(self, name):
- """Get the attribute with the given name."""
- attributes = self._element.attrib or {}
- return attributes.get(name)
-
- def get_attr_names(self):
- """Returns the list of attribute names."""
- attributes = self._element.attrib or {}
- return attributes.keys()
-
- def add_new_child(self, name, content, convert=False):
- """Add child with tag name and context.
-
- Convert replaces entity refs to chars.
- """
- child = NaElement(name)
- if convert:
- content = NaElement._convert_entity_refs(content)
- child.set_content(content)
- self.add_child_elem(child)
-
- @staticmethod
- def _convert_entity_refs(text):
- """Converts entity refs to chars to handle etree auto conversions."""
- text = text.replace("<", "<")
- text = text.replace(">", ">")
- return text
-
- @staticmethod
- def create_node_with_children(node, **children):
- """Creates and returns named node with children."""
- parent = NaElement(node)
- for child in children.keys():
- parent.add_new_child(child, children.get(child, None))
- return parent
-
- def add_node_with_children(self, node, **children):
- """Creates named node with children."""
- parent = NaElement.create_node_with_children(node, **children)
- self.add_child_elem(parent)
-
- def to_string(self, pretty=False, method='xml', encoding='UTF-8'):
- """Prints the element to string."""
- return etree.tostring(self._element, method=method, encoding=encoding,
- pretty_print=pretty)
-
- def __str__(self):
- return self.to_string(pretty=True)
-
- def __repr__(self):
- return str(self)
-
- def __getitem__(self, key):
- """Dict getter method for NaElement.
-
- Returns NaElement list if present,
- text value in case no NaElement node
- children or attribute value if present.
- """
-
- child = self.get_child_by_name(key)
- if child:
- if child.get_children():
- return child
- else:
- return child.get_content()
- elif self.has_attr(key):
- return self.get_attr(key)
- raise KeyError(_('No element by given name %s.') % (key))
-
- def __setitem__(self, key, value):
- """Dict setter method for NaElement.
-
- Accepts dict, list, tuple, str, int, float and long as valid value.
- """
- if key:
- if value:
- if isinstance(value, NaElement):
- child = NaElement(key)
- child.add_child_elem(value)
- self.add_child_elem(child)
- elif isinstance(value, (str, int, float, long)):
- self.add_new_child(key, six.text_type(value))
- elif isinstance(value, (list, tuple, dict)):
- child = NaElement(key)
- child.translate_struct(value)
- self.add_child_elem(child)
- else:
- raise TypeError(_('Not a valid value for NaElement.'))
- else:
- self.add_child_elem(NaElement(key))
- else:
- raise KeyError(_('NaElement name cannot be null.'))
-
- def translate_struct(self, data_struct):
- """Convert list, tuple, dict to NaElement and appends.
-
- Example usage:
- 1.
- <root>
- <elem1>vl1</elem1>
- <elem2>vl2</elem2>
- <elem3>vl3</elem3>
- </root>
- The above can be achieved by doing
- root = NaElement('root')
- root.translate_struct({'elem1': 'vl1', 'elem2': 'vl2',
- 'elem3': 'vl3'})
- 2.
- <root>
- <elem1>vl1</elem1>
- <elem2>vl2</elem2>
- <elem1>vl3</elem1>
- </root>
- The above can be achieved by doing
- root = NaElement('root')
- root.translate_struct([{'elem1': 'vl1', 'elem2': 'vl2'},
- {'elem1': 'vl3'}])
- """
- if isinstance(data_struct, (list, tuple)):
- for el in data_struct:
- if isinstance(el, (list, tuple, dict)):
- self.translate_struct(el)
- else:
- self.add_child_elem(NaElement(el))
- elif isinstance(data_struct, dict):
- for k in data_struct.keys():
- child = NaElement(k)
- if isinstance(data_struct[k], (dict, list, tuple)):
- child.translate_struct(data_struct[k])
- else:
- if data_struct[k]:
- child.set_content(six.text_type(data_struct[k]))
- self.add_child_elem(child)
- else:
- raise ValueError(_('Type cannot be converted into NaElement.'))
-
-
-class NaApiError(Exception):
- """Base exception class for NetApp API errors."""
-
- def __init__(self, code='unknown', message='unknown'):
- self.code = code
- self.message = message
-
- def __str__(self, *args, **kwargs):
- return 'NetApp API failed. Reason - %s:%s' % (self.code, self.message)
-
-
-NaErrors = {'API_NOT_FOUND': NaApiError('13005', 'Unable to find API'),
- 'INSUFFICIENT_PRIVS': NaApiError('13003',
- 'Insufficient privileges')}
-
-
-def invoke_api(na_server, api_name, api_family='cm', query=None,
- des_result=None, additional_elems=None,
- is_iter=False, records=0, tag=None,
- timeout=0, tunnel=None):
- """Invokes any given API call to a NetApp server.
-
- :param na_server: na_server instance
- :param api_name: API name string
- :param api_family: cm or 7m
- :param query: API query as dict
- :param des_result: desired result as dict
- :param additional_elems: dict other than query and des_result
- :param is_iter: is iterator API
- :param records: limit for records, 0 for infinite
- :param timeout: timeout seconds
- :param tunnel: tunnel entity, vserver or vfiler name
- """
- record_step = 50
- if not (na_server or isinstance(na_server, NaServer)):
- msg = _("Requires an NaServer instance.")
- raise exception.InvalidInput(reason=msg)
- server = copy.copy(na_server)
- if api_family == 'cm':
- server.set_vserver(tunnel)
- else:
- server.set_vfiler(tunnel)
- if timeout > 0:
- server.set_timeout(timeout)
- iter_records = 0
- cond = True
- while cond:
- na_element = create_api_request(
- api_name, query, des_result, additional_elems,
- is_iter, record_step, tag)
- result = server.invoke_successfully(na_element, True)
- if is_iter:
- if records > 0:
- iter_records = iter_records + record_step
- if iter_records >= records:
- cond = False
- tag_el = result.get_child_by_name('next-tag')
- tag = tag_el.get_content() if tag_el else None
- if not tag:
- cond = False
- else:
- cond = False
- yield result
-
-
-def create_api_request(api_name, query=None, des_result=None,
- additional_elems=None, is_iter=False,
- record_step=50, tag=None):
- """Creates a NetApp API request.
-
- :param api_name: API name string
- :param query: API query as dict
- :param des_result: desired result as dict
- :param additional_elems: dict other than query and des_result
- :param is_iter: is iterator API
- :param record_step: records at a time for iter API
- :param tag: next tag for iter API
- """
- api_el = NaElement(api_name)
- if query:
- query_el = NaElement('query')
- query_el.translate_struct(query)
- api_el.add_child_elem(query_el)
- if des_result:
- res_el = NaElement('desired-attributes')
- res_el.translate_struct(des_result)
- api_el.add_child_elem(res_el)
- if additional_elems:
- api_el.translate_struct(additional_elems)
- if is_iter:
- api_el.add_new_child('max-records', six.text_type(record_step))
- if tag:
- api_el.add_new_child('tag', tag, True)
- return api_el
import time
from oslo_log import log as logging
+from oslo_utils import importutils
import six
from cinder import exception
from cinder.i18n import _, _LW
from cinder import utils
-from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
from cinder.volume.drivers.netapp.dataontap.client import client_base
+netapp_lib = importutils.try_import('netapp_lib')
+if netapp_lib:
+ from netapp_lib.api.zapi import zapi as netapp_api
+
LOG = logging.getLogger(__name__)
from oslo_log import log as logging
from oslo_utils import excutils
+from oslo_utils import importutils
from oslo_utils import timeutils
+
import six
from cinder.i18n import _LE, _LW, _LI
from cinder import utils
-from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
+
+netapp_lib = importutils.try_import('netapp_lib')
+if netapp_lib:
+ from netapp_lib.api.zapi import zapi as netapp_api
LOG = logging.getLogger(__name__)
import math
from oslo_log import log as logging
+from oslo_utils import importutils
import six
from cinder import exception
from cinder.i18n import _, _LW
from cinder import utils
-from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
from cinder.volume.drivers.netapp.dataontap.client import client_base
from cinder.volume.drivers.netapp import utils as na_utils
+netapp_lib = importutils.try_import('netapp_lib')
+if netapp_lib:
+ from netapp_lib.api.zapi import errors as netapp_error
+ from netapp_lib.api.zapi import zapi as netapp_api
+
LOG = logging.getLogger(__name__)
DELETED_PREFIX = 'deleted_cinder_'
self.connection.invoke_successfully(na_el)
except Exception as e:
if isinstance(e, netapp_api.NaApiError):
- if (e.code == netapp_api.NaErrors
- ['API_NOT_FOUND'].code or
- e.code == netapp_api.NaErrors
- ['INSUFFICIENT_PRIVS'].code):
+ if(e.code == netapp_error.EAPINOTFOUND
+ or e.code == netapp_error.EAPIPRIVILEGE):
failed_apis.append(api_name)
elif major == 1 and minor >= 20:
failed_apis = copy.copy(api_list)
import threading
from oslo_log import log as logging
+from oslo_utils import importutils
from oslo_utils import timeutils
import six
from cinder import exception
from cinder.i18n import _, _LI, _LW
from cinder import utils
-from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
from cinder.volume.drivers.netapp import utils as na_utils
+netapp_lib = importutils.try_import('netapp_lib')
+if netapp_lib:
+ from netapp_lib.api.zapi import zapi as netapp_api
+
LOG = logging.getLogger(__name__)
Currently queries for raid and ha-policy.
"""
-
add_elems = {'aggregate': aggr_name}
attrs = {}
try:
import uuid
from oslo_log import log as logging
-import requests
+from oslo_utils import importutils
import six
from six.moves import urllib
from cinder import exception
-from cinder.i18n import _, _LE
+from cinder.i18n import _
import cinder.utils as cinder_utils
from cinder.volume.drivers.netapp.eseries import utils
+netapp_lib = importutils.try_import('netapp_lib')
+if netapp_lib:
+ from netapp_lib.api.rest import rest as netapp_restclient
-LOG = logging.getLogger(__name__)
-
-
-class WebserviceClient(object):
- """Base client for e-series web services."""
- def __init__(self, scheme, host, port, service_path, username,
- password, **kwargs):
- self._validate_params(scheme, host, port)
- self._create_endpoint(scheme, host, port, service_path)
- self._username = username
- self._password = password
- self._init_connection()
-
- def _validate_params(self, scheme, host, port):
- """Does some basic validation for web service params."""
- if host is None or port is None or scheme is None:
- msg = _("One of the required inputs from host, port"
- " or scheme not found.")
- raise exception.InvalidInput(reason=msg)
- if scheme not in ('http', 'https'):
- raise exception.InvalidInput(reason=_("Invalid transport type."))
-
- def _create_endpoint(self, scheme, host, port, service_path):
- """Creates end point url for the service."""
- netloc = '%s:%s' % (host, port)
- self._endpoint = urllib.parse.urlunparse((scheme, netloc, service_path,
- None, None, None))
-
- def _init_connection(self):
- """Do client specific set up for session and connection pooling."""
- self.conn = requests.Session()
- if self._username and self._password:
- self.conn.auth = (self._username, self._password)
-
- def invoke_service(self, method='GET', url=None, params=None, data=None,
- headers=None, timeout=None, verify=False):
- url = url or self._endpoint
- try:
- response = self.conn.request(method, url, params, data,
- headers=headers, timeout=timeout,
- verify=verify)
- # Catching error conditions other than the perceived ones.
- # Helps propagating only known exceptions back to the caller.
- except Exception as e:
- LOG.exception(_LE("Unexpected error while invoking web service."
- " Error - %s."), e)
- raise exception.NetAppDriverException(
- _("Invoking web service failed."))
- return response
+LOG = logging.getLogger(__name__)
-class RestClient(WebserviceClient):
+class RestClient(object):
"""REST client specific to e-series storage service."""
def __init__(self, scheme, host, port, service_path, username,
password, **kwargs):
- super(RestClient, self).__init__(scheme, host, port, service_path,
- username, password, **kwargs)
+
kwargs = kwargs or {}
+ self.client = netapp_restclient.WebserviceClient(scheme, host, port,
+ service_path,
+ username, password,
+ **kwargs)
self._system_id = kwargs.get('system_id')
self._content_type = kwargs.get('content_type') or 'json'
raise exception.NotFound(_('Storage system id not set.'))
kwargs['system-id'] = self._system_id
path = path.format(**kwargs)
- if not self._endpoint.endswith('/'):
- self._endpoint = '%s/' % self._endpoint
- return urllib.parse.urljoin(self._endpoint, path.lstrip('/'))
+ if not self.client._endpoint.endswith('/'):
+ self.client._endpoint = '%s/' % self.client._endpoint
+ return urllib.parse.urljoin(self.client._endpoint, path.lstrip('/'))
def _invoke(self, method, path, data=None, use_system=True,
timeout=None, verify=False, **kwargs):
if cinder_utils.TRACE_API:
self._log_http_request(method, url, headers, data)
data = json.dumps(data) if data else None
- res = self.invoke_service(method, url, data=data,
- headers=headers,
- timeout=timeout, verify=verify)
+ res = self.client.invoke_service(method, url, data=data,
+ headers=headers,
+ timeout=timeout, verify=verify)
res_dict = res.json() if res.text else None
if cinder_utils.TRACE_API:
from oslo_concurrency import processutils as putils
from oslo_log import log as logging
+from oslo_utils import importutils
import six
from cinder import context
raise exception.InvalidInput(reason=msg)
+def check_netapp_lib():
+ if not importutils.try_import('netapp_lib'):
+ msg = ('You have not installed the NetApp API Library for OpenStack. '
+ 'Please install it using "sudo pip install netapp-lib" and '
+ 'restart this service!')
+ raise exception.NetAppDriverException(msg)
+
+
def to_bool(val):
"""Converts true, yes, y, 1 to True, False otherwise."""
if val: