Enable Manila multi-SVM driver on NetApp cDOT 8.3
cDOT 8.3 includes numerous new networking features, at least one of which (broadcast domains) must be used when creating network interfaces. This patch enables the multi-SVM driver on cDOT 8.3 by ensuring that each physical port selected to host share server logical interfaces (LIFs) is a member of a broadcast domain. A couple of other API tweaks are included that also impeded operation on cDOT 8.3. Closes-Bug: #1425754 Change-Id: Ibe92aee217462bce06485e195b930f2fdf43fcc0
This commit is contained in:
parent
a778199f01
commit
dab1e3bc14
|
@ -42,6 +42,7 @@ EVOLNOTCLONE = '13170'
|
|||
EVOL_NOT_MOUNTED = '14716'
|
||||
ESIS_CLONE_NOT_LICENSED = '14956'
|
||||
EOBJECTNOTFOUND = '15661'
|
||||
E_VIFMGR_PORT_ALREADY_ASSIGNED_TO_BROADCAST_DOMAIN = '18605'
|
||||
|
||||
|
||||
class NaServer(object):
|
||||
|
|
|
@ -47,6 +47,10 @@ class NetAppBaseClient(object):
|
|||
minor = res.get_child_content('minor-version')
|
||||
return major, minor
|
||||
|
||||
def _init_features(self):
|
||||
"""Set up the repository of available Data ONTAP features."""
|
||||
self.features = Features()
|
||||
|
||||
def check_is_naelement(self, elem):
|
||||
"""Checks if object is instance of NaElement."""
|
||||
if not isinstance(elem, netapp_api.NaElement):
|
||||
|
@ -74,3 +78,19 @@ class NetAppBaseClient(object):
|
|||
def send_ems_log_message(self, message_dict):
|
||||
"""Sends a message to the Data ONTAP EMS log."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class Features(object):
|
||||
|
||||
def __init__(self):
|
||||
self.defined_features = set()
|
||||
|
||||
def add_feature(self, name, supported=True):
|
||||
if not isinstance(supported, bool):
|
||||
raise TypeError("Feature value must be a bool type.")
|
||||
self.defined_features.add(name)
|
||||
setattr(self, name, supported)
|
||||
|
||||
def __getattr__(self, name):
|
||||
# NOTE(cknight): Needed to keep pylint happy.
|
||||
raise AttributeError
|
||||
|
|
|
@ -32,6 +32,8 @@ from manila.share.drivers.netapp import utils as na_utils
|
|||
|
||||
LOG = log.getLogger(__name__)
|
||||
DELETED_PREFIX = 'deleted_manila_'
|
||||
DEFAULT_IPSPACE = 'Default'
|
||||
DEFAULT_BROADCAST_DOMAIN = 'OpenStack'
|
||||
|
||||
|
||||
class NetAppCmodeClient(client_base.NetAppBaseClient):
|
||||
|
@ -46,6 +48,19 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||
(major, minor) = self.get_ontapi_version(cached=False)
|
||||
self.connection.set_api_version(major, minor)
|
||||
|
||||
self._init_features()
|
||||
|
||||
def _init_features(self):
|
||||
"""Initialize cDOT feature support map."""
|
||||
super(NetAppCmodeClient, self)._init_features()
|
||||
|
||||
ontapi_version = self.get_ontapi_version(cached=True)
|
||||
ontapi_1_30 = ontapi_version >= (1, 30)
|
||||
|
||||
self.features.add_feature('BROADCAST_DOMAINS', supported=ontapi_1_30)
|
||||
self.features.add_feature('IPSPACES', supported=ontapi_1_30)
|
||||
self.features.add_feature('SUBNETS', supported=ontapi_1_30)
|
||||
|
||||
def _invoke_vserver_api(self, na_element, vserver):
|
||||
server = copy.copy(self.connection)
|
||||
server.set_vserver(vserver)
|
||||
|
@ -265,7 +280,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||
},
|
||||
},
|
||||
'desired-attributes': {
|
||||
'node-details-info': {
|
||||
'net-port-info': {
|
||||
'port': None,
|
||||
'node': None,
|
||||
'operational-speed': None,
|
||||
|
@ -350,6 +365,9 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||
self._create_vlan(node, port, vlan)
|
||||
home_port_name = '%(port)s-%(tag)s' % {'port': port, 'tag': vlan}
|
||||
|
||||
if self.features.BROADCAST_DOMAINS:
|
||||
self._ensure_broadcast_domain_for_port(node, home_port_name)
|
||||
|
||||
interface_name = (lif_name_template %
|
||||
{'node': node, 'net_allocation_id': allocation_id})
|
||||
|
||||
|
@ -393,6 +411,104 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||
msg_args = {'vlan': vlan, 'port': port, 'err_msg': e.message}
|
||||
raise exception.NetAppException(msg % msg_args)
|
||||
|
||||
@na_utils.trace
|
||||
def _ensure_broadcast_domain_for_port(self, node, port,
|
||||
domain=DEFAULT_BROADCAST_DOMAIN,
|
||||
ipspace=DEFAULT_IPSPACE):
|
||||
"""Ensure a port is in a broadcast domain. Create one if necessary."""
|
||||
|
||||
if self._get_broadcast_domain_for_port(node, port):
|
||||
return
|
||||
|
||||
if not self._broadcast_domain_exists(domain, ipspace):
|
||||
self._create_broadcast_domain(domain, ipspace)
|
||||
|
||||
self._add_port_to_broadcast_domain(node, port, domain, ipspace)
|
||||
|
||||
@na_utils.trace
|
||||
def _get_broadcast_domain_for_port(self, node, port):
|
||||
"""Get broadcast domain for a specific port."""
|
||||
api_args = {
|
||||
'query': {
|
||||
'net-port-info': {
|
||||
'node': node,
|
||||
'port': port,
|
||||
},
|
||||
},
|
||||
'desired-attributes': {
|
||||
'net-port-info': {
|
||||
'broadcast-domain': None,
|
||||
},
|
||||
},
|
||||
}
|
||||
result = self.send_request('net-port-get-iter', api_args)
|
||||
|
||||
net_port_info_list = result.get_child_by_name(
|
||||
'attributes-list') or netapp_api.NaElement('none')
|
||||
port_info = net_port_info_list.get_children()
|
||||
if not port_info:
|
||||
msg = _('Could not find port %(port)s on node %(node)s.')
|
||||
msg_args = {'port': port, 'node': node}
|
||||
raise exception.NetAppException(msg % msg_args)
|
||||
|
||||
return port_info[0].get_child_content('broadcast-domain')
|
||||
|
||||
@na_utils.trace
|
||||
def _broadcast_domain_exists(self, domain, ipspace):
|
||||
"""Check if a broadcast domain exists."""
|
||||
api_args = {
|
||||
'query': {
|
||||
'net-port-broadcast-domain-info': {
|
||||
'ipspace': ipspace,
|
||||
'broadcast-domain': domain,
|
||||
},
|
||||
},
|
||||
'desired-attributes': {
|
||||
'net-port-broadcast-domain-info': None,
|
||||
},
|
||||
}
|
||||
result = self.send_request('net-port-broadcast-domain-get-iter',
|
||||
api_args)
|
||||
return self._has_records(result)
|
||||
|
||||
@na_utils.trace
|
||||
def _create_broadcast_domain(self, domain, ipspace, mtu=1500):
|
||||
"""Create a broadcast domain."""
|
||||
api_args = {
|
||||
'ipspace': ipspace,
|
||||
'broadcast-domain': domain,
|
||||
'mtu': mtu,
|
||||
}
|
||||
self.send_request('net-port-broadcast-domain-create', api_args)
|
||||
|
||||
@na_utils.trace
|
||||
def _add_port_to_broadcast_domain(self, node, port, domain, ipspace):
|
||||
|
||||
qualified_port_name = ':'.join([node, port])
|
||||
try:
|
||||
api_args = {
|
||||
'ipspace': ipspace,
|
||||
'broadcast-domain': domain,
|
||||
'ports': {
|
||||
'net-qualified-port-name': qualified_port_name,
|
||||
}
|
||||
}
|
||||
self.send_request('net-port-broadcast-domain-add-ports', api_args)
|
||||
except netapp_api.NaApiError as e:
|
||||
if e.code == (netapp_api.
|
||||
E_VIFMGR_PORT_ALREADY_ASSIGNED_TO_BROADCAST_DOMAIN):
|
||||
LOG.debug('Port %(port)s already exists in broadcast domain '
|
||||
'%(domain)s', {'port': port, 'domain': domain})
|
||||
else:
|
||||
msg = _('Failed to add port %(port)s to broadcast domain '
|
||||
'%(domain)s. %(err_msg)s')
|
||||
msg_args = {
|
||||
'port': qualified_port_name,
|
||||
'domain': domain,
|
||||
'err_msg': e.message,
|
||||
}
|
||||
raise exception.NetAppException(msg % msg_args)
|
||||
|
||||
@na_utils.trace
|
||||
def network_interface_exists(self, vserver_name, node, port, ip, netmask,
|
||||
vlan):
|
||||
|
@ -595,12 +711,14 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||
def setup_security_services(self, security_services, vserver_client,
|
||||
vserver_name):
|
||||
api_args = {
|
||||
'name-mapping-switch': {
|
||||
'nmswitch': 'ldap,file',
|
||||
},
|
||||
'name-server-switch': {
|
||||
'nsswitch': 'ldap,file',
|
||||
},
|
||||
'name-mapping-switch': [
|
||||
{'nmswitch': 'ldap'},
|
||||
{'nmswitch': 'file'}
|
||||
],
|
||||
'name-server-switch': [
|
||||
{'nsswitch': 'ldap'},
|
||||
{'nsswitch': 'file'}
|
||||
],
|
||||
'vserver-name': vserver_name,
|
||||
}
|
||||
self.send_request('vserver-modify', api_args)
|
||||
|
|
|
@ -42,8 +42,6 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
|
|||
@na_utils.trace
|
||||
def check_for_setup_error(self):
|
||||
|
||||
self._check_data_ontap_version()
|
||||
|
||||
if self._have_cluster_creds:
|
||||
if self.configuration.netapp_vserver:
|
||||
msg = _LW('Vserver is specified in the configuration. This is '
|
||||
|
@ -65,17 +63,6 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
|
|||
super(NetAppCmodeMultiSVMFileStorageLibrary, self).\
|
||||
check_for_setup_error()
|
||||
|
||||
def _check_data_ontap_version(self):
|
||||
# Temporary check to indicate that the Kilo multi-SVM driver does not
|
||||
# support cDOT 8.3 or higher.
|
||||
ontapi_version = self._client.get_ontapi_version()
|
||||
if ontapi_version >= (1, 30):
|
||||
msg = _('Clustered Data ONTAP 8.3.0 or higher is not '
|
||||
'supported by this version of the driver when the '
|
||||
'configuration option driver_handles_share_servers '
|
||||
'is set to True.')
|
||||
raise exception.NetAppException(msg)
|
||||
|
||||
@na_utils.trace
|
||||
def _get_vserver(self, share_server=None):
|
||||
|
||||
|
|
|
@ -60,6 +60,9 @@ NETMASK = '255.255.255.0'
|
|||
NET_ALLOCATION_ID = 'fake_allocation_id'
|
||||
LIF_NAME_TEMPLATE = 'os_%(net_allocation_id)s'
|
||||
LIF_NAME = LIF_NAME_TEMPLATE % {'net_allocation_id': NET_ALLOCATION_ID}
|
||||
IPSPACE = 'fake_ipspace'
|
||||
BROADCAST_DOMAIN = 'fake_domain'
|
||||
MTU = 9000
|
||||
|
||||
EMS_MESSAGE = {
|
||||
'computer-name': 'fake_host',
|
||||
|
@ -431,6 +434,43 @@ SORTED_PORTS_ALL_SPEEDS = (
|
|||
{'node': NODE_NAME, 'port': 'port7'},
|
||||
)
|
||||
|
||||
NET_PORT_GET_ITER_BROADCAST_DOMAIN_RESPONSE = etree.XML("""
|
||||
<results status="passed">
|
||||
<attributes-list>
|
||||
<net-port-info>
|
||||
<broadcast-domain>%(domain)s</broadcast-domain>
|
||||
<node>%(node)s</node>
|
||||
<port>%(port)s</port>
|
||||
</net-port-info>
|
||||
</attributes-list>
|
||||
<num-records>1</num-records>
|
||||
</results>
|
||||
""" % {'domain': BROADCAST_DOMAIN, 'node': NODE_NAME, 'port': PORT})
|
||||
|
||||
NET_PORT_GET_ITER_BROADCAST_DOMAIN_MISSING_RESPONSE = etree.XML("""
|
||||
<results status="passed">
|
||||
<attributes-list>
|
||||
<net-port-info>
|
||||
<node>%(node)s</node>
|
||||
<port>%(port)s</port>
|
||||
</net-port-info>
|
||||
</attributes-list>
|
||||
<num-records>1</num-records>
|
||||
</results>
|
||||
""" % {'node': NODE_NAME, 'port': PORT})
|
||||
|
||||
NET_PORT_BROADCAST_DOMAIN_GET_ITER_RESPONSE = etree.XML("""
|
||||
<results status="passed">
|
||||
<attributes-list>
|
||||
<net-port-broadcast-domain-info>
|
||||
<broadcast-domain>%(domain)s</broadcast-domain>
|
||||
<ipspace>%(ipspace)s</ipspace>
|
||||
</net-port-broadcast-domain-info>
|
||||
</attributes-list>
|
||||
<num-records>1</num-records>
|
||||
</results>
|
||||
""" % {'domain': BROADCAST_DOMAIN, 'ipspace': IPSPACE})
|
||||
|
||||
NET_INTERFACE_GET_ITER_RESPONSE = etree.XML("""
|
||||
<results status="passed">
|
||||
<attributes-list>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
from oslo_log import log
|
||||
|
||||
|
@ -56,6 +57,12 @@ class NetAppBaseClientTestCase(test.TestCase):
|
|||
self.assertEqual(1, major)
|
||||
self.assertEqual(20, minor)
|
||||
|
||||
def test_init_features(self):
|
||||
|
||||
self.client._init_features()
|
||||
|
||||
self.assertSetEqual(set(), self.client.features.defined_features)
|
||||
|
||||
def test_check_is_naelement(self):
|
||||
|
||||
element = netapp_api.NaElement('name')
|
||||
|
@ -121,4 +128,39 @@ class NetAppBaseClientTestCase(test.TestCase):
|
|||
|
||||
self.assertRaises(NotImplementedError,
|
||||
self.client.send_ems_log_message,
|
||||
{})
|
||||
{})
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class FeaturesTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(FeaturesTestCase, self).setUp()
|
||||
self.features = client_base.Features()
|
||||
|
||||
def test_init(self):
|
||||
self.assertSetEqual(set(), self.features.defined_features)
|
||||
|
||||
def test_add_feature_default(self):
|
||||
self.features.add_feature('FEATURE_1')
|
||||
|
||||
self.assertTrue(self.features.FEATURE_1)
|
||||
self.assertIn('FEATURE_1', self.features.defined_features)
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_add_feature(self, value):
|
||||
self.features.add_feature('FEATURE_2', value)
|
||||
|
||||
self.assertEqual(value, self.features.FEATURE_2)
|
||||
self.assertIn('FEATURE_2', self.features.defined_features)
|
||||
|
||||
@ddt.data('True', 'False', 0, 1, 1.0, None, [], {}, (True,))
|
||||
def test_add_feature_type_error(self, value):
|
||||
self.assertRaises(TypeError,
|
||||
self.features.add_feature,
|
||||
'FEATURE_3',
|
||||
value)
|
||||
self.assertNotIn('FEATURE_3', self.features.defined_features)
|
||||
|
||||
def test_get_attr_missing(self):
|
||||
self.assertRaises(AttributeError, getattr, self.features, 'FEATURE_4')
|
||||
|
|
|
@ -63,6 +63,31 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||
def _mock_api_error(self, code='fake'):
|
||||
return mock.Mock(side_effect=netapp_api.NaApiError(code=code))
|
||||
|
||||
def test_init_features_ontapi_1_21(self):
|
||||
|
||||
self.mock_object(client_base.NetAppBaseClient,
|
||||
'get_ontapi_version',
|
||||
mock.Mock(return_value=(1, 21)))
|
||||
|
||||
self.client._init_features()
|
||||
|
||||
self.assertFalse(self.client.features.BROADCAST_DOMAINS)
|
||||
self.assertFalse(self.client.features.IPSPACES)
|
||||
self.assertFalse(self.client.features.SUBNETS)
|
||||
|
||||
@ddt.data((1, 30), (1, 40), (2, 0))
|
||||
def test_init_features_ontapi_1_30(self, ontapi_version):
|
||||
|
||||
self.mock_object(client_base.NetAppBaseClient,
|
||||
'get_ontapi_version',
|
||||
mock.Mock(return_value=ontapi_version))
|
||||
|
||||
self.client._init_features()
|
||||
|
||||
self.assertTrue(self.client.features.BROADCAST_DOMAINS)
|
||||
self.assertTrue(self.client.features.IPSPACES)
|
||||
self.assertTrue(self.client.features.SUBNETS)
|
||||
|
||||
def test_invoke_vserver_api(self):
|
||||
|
||||
self.client._invoke_vserver_api('fake-api', 'fake_vserver')
|
||||
|
@ -487,7 +512,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||
},
|
||||
},
|
||||
'desired-attributes': {
|
||||
'node-details-info': {
|
||||
'net-port-info': {
|
||||
'port': None,
|
||||
'node': None,
|
||||
'operational-speed': None,
|
||||
|
@ -559,8 +584,14 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||
|
||||
self.assertListEqual([], result)
|
||||
|
||||
def test_create_network_interface(self):
|
||||
@ddt.data((True, True), (True, False), (False, True), (False, False))
|
||||
@ddt.unpack
|
||||
def test_create_network_interface(self, broadcast_domains_supported,
|
||||
use_vlans):
|
||||
|
||||
self.client.features.add_feature('BROADCAST_DOMAINS',
|
||||
broadcast_domains_supported)
|
||||
self.mock_object(self.client, '_ensure_broadcast_domain_for_port')
|
||||
self.mock_object(self.client, '_create_vlan')
|
||||
self.mock_object(self.client, 'send_request')
|
||||
|
||||
|
@ -572,50 +603,32 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||
{'data-protocol': 'cifs'}
|
||||
],
|
||||
'home-node': fake.NODE_NAME,
|
||||
'home-port': fake.VLAN_PORT,
|
||||
'home-port': fake.VLAN_PORT if use_vlans else fake.PORT,
|
||||
'netmask': fake.NETMASK,
|
||||
'interface-name': fake.LIF_NAME,
|
||||
'role': 'data',
|
||||
'vserver': fake.VSERVER_NAME,
|
||||
}
|
||||
self.client.create_network_interface(fake.IP_ADDRESS, fake.NETMASK,
|
||||
fake.VLAN, fake.NODE_NAME,
|
||||
fake.PORT, fake.VSERVER_NAME,
|
||||
fake.VLAN if use_vlans else None,
|
||||
fake.NODE_NAME, fake.PORT,
|
||||
fake.VSERVER_NAME,
|
||||
fake.NET_ALLOCATION_ID,
|
||||
fake.LIF_NAME_TEMPLATE)
|
||||
|
||||
self.client._create_vlan.assert_called_with(fake.NODE_NAME, fake.PORT,
|
||||
fake.VLAN)
|
||||
if use_vlans:
|
||||
self.client._create_vlan.assert_called_with(
|
||||
fake.NODE_NAME, fake.PORT, fake.VLAN)
|
||||
else:
|
||||
self.assertFalse(self.client._create_vlan.called)
|
||||
|
||||
self.client.send_request.assert_has_calls([
|
||||
mock.call('net-interface-create', lif_create_args)])
|
||||
if broadcast_domains_supported:
|
||||
self.client._ensure_broadcast_domain_for_port.assert_called_with(
|
||||
fake.NODE_NAME, fake.VLAN_PORT if use_vlans else fake.PORT)
|
||||
else:
|
||||
self.assertFalse(
|
||||
self.client._ensure_broadcast_domain_for_port.called)
|
||||
|
||||
def test_create_network_interface_no_vlan(self):
|
||||
|
||||
self.mock_object(self.client, '_create_vlan')
|
||||
self.mock_object(self.client, 'send_request')
|
||||
|
||||
lif_create_args = {
|
||||
'address': fake.IP_ADDRESS,
|
||||
'administrative-status': 'up',
|
||||
'data-protocols': [
|
||||
{'data-protocol': 'nfs'},
|
||||
{'data-protocol': 'cifs'}
|
||||
],
|
||||
'home-node': fake.NODE_NAME,
|
||||
'home-port': fake.PORT,
|
||||
'netmask': fake.NETMASK,
|
||||
'interface-name': fake.LIF_NAME,
|
||||
'role': 'data',
|
||||
'vserver': fake.VSERVER_NAME,
|
||||
}
|
||||
self.client.create_network_interface(fake.IP_ADDRESS, fake.NETMASK,
|
||||
None, fake.NODE_NAME,
|
||||
fake.PORT, fake.VSERVER_NAME,
|
||||
fake.NET_ALLOCATION_ID,
|
||||
fake.LIF_NAME_TEMPLATE)
|
||||
|
||||
self.assertFalse(self.client._create_vlan.called)
|
||||
self.client.send_request.assert_has_calls([
|
||||
mock.call('net-interface-create', lif_create_args)])
|
||||
|
||||
|
@ -665,6 +678,229 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||
fake.PORT,
|
||||
fake.VLAN)
|
||||
|
||||
def test_ensure_broadcast_domain_for_port_has_domain(self):
|
||||
|
||||
self.mock_object(self.client,
|
||||
'_get_broadcast_domain_for_port',
|
||||
mock.Mock(return_value=fake.BROADCAST_DOMAIN))
|
||||
self.mock_object(self.client, '_broadcast_domain_exists')
|
||||
self.mock_object(self.client, '_create_broadcast_domain')
|
||||
self.mock_object(self.client, '_add_port_to_broadcast_domain')
|
||||
|
||||
self.client._ensure_broadcast_domain_for_port(fake.NODE_NAME,
|
||||
fake.PORT)
|
||||
|
||||
self.client._get_broadcast_domain_for_port.assert_has_calls([
|
||||
mock.call(fake.NODE_NAME, fake.PORT)])
|
||||
self.assertFalse(self.client._broadcast_domain_exists.called)
|
||||
self.assertFalse(self.client._create_broadcast_domain.called)
|
||||
self.assertFalse(self.client._add_port_to_broadcast_domain.called)
|
||||
|
||||
def test_ensure_broadcast_domain_for_port_domain_not_found(self):
|
||||
|
||||
self.mock_object(self.client,
|
||||
'_get_broadcast_domain_for_port',
|
||||
mock.Mock(return_value=None))
|
||||
self.mock_object(self.client,
|
||||
'_broadcast_domain_exists',
|
||||
mock.Mock(return_value=False))
|
||||
self.mock_object(self.client, '_create_broadcast_domain')
|
||||
self.mock_object(self.client, '_add_port_to_broadcast_domain')
|
||||
|
||||
self.client._ensure_broadcast_domain_for_port(
|
||||
fake.NODE_NAME, fake.PORT, domain=fake.BROADCAST_DOMAIN,
|
||||
ipspace=fake.IPSPACE)
|
||||
|
||||
self.client._get_broadcast_domain_for_port.assert_has_calls([
|
||||
mock.call(fake.NODE_NAME, fake.PORT)])
|
||||
self.client._broadcast_domain_exists.assert_has_calls([
|
||||
mock.call(fake.BROADCAST_DOMAIN, fake.IPSPACE)])
|
||||
self.client._create_broadcast_domain.assert_has_calls([
|
||||
mock.call(fake.BROADCAST_DOMAIN, fake.IPSPACE)])
|
||||
self.client._add_port_to_broadcast_domain.assert_has_calls([
|
||||
mock.call(fake.NODE_NAME, fake.PORT, fake.BROADCAST_DOMAIN,
|
||||
fake.IPSPACE)])
|
||||
|
||||
def test_ensure_broadcast_domain_for_port_domain_found(self):
|
||||
|
||||
self.mock_object(self.client,
|
||||
'_get_broadcast_domain_for_port',
|
||||
mock.Mock(return_value=None))
|
||||
self.mock_object(self.client,
|
||||
'_broadcast_domain_exists',
|
||||
mock.Mock(return_value=True))
|
||||
self.mock_object(self.client, '_create_broadcast_domain')
|
||||
self.mock_object(self.client, '_add_port_to_broadcast_domain')
|
||||
|
||||
self.client._ensure_broadcast_domain_for_port(
|
||||
fake.NODE_NAME, fake.PORT, domain=fake.BROADCAST_DOMAIN,
|
||||
ipspace=fake.IPSPACE)
|
||||
|
||||
self.client._get_broadcast_domain_for_port.assert_has_calls([
|
||||
mock.call(fake.NODE_NAME, fake.PORT)])
|
||||
self.client._broadcast_domain_exists.assert_has_calls([
|
||||
mock.call(fake.BROADCAST_DOMAIN, fake.IPSPACE)])
|
||||
self.assertFalse(self.client._create_broadcast_domain.called)
|
||||
self.client._add_port_to_broadcast_domain.assert_has_calls([
|
||||
mock.call(fake.NODE_NAME, fake.PORT, fake.BROADCAST_DOMAIN,
|
||||
fake.IPSPACE)])
|
||||
|
||||
def test_get_broadcast_domain_for_port(self):
|
||||
|
||||
api_response = netapp_api.NaElement(
|
||||
fake.NET_PORT_GET_ITER_BROADCAST_DOMAIN_RESPONSE)
|
||||
self.mock_object(self.client,
|
||||
'send_request',
|
||||
mock.Mock(return_value=api_response))
|
||||
|
||||
net_port_get_iter_args = {
|
||||
'query': {
|
||||
'net-port-info': {
|
||||
'node': fake.NODE_NAME,
|
||||
'port': fake.PORT,
|
||||
},
|
||||
},
|
||||
'desired-attributes': {
|
||||
'net-port-info': {
|
||||
'broadcast-domain': None,
|
||||
},
|
||||
},
|
||||
}
|
||||
result = self.client._get_broadcast_domain_for_port(fake.NODE_NAME,
|
||||
fake.PORT)
|
||||
|
||||
self.client.send_request.assert_has_calls([
|
||||
mock.call('net-port-get-iter', net_port_get_iter_args)])
|
||||
self.assertEqual(fake.BROADCAST_DOMAIN, result)
|
||||
|
||||
def test_get_broadcast_domain_for_port_port_not_found(self):
|
||||
|
||||
api_response = netapp_api.NaElement(
|
||||
fake.NO_RECORDS_RESPONSE)
|
||||
self.mock_object(self.client,
|
||||
'send_request',
|
||||
mock.Mock(return_value=api_response))
|
||||
|
||||
self.assertRaises(exception.NetAppException,
|
||||
self.client._get_broadcast_domain_for_port,
|
||||
fake.NODE_NAME,
|
||||
fake.PORT)
|
||||
|
||||
def test_get_broadcast_domain_for_port_domain_not_found(self):
|
||||
|
||||
api_response = netapp_api.NaElement(
|
||||
fake.NET_PORT_GET_ITER_BROADCAST_DOMAIN_MISSING_RESPONSE)
|
||||
self.mock_object(self.client,
|
||||
'send_request',
|
||||
mock.Mock(return_value=api_response))
|
||||
|
||||
result = self.client._get_broadcast_domain_for_port(fake.NODE_NAME,
|
||||
fake.PORT)
|
||||
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_broadcast_domain_exists(self):
|
||||
|
||||
api_response = netapp_api.NaElement(
|
||||
fake.NET_PORT_BROADCAST_DOMAIN_GET_ITER_RESPONSE)
|
||||
self.mock_object(self.client,
|
||||
'send_request',
|
||||
mock.Mock(return_value=api_response))
|
||||
|
||||
result = self.client._broadcast_domain_exists(fake.BROADCAST_DOMAIN,
|
||||
fake.IPSPACE)
|
||||
|
||||
net_port_broadcast_domain_get_iter_args = {
|
||||
'query': {
|
||||
'net-port-broadcast-domain-info': {
|
||||
'ipspace': fake.IPSPACE,
|
||||
'broadcast-domain': fake.BROADCAST_DOMAIN,
|
||||
},
|
||||
},
|
||||
'desired-attributes': {
|
||||
'net-port-broadcast-domain-info': None,
|
||||
},
|
||||
}
|
||||
self.client.send_request.assert_has_calls([
|
||||
mock.call('net-port-broadcast-domain-get-iter',
|
||||
net_port_broadcast_domain_get_iter_args)])
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_broadcast_domain_exists_not_found(self):
|
||||
|
||||
api_response = netapp_api.NaElement(
|
||||
fake.NO_RECORDS_RESPONSE)
|
||||
self.mock_object(self.client,
|
||||
'send_request',
|
||||
mock.Mock(return_value=api_response))
|
||||
|
||||
result = self.client._broadcast_domain_exists(fake.BROADCAST_DOMAIN,
|
||||
fake.IPSPACE)
|
||||
|
||||
self.assertFalse(result)
|
||||
|
||||
def test_create_broadcast_domain(self):
|
||||
|
||||
self.mock_object(self.client, 'send_request')
|
||||
|
||||
result = self.client._create_broadcast_domain(fake.BROADCAST_DOMAIN,
|
||||
fake.IPSPACE,
|
||||
mtu=fake.MTU)
|
||||
|
||||
net_port_broadcast_domain_create_args = {
|
||||
'ipspace': fake.IPSPACE,
|
||||
'broadcast-domain': fake.BROADCAST_DOMAIN,
|
||||
'mtu': fake.MTU,
|
||||
}
|
||||
self.assertIsNone(result)
|
||||
self.client.send_request.assert_has_calls([
|
||||
mock.call('net-port-broadcast-domain-create',
|
||||
net_port_broadcast_domain_create_args)])
|
||||
|
||||
def test_add_port_to_broadcast_domain(self):
|
||||
|
||||
self.mock_object(self.client, 'send_request')
|
||||
|
||||
add_port_to_broadcast_domain_args = {
|
||||
'ipspace': fake.IPSPACE,
|
||||
'broadcast-domain': fake.BROADCAST_DOMAIN,
|
||||
'ports': {
|
||||
'net-qualified-port-name': ':'.join([fake.NODE_NAME,
|
||||
fake.VLAN_PORT])
|
||||
}
|
||||
}
|
||||
result = self.client._add_port_to_broadcast_domain(
|
||||
fake.NODE_NAME, fake.VLAN_PORT, fake.BROADCAST_DOMAIN,
|
||||
fake.IPSPACE)
|
||||
|
||||
self.assertIsNone(result)
|
||||
self.client.send_request.assert_has_calls([
|
||||
mock.call('net-port-broadcast-domain-add-ports',
|
||||
add_port_to_broadcast_domain_args)])
|
||||
|
||||
def test_add_port_to_broadcast_domain_already_present(self):
|
||||
|
||||
self.mock_object(self.client, 'send_request', self._mock_api_error(
|
||||
code=netapp_api.
|
||||
E_VIFMGR_PORT_ALREADY_ASSIGNED_TO_BROADCAST_DOMAIN))
|
||||
|
||||
result = self.client._add_port_to_broadcast_domain(
|
||||
fake.NODE_NAME, fake.VLAN_PORT, fake.BROADCAST_DOMAIN,
|
||||
fake.IPSPACE)
|
||||
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_add_port_to_broadcast_domain_api_error(self):
|
||||
|
||||
self.mock_object(self.client, 'send_request', self._mock_api_error())
|
||||
|
||||
self.assertRaises(exception.NetAppException,
|
||||
self.client._add_port_to_broadcast_domain,
|
||||
fake.NODE_NAME,
|
||||
fake.VLAN_PORT,
|
||||
fake.BROADCAST_DOMAIN,
|
||||
fake.IPSPACE)
|
||||
|
||||
def test_network_interface_exists(self):
|
||||
|
||||
api_response = netapp_api.NaElement(
|
||||
|
@ -1029,11 +1265,16 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||
fake.VSERVER_NAME)
|
||||
|
||||
vserver_modify_args = {
|
||||
'name-mapping-switch': {'nmswitch': 'ldap,file'},
|
||||
'name-server-switch': {'nsswitch': 'ldap,file'},
|
||||
'name-mapping-switch': [
|
||||
{'nmswitch': 'ldap'},
|
||||
{'nmswitch': 'file'},
|
||||
],
|
||||
'name-server-switch': [
|
||||
{'nsswitch': 'ldap'},
|
||||
{'nsswitch': 'file'},
|
||||
],
|
||||
'vserver-name': fake.VSERVER_NAME
|
||||
}
|
||||
|
||||
self.client.send_request.assert_has_calls([
|
||||
mock.call('vserver-modify', vserver_modify_args)])
|
||||
self.vserver_client.configure_ldap.assert_has_calls([
|
||||
|
@ -1049,11 +1290,16 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||
fake.VSERVER_NAME)
|
||||
|
||||
vserver_modify_args = {
|
||||
'name-mapping-switch': {'nmswitch': 'ldap,file'},
|
||||
'name-server-switch': {'nsswitch': 'ldap,file'},
|
||||
'name-mapping-switch': [
|
||||
{'nmswitch': 'ldap'},
|
||||
{'nmswitch': 'file'},
|
||||
],
|
||||
'name-server-switch': [
|
||||
{'nsswitch': 'ldap'},
|
||||
{'nsswitch': 'file'},
|
||||
],
|
||||
'vserver-name': fake.VSERVER_NAME
|
||||
}
|
||||
|
||||
self.client.send_request.assert_has_calls([
|
||||
mock.call('vserver-modify', vserver_modify_args)])
|
||||
self.vserver_client.configure_active_directory.assert_has_calls([
|
||||
|
@ -1070,11 +1316,16 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||
fake.VSERVER_NAME)
|
||||
|
||||
vserver_modify_args = {
|
||||
'name-mapping-switch': {'nmswitch': 'ldap,file'},
|
||||
'name-server-switch': {'nsswitch': 'ldap,file'},
|
||||
'name-mapping-switch': [
|
||||
{'nmswitch': 'ldap'},
|
||||
{'nmswitch': 'file'},
|
||||
],
|
||||
'name-server-switch': [
|
||||
{'nsswitch': 'ldap'},
|
||||
{'nsswitch': 'file'},
|
||||
],
|
||||
'vserver-name': fake.VSERVER_NAME
|
||||
}
|
||||
|
||||
self.client.send_request.assert_has_calls([
|
||||
mock.call('vserver-modify', vserver_modify_args)])
|
||||
self.client.create_kerberos_realm.assert_has_calls([
|
||||
|
@ -1093,11 +1344,16 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||
fake.VSERVER_NAME)
|
||||
|
||||
vserver_modify_args = {
|
||||
'name-mapping-switch': {'nmswitch': 'ldap,file'},
|
||||
'name-server-switch': {'nsswitch': 'ldap,file'},
|
||||
'name-mapping-switch': [
|
||||
{'nmswitch': 'ldap'},
|
||||
{'nmswitch': 'file'},
|
||||
],
|
||||
'name-server-switch': [
|
||||
{'nsswitch': 'ldap'},
|
||||
{'nsswitch': 'file'},
|
||||
],
|
||||
'vserver-name': fake.VSERVER_NAME
|
||||
}
|
||||
|
||||
self.client.send_request.assert_has_calls([
|
||||
mock.call('vserver-modify', vserver_modify_args)])
|
||||
|
||||
|
|
|
@ -65,14 +65,11 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||
self.mock_object(self.library,
|
||||
'_find_matching_aggregates',
|
||||
mock.Mock(return_value=fake.AGGREGATES))
|
||||
mock_check_data_ontap_version = self.mock_object(
|
||||
self.library, '_check_data_ontap_version')
|
||||
mock_super = self.mock_object(lib_base.NetAppCmodeFileStorageLibrary,
|
||||
'check_for_setup_error')
|
||||
|
||||
self.library.check_for_setup_error()
|
||||
|
||||
self.assertTrue(mock_check_data_ontap_version.called)
|
||||
self.assertTrue(self.library._find_matching_aggregates.called)
|
||||
mock_super.assert_called_once_with()
|
||||
|
||||
|
@ -107,17 +104,6 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||
self.library.check_for_setup_error)
|
||||
self.assertTrue(self.library._find_matching_aggregates.called)
|
||||
|
||||
@ddt.data((1, 20), (1, 21))
|
||||
def test_check_data_ontap_version(self, version):
|
||||
self.library._client.get_ontapi_version.return_value = version
|
||||
self.assertIsNone(self.library._check_data_ontap_version())
|
||||
|
||||
@ddt.data((1, 30), (1, 31), (1, 40), (2, 0))
|
||||
def test_check_data_ontap_version_too_new(self, version):
|
||||
self.library._client.get_ontapi_version.return_value = version
|
||||
self.assertRaises(exception.NetAppException,
|
||||
self.library._check_data_ontap_version)
|
||||
|
||||
def test_get_vserver_no_share_server(self):
|
||||
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
|
|
Loading…
Reference in New Issue