Revert use of netapp_lib from NetApp Drivers
This patch cleanly reverts the changes made via the
commit: e681ba2a99
cleanly and mitigates the conflicts that would
occur with git-revert on the said commit.
The revert is solely for changes pertaining to the use
of the external library, netapp_lib. Minor code refactors
from the prior change are retained.
Unit test coverage has been increased for ZAPI and REST
interface code in netapp/dataontap/client/api.py and
netapp/eseries/client.py.
Closes-Bug: #1499334
Change-Id: Icead7e168e1c7187840de87c69365d26aedd5924
This commit is contained in:
parent
40237051c8
commit
ff81307ca4
|
@ -23,8 +23,6 @@ from six.moves import http_client
|
|||
|
||||
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
|
||||
from cinder.volume import configuration as conf
|
||||
from cinder.volume.drivers.netapp import common
|
||||
|
@ -561,10 +559,6 @@ class NetAppDirectCmodeISCSIDriverTestCase(test.TestCase):
|
|||
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',
|
||||
|
@ -785,10 +779,6 @@ class NetAppDriverNegativeTestCase(test.TestCase):
|
|||
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()
|
||||
|
@ -1252,10 +1242,6 @@ class NetAppDirect7modeISCSIDriverTestCase_NV(test.TestCase):
|
|||
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',
|
||||
|
@ -1316,10 +1302,6 @@ class NetAppDirect7modeISCSIDriverTestCase_WV(
|
|||
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',
|
||||
|
|
|
@ -668,10 +668,6 @@ class NetAppEseriesISCSIDriverTestCase(test.TestCase):
|
|||
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
|
||||
|
|
|
@ -27,8 +27,6 @@ import six
|
|||
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
|
||||
|
@ -36,6 +34,7 @@ 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 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.dataontap.client import client_cmode
|
||||
|
@ -159,10 +158,6 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
|
|||
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()
|
||||
|
@ -1472,10 +1467,6 @@ class NetApp7modeNfsDriverTestCase(NetAppCmodeNfsDriverTestCase):
|
|||
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(
|
||||
|
|
|
@ -25,8 +25,7 @@ from six.moves import http_client
|
|||
|
||||
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.drivers.netapp.dataontap.client import api as netapp_api
|
||||
from cinder.volume.drivers.netapp.dataontap import ssc_cmode
|
||||
|
||||
|
||||
|
@ -375,7 +374,6 @@ class SscUtilsTestCase(test.TestCase):
|
|||
|
||||
def setUp(self):
|
||||
super(SscUtilsTestCase, self).setUp()
|
||||
netapp_api.mock_netapp_lib([ssc_cmode])
|
||||
self.stubs.Set(http_client, 'HTTPConnection',
|
||||
FakeDirectCmodeHTTPConnection)
|
||||
|
||||
|
|
|
@ -1,238 +0,0 @@
|
|||
# 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.')
|
|
@ -14,7 +14,83 @@
|
|||
|
||||
|
||||
from lxml import etree
|
||||
import mock
|
||||
from six.moves import urllib
|
||||
|
||||
import cinder.volume.drivers.netapp.dataontap.client.api as netapp_api
|
||||
|
||||
|
||||
FAKE_VOL_XML = """<volume-info xmlns='http://www.netapp.com/filer/admin'>
|
||||
<name>open123</name>
|
||||
<state>online</state>
|
||||
<size-total>0</size-total>
|
||||
<size-used>0</size-used>
|
||||
<size-available>0</size-available>
|
||||
<is-inconsistent>false</is-inconsistent>
|
||||
<is-invalid>false</is-invalid>
|
||||
</volume-info>"""
|
||||
|
||||
FAKE_XML1 = """<options>\
|
||||
<test1>abc</test1>\
|
||||
<test2>abc</test2>\
|
||||
</options>"""
|
||||
|
||||
FAKE_XML2 = """<root><options>somecontent</options></root>"""
|
||||
|
||||
FAKE_NA_ELEMENT = netapp_api.NaElement(etree.XML(FAKE_VOL_XML))
|
||||
|
||||
FAKE_INVOKE_DATA = 'somecontent'
|
||||
|
||||
FAKE_XML_STR = 'abc'
|
||||
|
||||
FAKE_API_NAME = 'volume-get-iter'
|
||||
|
||||
FAKE_API_NAME_ELEMENT = netapp_api.NaElement(FAKE_API_NAME)
|
||||
|
||||
FAKE_NA_SERVER_STR = '127.0.0.1'
|
||||
|
||||
FAKE_NA_SERVER = netapp_api.NaServer(FAKE_NA_SERVER_STR)
|
||||
|
||||
FAKE_NA_SERVER_API_1_5 = netapp_api.NaServer(FAKE_NA_SERVER_STR)
|
||||
FAKE_NA_SERVER_API_1_5.set_vfiler('filer')
|
||||
FAKE_NA_SERVER_API_1_5.set_api_version(1, 5)
|
||||
|
||||
|
||||
FAKE_NA_SERVER_API_1_14 = netapp_api.NaServer(FAKE_NA_SERVER_STR)
|
||||
FAKE_NA_SERVER_API_1_14.set_vserver('server')
|
||||
FAKE_NA_SERVER_API_1_14.set_api_version(1, 14)
|
||||
|
||||
|
||||
FAKE_NA_SERVER_API_1_20 = netapp_api.NaServer(FAKE_NA_SERVER_STR)
|
||||
FAKE_NA_SERVER_API_1_20.set_vfiler('filer')
|
||||
FAKE_NA_SERVER_API_1_20.set_vserver('server')
|
||||
FAKE_NA_SERVER_API_1_20.set_api_version(1, 20)
|
||||
|
||||
|
||||
FAKE_QUERY = {'volume-attributes': None}
|
||||
|
||||
FAKE_DES_ATTR = {'volume-attributes': ['volume-id-attributes',
|
||||
'volume-space-attributes',
|
||||
'volume-state-attributes',
|
||||
'volume-qos-attributes']}
|
||||
|
||||
FAKE_CALL_ARGS_LIST = [mock.call(80), mock.call(8088), mock.call(443),
|
||||
mock.call(8488)]
|
||||
|
||||
FAKE_RESULT_API_ERR_REASON = netapp_api.NaElement('result')
|
||||
FAKE_RESULT_API_ERR_REASON.add_attr('errno', '000')
|
||||
FAKE_RESULT_API_ERR_REASON.add_attr('reason', 'fake_reason')
|
||||
|
||||
FAKE_RESULT_API_ERRNO_INVALID = netapp_api.NaElement('result')
|
||||
FAKE_RESULT_API_ERRNO_INVALID.add_attr('errno', '000')
|
||||
|
||||
FAKE_RESULT_API_ERRNO_VALID = netapp_api.NaElement('result')
|
||||
FAKE_RESULT_API_ERRNO_VALID.add_attr('errno', '14956')
|
||||
|
||||
FAKE_RESULT_SUCCESS = netapp_api.NaElement('result')
|
||||
FAKE_RESULT_SUCCESS.add_attr('status', 'passed')
|
||||
|
||||
FAKE_HTTP_OPENER = urllib.request.build_opener()
|
||||
|
||||
GET_OPERATIONAL_NETWORK_INTERFACE_ADDRESSES_RESPONSE = etree.XML("""
|
||||
<results status="passed">
|
||||
|
|
|
@ -0,0 +1,509 @@
|
|||
# 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
|
||||
"""
|
||||
import ddt
|
||||
from lxml import etree
|
||||
import mock
|
||||
import six
|
||||
from six.moves import urllib
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder import test
|
||||
from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
|
||||
fakes as zapi_fakes)
|
||||
from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class NetAppApiServerTests(test.TestCase):
|
||||
"""Test case for NetApp API server methods"""
|
||||
def setUp(self):
|
||||
self.root = netapp_api.NaServer('127.0.0.1')
|
||||
super(NetAppApiServerTests, self).setUp()
|
||||
|
||||
@ddt.data(None, 'ftp')
|
||||
def test_set_transport_type_value_error(self, transport_type):
|
||||
"""Tests setting an invalid transport type"""
|
||||
self.assertRaises(ValueError, self.root.set_transport_type,
|
||||
transport_type)
|
||||
|
||||
@ddt.data({'params': {'transport_type': 'http',
|
||||
'server_type_filer': 'filer'}},
|
||||
{'params': {'transport_type': 'http',
|
||||
'server_type_filer': 'xyz'}},
|
||||
{'params': {'transport_type': 'https',
|
||||
'server_type_filer': 'filer'}},
|
||||
{'params': {'transport_type': 'https',
|
||||
'server_type_filer': 'xyz'}})
|
||||
@ddt.unpack
|
||||
def test_set_transport_type_valid(self, params):
|
||||
"""Tests setting a valid transport type"""
|
||||
self.root._server_type = params['server_type_filer']
|
||||
mock_invoke = self.mock_object(self.root, 'set_port')
|
||||
|
||||
self.root.set_transport_type(params['transport_type'])
|
||||
|
||||
expected_call_args = zapi_fakes.FAKE_CALL_ARGS_LIST
|
||||
|
||||
self.assertTrue(mock_invoke.call_args in expected_call_args)
|
||||
|
||||
@ddt.data('stor', 'STORE', '')
|
||||
def test_set_server_type_value_error(self, server_type):
|
||||
"""Tests Value Error on setting the wrong server type"""
|
||||
self.assertRaises(ValueError, self.root.set_server_type, server_type)
|
||||
|
||||
@ddt.data('!&', '80na', '')
|
||||
def test_set_port__value_error(self, port):
|
||||
"""Tests Value Error on trying to set port with a non-integer"""
|
||||
self.assertRaises(ValueError, self.root.set_port, port)
|
||||
|
||||
@ddt.data('!&', '80na', '')
|
||||
def test_set_timeout_value_error(self, timeout):
|
||||
"""Tests Value Error on trying to set port with a non-integer"""
|
||||
self.assertRaises(ValueError, self.root.set_timeout, timeout)
|
||||
|
||||
@ddt.data({'params': {'major': 1, 'minor': '20a'}},
|
||||
{'params': {'major': '20a', 'minor': 1}},
|
||||
{'params': {'major': '!*', 'minor': '20a'}})
|
||||
@ddt.unpack
|
||||
def test_set_api_version_value_error(self, params):
|
||||
"""Tests Value Error on setting non-integer version"""
|
||||
self.assertRaises(ValueError, self.root.set_api_version, **params)
|
||||
|
||||
def test_set_api_version_valid(self):
|
||||
"""Tests Value Error on setting non-integer version"""
|
||||
args = {'major': '20', 'minor': 1}
|
||||
|
||||
expected_call_args_list = [mock.call('20'), mock.call(1)]
|
||||
|
||||
mock_invoke = self.mock_object(six, 'text_type',
|
||||
mock.Mock(return_value='str'))
|
||||
self.root.set_api_version(**args)
|
||||
|
||||
self.assertEqual(expected_call_args_list, mock_invoke.call_args_list)
|
||||
|
||||
@ddt.data({'params': {'result': zapi_fakes.FAKE_RESULT_API_ERR_REASON}},
|
||||
{'params': {'result': zapi_fakes.FAKE_RESULT_API_ERRNO_INVALID}},
|
||||
{'params': {'result': zapi_fakes.FAKE_RESULT_API_ERRNO_VALID}})
|
||||
@ddt.unpack
|
||||
def test_invoke_successfully_naapi_error(self, params):
|
||||
"""Tests invoke successfully raising NaApiError"""
|
||||
self.mock_object(self.root, 'invoke_elem',
|
||||
mock.Mock(return_value=params['result']))
|
||||
|
||||
self.assertRaises(netapp_api.NaApiError,
|
||||
self.root.invoke_successfully,
|
||||
zapi_fakes.FAKE_NA_ELEMENT)
|
||||
|
||||
def test_invoke_successfully_no_error(self):
|
||||
"""Tests invoke successfully with no errors"""
|
||||
self.mock_object(self.root, 'invoke_elem', mock.Mock(
|
||||
return_value=zapi_fakes.FAKE_RESULT_SUCCESS))
|
||||
|
||||
self.assertEqual(zapi_fakes.FAKE_RESULT_SUCCESS.to_string(),
|
||||
self.root.invoke_successfully(
|
||||
zapi_fakes.FAKE_NA_ELEMENT).to_string())
|
||||
|
||||
def test__create_request(self):
|
||||
"""Tests method _create_request"""
|
||||
self.root._ns = zapi_fakes.FAKE_XML_STR
|
||||
self.root._api_version = '1.20'
|
||||
self.mock_object(self.root, '_enable_tunnel_request')
|
||||
self.mock_object(netapp_api.NaElement, 'add_child_elem')
|
||||
self.mock_object(netapp_api.NaElement, 'to_string',
|
||||
mock.Mock(return_value=zapi_fakes.FAKE_XML_STR))
|
||||
mock_invoke = self.mock_object(urllib.request, 'Request')
|
||||
|
||||
self.root._create_request(zapi_fakes.FAKE_NA_ELEMENT, True)
|
||||
|
||||
self.assertTrue(mock_invoke.called)
|
||||
|
||||
@ddt.data({'params': {'server': zapi_fakes.FAKE_NA_SERVER_API_1_5}},
|
||||
{'params': {'server': zapi_fakes.FAKE_NA_SERVER_API_1_14}})
|
||||
@ddt.unpack
|
||||
def test__enable_tunnel_request__value_error(self, params):
|
||||
"""Tests value errors with creating tunnel request"""
|
||||
|
||||
self.assertRaises(ValueError, params['server']._enable_tunnel_request,
|
||||
'test')
|
||||
|
||||
def test__enable_tunnel_request_valid(self):
|
||||
"""Tests creating tunnel request with correct values"""
|
||||
netapp_elem = zapi_fakes.FAKE_NA_ELEMENT
|
||||
server = zapi_fakes.FAKE_NA_SERVER_API_1_20
|
||||
mock_invoke = self.mock_object(netapp_elem, 'add_attr')
|
||||
expected_call_args = [mock.call('vfiler', 'filer'),
|
||||
mock.call('vfiler', 'server')]
|
||||
|
||||
server._enable_tunnel_request(netapp_elem)
|
||||
|
||||
self.assertEqual(expected_call_args, mock_invoke.call_args_list)
|
||||
|
||||
def test__parse_response__naapi_error(self):
|
||||
"""Tests NaApiError on no response"""
|
||||
self.assertRaises(netapp_api.NaApiError,
|
||||
self.root._parse_response, None)
|
||||
|
||||
def test__parse_response_no_error(self):
|
||||
"""Tests parse function with appropriate response"""
|
||||
mock_invoke = self.mock_object(etree, 'XML', mock.Mock(
|
||||
return_value='xml'))
|
||||
|
||||
self.root._parse_response(zapi_fakes.FAKE_XML_STR)
|
||||
|
||||
mock_invoke.assert_called_with(zapi_fakes.FAKE_XML_STR)
|
||||
|
||||
def test__build_opener_not_implemented_error(self):
|
||||
"""Tests whether certificate style authorization raises Exception"""
|
||||
self.root._auth_style = 'not_basic_auth'
|
||||
|
||||
self.assertRaises(NotImplementedError, self.root._build_opener)
|
||||
|
||||
def test__build_opener_valid(self):
|
||||
"""Tests whether build opener works with valid parameters"""
|
||||
self.root._auth_style = 'basic_auth'
|
||||
mock_invoke = self.mock_object(urllib.request, 'build_opener')
|
||||
|
||||
self.root._build_opener()
|
||||
|
||||
self.assertTrue(mock_invoke.called)
|
||||
|
||||
@ddt.data(None, zapi_fakes.FAKE_XML_STR)
|
||||
def test_invoke_elem_value_error(self, na_element):
|
||||
"""Tests whether invalid NaElement parameter causes error"""
|
||||
|
||||
self.assertRaises(ValueError, self.root.invoke_elem, na_element)
|
||||
|
||||
def test_invoke_elem_http_error(self):
|
||||
"""Tests handling of HTTPError"""
|
||||
na_element = zapi_fakes.FAKE_NA_ELEMENT
|
||||
self.mock_object(self.root, '_create_request', mock.Mock(
|
||||
return_value=('abc', zapi_fakes.FAKE_NA_ELEMENT)))
|
||||
self.mock_object(netapp_api, 'LOG')
|
||||
self.root._opener = zapi_fakes.FAKE_HTTP_OPENER
|
||||
self.mock_object(self.root, '_build_opener')
|
||||
self.mock_object(self.root._opener, 'open', mock.Mock(
|
||||
side_effect=urllib.error.HTTPError(url='', hdrs='',
|
||||
fp=None, code='401',
|
||||
msg='httperror')))
|
||||
|
||||
self.assertRaises(netapp_api.NaApiError, self.root.invoke_elem,
|
||||
na_element)
|
||||
|
||||
def test_invoke_elem_unknown_exception(self):
|
||||
"""Tests handling of Unknown Exception"""
|
||||
na_element = zapi_fakes.FAKE_NA_ELEMENT
|
||||
self.mock_object(self.root, '_create_request', mock.Mock(
|
||||
return_value=('abc', zapi_fakes.FAKE_NA_ELEMENT)))
|
||||
self.mock_object(netapp_api, 'LOG')
|
||||
self.root._opener = zapi_fakes.FAKE_HTTP_OPENER
|
||||
self.mock_object(self.root, '_build_opener')
|
||||
self.mock_object(self.root._opener, 'open', mock.Mock(
|
||||
side_effect=Exception))
|
||||
|
||||
self.assertRaises(netapp_api.NaApiError, self.root.invoke_elem,
|
||||
na_element)
|
||||
|
||||
def test_invoke_elem_valid(self):
|
||||
"""Tests the method invoke_elem with valid parameters"""
|
||||
na_element = zapi_fakes.FAKE_NA_ELEMENT
|
||||
self.root._trace = True
|
||||
self.mock_object(self.root, '_create_request', mock.Mock(
|
||||
return_value=('abc', zapi_fakes.FAKE_NA_ELEMENT)))
|
||||
self.mock_object(netapp_api, 'LOG')
|
||||
self.root._opener = zapi_fakes.FAKE_HTTP_OPENER
|
||||
self.mock_object(self.root, '_build_opener')
|
||||
self.mock_object(self.root, '_get_result', mock.Mock(
|
||||
return_value=zapi_fakes.FAKE_NA_ELEMENT))
|
||||
opener_mock = self.mock_object(
|
||||
self.root._opener, 'open', mock.Mock())
|
||||
opener_mock.read.side_effect = ['resp1', 'resp2']
|
||||
|
||||
self.root.invoke_elem(na_element)
|
||||
|
||||
|
||||
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.'))
|
||||
|
||||
def test_getter_key_error(self):
|
||||
"""Tests invalid key raises exception"""
|
||||
root = netapp_api.NaElement('root')
|
||||
self.mock_object(root, 'get_child_by_name',
|
||||
mock.Mock(return_value=None))
|
||||
self.mock_object(root, 'has_attr',
|
||||
mock.Mock(return_value=None))
|
||||
|
||||
self.assertRaises(KeyError,
|
||||
netapp_api.NaElement.__getitem__,
|
||||
root, '123')
|
||||
|
||||
def test_getter_na_element_list(self):
|
||||
"""Tests returning NaElement list"""
|
||||
root = netapp_api.NaElement('root')
|
||||
root['key'] = ['val1', 'val2']
|
||||
|
||||
self.assertEqual(root.get_child_by_name('key').get_name(),
|
||||
root.__getitem__('key').get_name())
|
||||
|
||||
def test_getter_child_text(self):
|
||||
"""Tests NaElement having no children"""
|
||||
root = netapp_api.NaElement('root')
|
||||
root.set_content('FAKE_CONTENT')
|
||||
self.mock_object(root, 'get_child_by_name',
|
||||
mock.Mock(return_value=root))
|
||||
|
||||
self.assertEqual('FAKE_CONTENT',
|
||||
root.__getitem__('root'))
|
||||
|
||||
def test_getter_child_attr(self):
|
||||
"""Tests invalid key raises exception"""
|
||||
root = netapp_api.NaElement('root')
|
||||
root.add_attr('val', 'FAKE_VALUE')
|
||||
|
||||
self.assertEqual('FAKE_VALUE',
|
||||
root.__getitem__('val'))
|
||||
|
||||
def test_add_node_with_children(self):
|
||||
"""Tests adding a child node with its own children"""
|
||||
root = netapp_api.NaElement('root')
|
||||
self.mock_object(netapp_api.NaElement,
|
||||
'create_node_with_children',
|
||||
mock.Mock(return_value=zapi_fakes.FAKE_INVOKE_DATA))
|
||||
mock_invoke = self.mock_object(root, 'add_child_elem')
|
||||
|
||||
root.add_node_with_children('options')
|
||||
|
||||
mock_invoke.assert_called_with(zapi_fakes.FAKE_INVOKE_DATA)
|
||||
|
||||
def test_create_node_with_children(self):
|
||||
"""Tests adding a child node with its own children"""
|
||||
root = netapp_api.NaElement('root')
|
||||
self.mock_object(root, 'add_new_child', mock.Mock(return_value='abc'))
|
||||
|
||||
self.assertEqual(zapi_fakes.FAKE_XML1, root.create_node_with_children(
|
||||
'options', test1=zapi_fakes.FAKE_XML_STR,
|
||||
test2=zapi_fakes.FAKE_XML_STR).to_string())
|
||||
|
||||
def test_add_new_child(self):
|
||||
"""Tests adding a child node with its own children"""
|
||||
root = netapp_api.NaElement('root')
|
||||
self.mock_object(netapp_api.NaElement,
|
||||
'_convert_entity_refs',
|
||||
mock.Mock(return_value=zapi_fakes.FAKE_INVOKE_DATA))
|
||||
|
||||
root.add_new_child('options', zapi_fakes.FAKE_INVOKE_DATA)
|
||||
|
||||
self.assertEqual(zapi_fakes.FAKE_XML2, root.to_string())
|
||||
|
||||
def test_get_attr_names_empty_attr(self):
|
||||
"""Tests _elements.attrib being empty"""
|
||||
root = netapp_api.NaElement('root')
|
||||
|
||||
self.assertEqual([], root.get_attr_names())
|
||||
|
||||
def test_get_attr_names(self):
|
||||
"""Tests _elements.attrib being non-empty"""
|
||||
root = netapp_api.NaElement('root')
|
||||
root.add_attr('attr1', 'a1')
|
||||
root.add_attr('attr2', 'a2')
|
||||
|
||||
self.assertEqual(['attr1', 'attr2'], root.get_attr_names())
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class NetAppApiInvokeTests(test.TestCase):
|
||||
"""Test Cases for api request creation and invocation"""
|
||||
|
||||
def setUp(self):
|
||||
super(NetAppApiInvokeTests, self).setUp()
|
||||
|
||||
@ddt.data(None, zapi_fakes.FAKE_XML_STR)
|
||||
def test_invoke_api_invalid_input(self, na_server):
|
||||
"""Tests Zapi Invocation Type Error"""
|
||||
na_server = None
|
||||
api_name = zapi_fakes.FAKE_API_NAME
|
||||
invoke_generator = netapp_api.invoke_api(na_server, api_name)
|
||||
|
||||
self.assertRaises(exception.InvalidInput, invoke_generator.next)
|
||||
|
||||
@ddt.data({'params': {'na_server': zapi_fakes.FAKE_NA_SERVER,
|
||||
'api_name': zapi_fakes.FAKE_API_NAME}},
|
||||
{'params': {'na_server': zapi_fakes.FAKE_NA_SERVER,
|
||||
'api_name': zapi_fakes.FAKE_API_NAME,
|
||||
'api_family': 'cm',
|
||||
'query': zapi_fakes.FAKE_QUERY,
|
||||
'des_result': zapi_fakes.FAKE_DES_ATTR,
|
||||
'additional_elems': None,
|
||||
'is_iter': True}})
|
||||
@ddt.unpack
|
||||
def test_invoke_api_valid(self, params):
|
||||
"""Test invoke_api with valid naserver"""
|
||||
self.mock_object(netapp_api, 'create_api_request', mock.Mock(
|
||||
return_value='success'))
|
||||
self.mock_object(netapp_api.NaServer, 'invoke_successfully',
|
||||
mock.Mock(
|
||||
return_value=netapp_api.NaElement('success')))
|
||||
|
||||
invoke_generator = netapp_api.invoke_api(**params)
|
||||
|
||||
self.assertEqual(netapp_api.NaElement('success').to_string(),
|
||||
invoke_generator.next().to_string())
|
||||
|
||||
def test_create_api_request(self):
|
||||
""""Tests creating api request"""
|
||||
self.mock_object(netapp_api.NaElement, 'translate_struct')
|
||||
self.mock_object(netapp_api.NaElement, 'add_child_elem')
|
||||
|
||||
params = {'api_name': zapi_fakes.FAKE_API_NAME,
|
||||
'query': zapi_fakes.FAKE_QUERY,
|
||||
'des_result': zapi_fakes.FAKE_DES_ATTR,
|
||||
'additional_elems': zapi_fakes.FAKE_XML_STR,
|
||||
'is_iter': True,
|
||||
'tag': 'tag'}
|
||||
|
||||
self.assertEqual(zapi_fakes.FAKE_API_NAME_ELEMENT.to_string(),
|
||||
netapp_api.create_api_request(**params).to_string())
|
|
@ -21,11 +21,9 @@ import mock
|
|||
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',
|
||||
|
@ -42,9 +40,6 @@ class NetApp7modeClientTestCase(test.TestCase):
|
|||
|
||||
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)):
|
||||
|
|
|
@ -20,9 +20,8 @@ import mock
|
|||
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
|
||||
|
||||
|
||||
|
@ -38,9 +37,6 @@ class NetAppBaseClientTestCase(test.TestCase):
|
|||
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()
|
||||
|
|
|
@ -22,11 +22,10 @@ import six
|
|||
|
||||
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
|
||||
|
||||
|
@ -44,9 +43,6 @@ class NetAppCmodeClientTestCase(test.TestCase):
|
|||
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)):
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
from lxml import etree
|
||||
|
||||
from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
|
||||
fake_api as netapp_api)
|
||||
from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
|
||||
from cinder.volume.drivers.netapp.dataontap import ssc_cmode
|
||||
|
||||
|
||||
|
|
|
@ -25,14 +25,13 @@ import mock
|
|||
|
||||
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.client.fakes \
|
||||
as client_fakes
|
||||
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
|
||||
|
||||
|
@ -44,9 +43,6 @@ class NetAppBlockStorage7modeLibraryTestCase(test.TestCase):
|
|||
def setUp(self):
|
||||
super(NetAppBlockStorage7modeLibraryTestCase, self).setUp()
|
||||
|
||||
# Inject fake netapp_lib module classes.
|
||||
netapp_api.mock_netapp_lib([block_7mode, client_base])
|
||||
|
||||
kwargs = {'configuration': self.get_config_7mode()}
|
||||
self.library = block_7mode.NetAppBlockStorage7modeLibrary(
|
||||
'driver', 'protocol', **kwargs)
|
||||
|
|
|
@ -29,11 +29,10 @@ from oslo_utils import units
|
|||
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
|
||||
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.client import api as netapp_api
|
||||
from cinder.volume.drivers.netapp import utils as na_utils
|
||||
from cinder.volume import utils as volume_utils
|
||||
|
||||
|
@ -43,9 +42,6 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase):
|
|||
def setUp(self):
|
||||
super(NetAppBlockStorageLibraryTestCase, self).setUp()
|
||||
|
||||
# Inject fake netapp_lib module classes.
|
||||
netapp_api.mock_netapp_lib([block_base])
|
||||
|
||||
kwargs = {'configuration': self.get_config_base()}
|
||||
self.library = block_base.NetAppBlockStorageLibrary(
|
||||
'driver', 'protocol', **kwargs)
|
||||
|
|
|
@ -23,12 +23,11 @@ from oslo_service import loopingcall
|
|||
|
||||
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
|
||||
|
@ -41,9 +40,6 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
|
|||
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)
|
||||
|
|
|
@ -24,11 +24,10 @@ from oslo_utils import units
|
|||
|
||||
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
|
||||
|
|
|
@ -26,13 +26,6 @@ from cinder.volume.drivers.netapp.eseries import utils
|
|||
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,
|
||||
|
@ -855,6 +848,8 @@ FAKE_ENDPOINT_HTTP = 'http://host:80/endpoint'
|
|||
|
||||
FAKE_ENDPOINT_HTTPS = 'https://host:8443/endpoint'
|
||||
|
||||
FAKE_INVOC_MSG = 'success'
|
||||
|
||||
FAKE_CLIENT_PARAMS = {
|
||||
'scheme': 'http',
|
||||
'host': '127.0.0.1',
|
||||
|
|
|
@ -41,13 +41,10 @@ class NetAppEseriesClientDriverTestCase(test.TestCase):
|
|||
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.my_client._endpoint = eseries_fake.FAKE_ENDPOINT_HTTP
|
||||
|
||||
fake_response = mock.Mock()
|
||||
fake_response.status_code = 200
|
||||
|
@ -67,8 +64,8 @@ class NetAppEseriesClientDriverTestCase(test.TestCase):
|
|||
fake_resp.status_code = status_code
|
||||
expected_msg = "Response error code - %s." % status_code
|
||||
|
||||
with self.assertRaisesRegexp(es_exception.WebServiceException,
|
||||
expected_msg) as exc:
|
||||
with self.assertRaisesRegex(es_exception.WebServiceException,
|
||||
expected_msg) as exc:
|
||||
self.my_client._eval_response(fake_resp)
|
||||
|
||||
self.assertEqual(status_code, exc.status_code)
|
||||
|
@ -81,8 +78,8 @@ class NetAppEseriesClientDriverTestCase(test.TestCase):
|
|||
fake_resp.text = resp_text
|
||||
expected_msg = "Response error - %s." % resp_text
|
||||
|
||||
with self.assertRaisesRegexp(es_exception.WebServiceException,
|
||||
expected_msg) as exc:
|
||||
with self.assertRaisesRegex(es_exception.WebServiceException,
|
||||
expected_msg) as exc:
|
||||
self.my_client._eval_response(fake_resp)
|
||||
|
||||
self.assertEqual(status_code, exc.status_code)
|
||||
|
@ -413,7 +410,7 @@ class NetAppEseriesClientDriverTestCase(test.TestCase):
|
|||
client.RestClient, '_get_resource_url',
|
||||
mock.Mock(return_value=eseries_fake.FAKE_RESOURCE_URL))
|
||||
self.mock_object(
|
||||
self.my_client.client, 'invoke_service',
|
||||
self.my_client, 'invoke_service',
|
||||
mock.Mock(return_value=fake_invoke_service))
|
||||
|
||||
eseries_info = client.RestClient.get_eseries_api_info(
|
||||
|
@ -732,3 +729,42 @@ class NetAppEseriesClientDriverTestCase(test.TestCase):
|
|||
client.RestClient._init_features(self.my_client)
|
||||
|
||||
self.assertTrue(self.my_client.features.SSC_API_V2.supported)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestWebserviceClientTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""sets up the mock tests"""
|
||||
super(TestWebserviceClientTestCase, self).setUp()
|
||||
self.mock_log = mock.Mock()
|
||||
self.mock_object(client, 'LOG', self.mock_log)
|
||||
self.webclient = client.WebserviceClient('http', 'host', '80',
|
||||
'/test', 'user', '****')
|
||||
|
||||
@ddt.data({'params': {'host': None, 'scheme': 'https', 'port': '80'}},
|
||||
{'params': {'host': 'host', 'scheme': None, 'port': '80'}},
|
||||
{'params': {'host': 'host', 'scheme': 'http', 'port': None}})
|
||||
@ddt.unpack
|
||||
def test__validate_params_value_error(self, params):
|
||||
"""Tests various scenarios for ValueError in validate method"""
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
self.webclient._validate_params, **params)
|
||||
|
||||
def test_invoke_service_no_endpoint_error(self):
|
||||
"""Tests Exception and Log error if no endpoint is provided"""
|
||||
self.webclient._endpoint = None
|
||||
log_error = 'Unexpected error while invoking web service'
|
||||
|
||||
self.assertRaises(exception.NetAppDriverException,
|
||||
self.webclient.invoke_service)
|
||||
self.assertTrue(self.mock_log.exception.find(log_error))
|
||||
|
||||
def test_invoke_service(self):
|
||||
"""Tests if invoke_service evaluates the right response"""
|
||||
self.webclient._endpoint = eseries_fake.FAKE_ENDPOINT_HTTP
|
||||
self.mock_object(self.webclient.conn, 'request',
|
||||
mock.Mock(return_value=eseries_fake.FAKE_INVOC_MSG))
|
||||
result = self.webclient.invoke_service()
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
|
|
|
@ -35,7 +35,6 @@ class NetAppDriverFactoryTestCase(test.TestCase):
|
|||
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'
|
||||
|
@ -47,7 +46,6 @@ class NetAppDriverFactoryTestCase(test.TestCase):
|
|||
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):
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ import platform
|
|||
|
||||
import mock
|
||||
from oslo_concurrency import processutils as putils
|
||||
from oslo_utils import importutils
|
||||
|
||||
from cinder import context
|
||||
from cinder import exception
|
||||
|
@ -64,21 +63,6 @@ class NetAppDriverUtilsTestCase(test.TestCase):
|
|||
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'))
|
||||
|
|
|
@ -73,7 +73,6 @@ class NetAppDriver(driver.ProxyVD):
|
|||
|
||||
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'),
|
||||
|
|
|
@ -29,22 +29,18 @@ import uuid
|
|||
from oslo_log import log as logging
|
||||
from oslo_log import versionutils
|
||||
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 netapp_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__)
|
||||
|
||||
|
|
|
@ -0,0 +1,613 @@
|
|||
# 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 _
|
||||
|
||||
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 not transport_type:
|
||||
raise ValueError('No transport type specified')
|
||||
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."""
|
||||
if not na_element or not isinstance(na_element, NaElement):
|
||||
raise 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:
|
||||
raise NaApiError('Unexpected error')
|
||||
|
||||
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
|
|
@ -19,18 +19,14 @@ import math
|
|||
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__)
|
||||
|
||||
|
|
|
@ -20,19 +20,15 @@ import sys
|
|||
|
||||
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
|
||||
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__)
|
||||
|
||||
|
|
|
@ -19,20 +19,15 @@ import copy
|
|||
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_'
|
||||
|
@ -528,8 +523,10 @@ class Client(client_base.Client):
|
|||
self.connection.invoke_successfully(na_el)
|
||||
except Exception as e:
|
||||
if isinstance(e, netapp_api.NaApiError):
|
||||
if(e.code == netapp_error.EAPINOTFOUND
|
||||
or e.code == netapp_error.EAPIPRIVILEGE):
|
||||
if (e.code == netapp_api.NaErrors
|
||||
['API_NOT_FOUND'].code or
|
||||
e.code == netapp_api.NaErrors
|
||||
['INSUFFICIENT_PRIVS'].code):
|
||||
failed_apis.append(api_name)
|
||||
elif major == 1 and minor >= 20:
|
||||
failed_apis = copy.copy(api_list)
|
||||
|
|
|
@ -24,19 +24,15 @@ import copy
|
|||
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__)
|
||||
|
||||
|
|
|
@ -26,26 +26,72 @@ import json
|
|||
import uuid
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
import requests
|
||||
import six
|
||||
from six.moves import urllib
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder.i18n import _LE
|
||||
import cinder.utils as cinder_utils
|
||||
from cinder.volume.drivers.netapp.eseries import exception as es_exception
|
||||
from cinder.volume.drivers.netapp.eseries import utils
|
||||
from cinder.volume.drivers.netapp import utils as na_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 RestClient(object):
|
||||
class WebserviceClient(object):
|
||||
"""Base client for NetApp Storage 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 was 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
|
||||
|
||||
|
||||
class RestClient(WebserviceClient):
|
||||
"""REST client specific to e-series storage service."""
|
||||
|
||||
ID = 'id'
|
||||
|
@ -66,11 +112,11 @@ class RestClient(object):
|
|||
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'
|
||||
|
||||
|
@ -149,9 +195,9 @@ class RestClient(object):
|
|||
raise exception.NotFound(_('Storage system id not set.'))
|
||||
kwargs['system-id'] = self._system_id
|
||||
path = path.format(**kwargs)
|
||||
if not self.client._endpoint.endswith('/'):
|
||||
self.client._endpoint = '%s/' % self.client._endpoint
|
||||
return urllib.parse.urljoin(self.client._endpoint, path.lstrip('/'))
|
||||
if not self._endpoint.endswith('/'):
|
||||
self._endpoint = '%s/' % self._endpoint
|
||||
return urllib.parse.urljoin(self._endpoint, path.lstrip('/'))
|
||||
|
||||
def _invoke(self, method, path, data=None, use_system=True,
|
||||
timeout=None, verify=False, **kwargs):
|
||||
|
@ -163,9 +209,9 @@ class RestClient(object):
|
|||
if cinder_utils.TRACE_API:
|
||||
self._log_http_request(method, url, headers, data)
|
||||
data = json.dumps(data) if data else None
|
||||
res = self.client.invoke_service(method, url, data=data,
|
||||
headers=headers,
|
||||
timeout=timeout, verify=verify)
|
||||
res = self.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:
|
||||
|
@ -664,9 +710,9 @@ class RestClient(object):
|
|||
'Accept': 'application/json'}
|
||||
url = self._get_resource_url(path, True).replace(
|
||||
'/devmgr/v2', '', 1)
|
||||
result = self.client.invoke_service(method='GET', url=url,
|
||||
headers=headers,
|
||||
verify=verify)
|
||||
result = self.invoke_service(method='GET', url=url,
|
||||
headers=headers,
|
||||
verify=verify)
|
||||
about_response_dict = result.json()
|
||||
mode_is_proxy = about_response_dict['runningAsProxy']
|
||||
if mode_is_proxy:
|
||||
|
|
|
@ -29,7 +29,6 @@ import socket
|
|||
|
||||
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
|
||||
|
@ -76,14 +75,6 @@ def check_flags(required_flags, configuration):
|
|||
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:
|
||||
|
|
Loading…
Reference in New Issue