NetApp cDOT multi-SVM driver can't handle duplicate addresses

When operating in DHSS==True mode, the cDOT driver can receive
separate Neutron subnets for different tenants with overlapping
IP address ranges.  This is typically seen in Tempest testing,
and it is likely to occur for production clouds as well.  VLAN
segmentation is insufficient by itself to resolve the problem.
Starting with 8.3, clustered Data ONTAP supports IPspaces,
which allow different storage virtual machines (i.e. share
servers) to utilize overlapping address spaces.  This commit
resolves the issue on cDOT 8.3 by creating a new IPspace for
each unique tenant network segment (i.e., each Neutron subnet)
it encounters while creating share servers. It also includes
some minor improvements to error handling when vservers aren't
created and configured successfully plus some trivial refactoring
in the vserver creation path.

Closes-Bug: #1491384
Change-Id: I808547c7d38e6bb61ae10ecdb174d3ab52b1443a
This commit is contained in:
Clinton Knight 2015-09-13 12:22:08 -04:00
parent 7049a9aac9
commit 6369e3f9c8
6 changed files with 946 additions and 126 deletions

View File

@ -81,7 +81,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
@na_utils.trace
def create_vserver(self, vserver_name, root_volume_aggregate_name,
root_volume_name, aggregate_names):
root_volume_name, aggregate_names, ipspace_name):
"""Creates new vserver and assigns aggregates."""
create_args = {
'vserver-name': vserver_name,
@ -92,6 +92,14 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
'nsswitch': 'file',
},
}
if ipspace_name:
if not self.features.IPSPACES:
msg = 'IPSpaces are not supported on this backend.'
raise exception.NetAppException(msg)
else:
create_args['ipspace'] = ipspace_name
self.send_request('vserver-create', create_args)
aggr_list = [{'aggr-name': aggr_name} for aggr_name in aggregate_names]
@ -148,6 +156,57 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
raise exception.NetAppException(msg)
return root_volume_name
@na_utils.trace
def get_vserver_ipspace(self, vserver_name):
"""Get the IPspace of the vserver, or None if not supported."""
if not self.features.IPSPACES:
return None
api_args = {
'query': {
'vserver-info': {
'vserver-name': vserver_name,
},
},
'desired-attributes': {
'vserver-info': {
'ipspace': None,
},
},
}
vserver_info = self.send_request('vserver-get-iter', api_args)
try:
ipspace = vserver_info.get_child_by_name(
'attributes-list').get_child_by_name(
'vserver-info').get_child_content('ipspace')
except AttributeError:
msg = _('Could not determine IPspace for Vserver %s.')
raise exception.NetAppException(msg % vserver_name)
return ipspace
@na_utils.trace
def ipspace_has_data_vservers(self, ipspace_name):
"""Check whether an IPspace has any data Vservers assigned to it."""
if not self.features.IPSPACES:
return False
api_args = {
'query': {
'vserver-info': {
'ipspace': ipspace_name,
'vserver-type': 'data'
},
},
'desired-attributes': {
'vserver-info': {
'vserver-name': None,
},
},
}
result = self.send_request('vserver-get-iter', api_args)
return self._has_records(result)
@na_utils.trace
def list_vservers(self, vserver_type='data'):
"""Get the names of vservers present, optionally filtered by type."""
@ -358,7 +417,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
@na_utils.trace
def create_network_interface(self, ip, netmask, vlan, node, port,
vserver_name, allocation_id,
lif_name_template):
lif_name_template, ipspace_name):
"""Creates LIF on VLAN port."""
home_port_name = port
@ -367,7 +426,8 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
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)
self._ensure_broadcast_domain_for_port(node, home_port_name,
ipspace=ipspace_name)
interface_name = (lif_name_template %
{'node': node, 'net_allocation_id': allocation_id})
@ -416,14 +476,34 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
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."""
"""Ensure a port is in a broadcast domain. Create one if necessary.
if self._get_broadcast_domain_for_port(node, port):
If the IPspace:domain pair match for the given port, which commonly
happens in multi-node clusters, then there isn't anything to do.
Otherwise, we can assume the IPspace is correct and extant by this
point, so the remaining task is to remove the port from any domain it
is already in, create the desired domain if it doesn't exist, and add
the port to the desired domain.
"""
port_info = self._get_broadcast_domain_for_port(node, port)
# Port already in desired ipspace and broadcast domain.
if (port_info['ipspace'] == ipspace
and port_info['broadcast-domain'] == domain):
return
# If in another broadcast domain, remove port from it.
if port_info['broadcast-domain']:
self._remove_port_from_broadcast_domain(
node, port, port_info['broadcast-domain'],
port_info['ipspace'])
# If desired broadcast domain doesn't exist, create it.
if not self._broadcast_domain_exists(domain, ipspace):
self._create_broadcast_domain(domain, ipspace)
# Move the port into the broadcast domain where it is needed.
self._add_port_to_broadcast_domain(node, port, domain, ipspace)
@na_utils.trace
@ -439,6 +519,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
'desired-attributes': {
'net-port-info': {
'broadcast-domain': None,
'ipspace': None,
},
},
}
@ -452,7 +533,12 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
msg_args = {'port': port, 'node': node}
raise exception.NetAppException(msg % msg_args)
return port_info[0].get_child_content('broadcast-domain')
port = {
'broadcast-domain':
port_info[0].get_child_content('broadcast-domain'),
'ipspace': port_info[0].get_child_content('ipspace')
}
return port
@na_utils.trace
def _broadcast_domain_exists(self, domain, ipspace):
@ -482,6 +568,25 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
}
self.send_request('net-port-broadcast-domain-create', api_args)
@na_utils.trace
def _delete_broadcast_domain(self, domain, ipspace):
"""Delete a broadcast domain."""
api_args = {
'ipspace': ipspace,
'broadcast-domain': domain,
}
self.send_request('net-port-broadcast-domain-destroy', api_args)
def _delete_broadcast_domains_for_ipspace(self, ipspace_name):
"""Deletes all broadcast domains in an IPspace."""
ipspaces = self.get_ipspaces(ipspace_name=ipspace_name)
if not ipspaces:
return
ipspace = ipspaces[0]
for broadcast_domain_name in ipspace['broadcast-domains']:
self._delete_broadcast_domain(broadcast_domain_name, ipspace_name)
@na_utils.trace
def _add_port_to_broadcast_domain(self, node, port, domain, ipspace):
@ -510,6 +615,19 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
}
raise exception.NetAppException(msg % msg_args)
@na_utils.trace
def _remove_port_from_broadcast_domain(self, node, port, domain, ipspace):
qualified_port_name = ':'.join([node, port])
api_args = {
'ipspace': ipspace,
'broadcast-domain': domain,
'ports': {
'net-qualified-port-name': qualified_port_name,
}
}
self.send_request('net-port-broadcast-domain-remove-ports', api_args)
@na_utils.trace
def network_interface_exists(self, vserver_name, node, port, ip, netmask,
vlan):
@ -594,6 +712,103 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
api_args = {'vserver': None, 'interface-name': interface_name}
self.send_request('net-interface-delete', api_args)
@na_utils.trace
def get_ipspaces(self, ipspace_name=None, max_records=1000):
"""Gets one or more IPSpaces."""
if not self.features.IPSPACES:
return []
api_args = {'max-records': max_records}
if ipspace_name:
api_args['query'] = {
'net-ipspaces-info': {
'ipspace': ipspace_name,
}
}
result = self.send_request('net-ipspaces-get-iter', api_args)
if not self._has_records(result):
return []
ipspaces = []
for net_ipspaces_info in result.get_child_by_name(
'attributes-list').get_children():
ipspace = {
'ports': [],
'vservers': [],
'broadcast-domains': [],
}
ports = net_ipspaces_info.get_child_by_name(
'ports') or netapp_api.NaElement('none')
for port in ports.get_children():
ipspace['ports'].append(port.get_content())
vservers = net_ipspaces_info.get_child_by_name(
'vservers') or netapp_api.NaElement('none')
for vserver in vservers.get_children():
ipspace['vservers'].append(vserver.get_content())
broadcast_domains = net_ipspaces_info.get_child_by_name(
'broadcast-domains') or netapp_api.NaElement('none')
for broadcast_domain in broadcast_domains.get_children():
ipspace['broadcast-domains'].append(
broadcast_domain.get_content())
ipspace['ipspace'] = net_ipspaces_info.get_child_content('ipspace')
ipspace['id'] = net_ipspaces_info.get_child_content('id')
ipspace['uuid'] = net_ipspaces_info.get_child_content('uuid')
ipspaces.append(ipspace)
return ipspaces
@na_utils.trace
def ipspace_exists(self, ipspace_name):
"""Checks if IPspace exists."""
if not self.features.IPSPACES:
return False
api_args = {
'query': {
'net-ipspaces-info': {
'ipspace': ipspace_name,
},
},
'desired-attributes': {
'net-ipspaces-info': {
'ipspace': None,
},
},
}
result = self.send_request('net-ipspaces-get-iter', api_args)
return self._has_records(result)
@na_utils.trace
def create_ipspace(self, ipspace_name):
"""Creates an IPspace."""
api_args = {'ipspace': ipspace_name}
self.send_request('net-ipspaces-create', api_args)
@na_utils.trace
def delete_ipspace(self, ipspace_name):
"""Deletes an IPspace."""
self._delete_broadcast_domains_for_ipspace(ipspace_name)
api_args = {'ipspace': ipspace_name}
self.send_request('net-ipspaces-destroy', api_args)
@na_utils.trace
def add_vserver_to_ipspace(self, ipspace_name, vserver_name):
"""Assigns a vserver to an IPspace."""
api_args = {'ipspace': ipspace_name, 'vserver': vserver_name}
self.send_request('net-ipspaces-assign-vserver', api_args)
@na_utils.trace
def get_node_for_aggregate(self, aggregate_name):
"""Get home node for the specified aggregate.

View File

@ -27,6 +27,7 @@ from oslo_utils import excutils
from manila import exception
from manila.i18n import _, _LE, _LW
from manila.share.drivers.netapp.dataontap.client import client_cmode
from manila.share.drivers.netapp.dataontap.cluster_mode import lib_base
from manila.share.drivers.netapp import utils as na_utils
from manila import utils
@ -34,6 +35,7 @@ from manila import utils
LOG = log.getLogger(__name__)
SUPPORTED_NETWORK_TYPES = (None, 'flat', 'vlan')
SEGMENTED_NETWORK_TYPES = ('vlan',)
class NetAppCmodeMultiSVMFileStorageLibrary(
@ -111,7 +113,7 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
server_details = {'vserver_name': vserver_name}
try:
self._create_vserver_if_nonexistent(vserver_name, network_info)
self._create_vserver(vserver_name, network_info)
except Exception as e:
e.detail_data = {'server_details': server_details}
raise
@ -132,41 +134,74 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
return self.configuration.netapp_vserver_name_template % server_id
@na_utils.trace
def _create_vserver_if_nonexistent(self, vserver_name, network_info):
def _create_vserver(self, vserver_name, network_info):
"""Creates Vserver with given parameters if it doesn't exist."""
if self._client.vserver_exists(vserver_name):
msg = _('Vserver %s already exists.')
raise exception.NetAppException(msg % vserver_name)
ipspace_name = self._create_ipspace(network_info)
LOG.debug('Vserver %s does not exist, creating.', vserver_name)
self._client.create_vserver(
vserver_name,
self.configuration.netapp_root_volume_aggregate,
self.configuration.netapp_root_volume,
self._find_matching_aggregates())
self._find_matching_aggregates(),
ipspace_name)
vserver_client = self._get_api_client(vserver=vserver_name)
security_services = None
try:
self._create_vserver_lifs(vserver_name,
vserver_client,
network_info)
network_info,
ipspace_name)
vserver_client.enable_nfs()
security_services = network_info.get('security_services')
if security_services:
self._client.setup_security_services(security_services,
vserver_client,
vserver_name)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error(_LE("Failed to create network interface(s)."))
self._client.delete_vserver(vserver_name, vserver_client)
LOG.error(_LE("Failed to configure Vserver."))
self._delete_vserver(vserver_name,
security_services=security_services)
vserver_client.enable_nfs()
def _get_valid_ipspace_name(self, network_id):
"""Get IPspace name according to network id."""
return 'ipspace_' + network_id.replace('-', '_')
security_services = network_info.get('security_services')
if security_services:
self._client.setup_security_services(security_services,
vserver_client,
vserver_name)
@na_utils.trace
def _create_ipspace(self, network_info):
"""If supported, create an IPspace for a new Vserver."""
if not self._client.features.IPSPACES:
return None
if network_info['network_type'] not in SEGMENTED_NETWORK_TYPES:
return client_cmode.DEFAULT_IPSPACE
# NOTE(cknight): Neutron needs cDOT IP spaces because it can provide
# overlapping IP address ranges for different subnets. That is not
# believed to be an issue for any of Manila's other network plugins.
ipspace_id = network_info.get('neutron_subnet_id')
if not ipspace_id:
return client_cmode.DEFAULT_IPSPACE
ipspace_name = self._get_valid_ipspace_name(ipspace_id)
if not self._client.ipspace_exists(ipspace_name):
self._client.create_ipspace(ipspace_name)
return ipspace_name
@na_utils.trace
def _create_vserver_lifs(self, vserver_name, vserver_client,
network_info):
network_info, ipspace_name):
nodes = self._client.list_cluster_nodes()
node_network_info = zip(nodes, network_info['network_allocations'])
@ -183,6 +218,7 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
port,
ip,
netmask,
ipspace_name,
vserver_client)
@na_utils.trace
@ -199,14 +235,15 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
@na_utils.trace
def _create_lif_if_nonexistent(self, vserver_name, allocation_id, vlan,
node, port, ip, netmask, vserver_client):
node, port, ip, netmask, ipspace_name,
vserver_client):
"""Creates LIF for Vserver."""
if not vserver_client.network_interface_exists(vserver_name, node,
port, ip, netmask,
vlan):
self._client.create_network_interface(
ip, netmask, vlan, node, port, vserver_name, allocation_id,
self.configuration.netapp_lif_name_template)
self.configuration.netapp_lif_name_template, ipspace_name)
@na_utils.trace
def get_network_allocations_number(self):
@ -231,7 +268,19 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
"record will proceed anyway."), vserver)
return
self._delete_vserver(vserver, security_services=security_services)
@na_utils.trace
def _delete_vserver(self, vserver, security_services=None):
"""Delete a Vserver plus IPspace and security services as needed."""
ipspace_name = self._client.get_vserver_ipspace(vserver)
vserver_client = self._get_api_client(vserver=vserver)
self._client.delete_vserver(vserver,
vserver_client,
security_services=security_services)
if ipspace_name and not self._client.ipspace_has_data_vservers(
ipspace_name):
self._client.delete_ipspace(ipspace_name)

View File

@ -65,10 +65,22 @@ 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'
IPSPACE_NAME = 'fake_ipspace'
BROADCAST_DOMAIN = 'fake_domain'
MTU = 9000
IPSPACES = [{
'uuid': 'fake_uuid',
'ipspace': IPSPACE_NAME,
'id': 'fake_id',
'broadcast-domains': ['OpenStack'],
'ports': [NODE_NAME + ':' + VLAN_PORT],
'vservers': [
IPSPACE_NAME,
VSERVER_NAME,
]
}]
EMS_MESSAGE = {
'computer-name': 'fake_host',
'event-id': '0',
@ -113,6 +125,18 @@ VSERVER_GET_ROOT_VOLUME_NAME_RESPONSE = etree.XML("""
</results>
""" % {'root_volume': ROOT_VOLUME_NAME, 'fake_vserver': VSERVER_NAME})
VSERVER_GET_IPSPACE_NAME_RESPONSE = etree.XML("""
<results status="passed">
<attributes-list>
<vserver-info>
<ipspace>%(ipspace)s</ipspace>
<vserver-name>%(fake_vserver)s</vserver-name>
</vserver-info>
</attributes-list>
<num-records>1</num-records>
</results>
""" % {'ipspace': IPSPACE_NAME, 'fake_vserver': VSERVER_NAME})
VSERVER_GET_RESPONSE = etree.XML("""
<results status="passed">
<attributes>
@ -447,6 +471,7 @@ NET_PORT_GET_ITER_BROADCAST_DOMAIN_RESPONSE = etree.XML("""
<results status="passed">
<attributes-list>
<net-port-info>
<ipspace>%(ipspace)s</ipspace>
<broadcast-domain>%(domain)s</broadcast-domain>
<node>%(node)s</node>
<port>%(port)s</port>
@ -454,19 +479,25 @@ NET_PORT_GET_ITER_BROADCAST_DOMAIN_RESPONSE = etree.XML("""
</attributes-list>
<num-records>1</num-records>
</results>
""" % {'domain': BROADCAST_DOMAIN, 'node': NODE_NAME, 'port': PORT})
""" % {
'domain': BROADCAST_DOMAIN,
'node': NODE_NAME,
'port': PORT,
'ipspace': IPSPACE_NAME,
})
NET_PORT_GET_ITER_BROADCAST_DOMAIN_MISSING_RESPONSE = etree.XML("""
<results status="passed">
<attributes-list>
<net-port-info>
<ipspace>%(ipspace)s</ipspace>
<node>%(node)s</node>
<port>%(port)s</port>
</net-port-info>
</attributes-list>
<num-records>1</num-records>
</results>
""" % {'node': NODE_NAME, 'port': PORT})
""" % {'node': NODE_NAME, 'port': PORT, 'ipspace': IPSPACE_NAME})
NET_PORT_BROADCAST_DOMAIN_GET_ITER_RESPONSE = etree.XML("""
<results status="passed">
@ -478,7 +509,35 @@ NET_PORT_BROADCAST_DOMAIN_GET_ITER_RESPONSE = etree.XML("""
</attributes-list>
<num-records>1</num-records>
</results>
""" % {'domain': BROADCAST_DOMAIN, 'ipspace': IPSPACE})
""" % {'domain': BROADCAST_DOMAIN, 'ipspace': IPSPACE_NAME})
NET_IPSPACES_GET_ITER_RESPONSE = etree.XML("""
<results status="passed">
<attributes-list>
<net-ipspaces-info>
<broadcast-domains>
<broadcast-domain-name>OpenStack</broadcast-domain-name>
</broadcast-domains>
<id>fake_id</id>
<ipspace>%(ipspace)s</ipspace>
<ports>
<net-qualified-port-name>%(node)s:%(port)s</net-qualified-port-name>
</ports>
<uuid>fake_uuid</uuid>
<vservers>
<vserver-name>%(ipspace)s</vserver-name>
<vserver-name>%(vserver)s</vserver-name>
</vservers>
</net-ipspaces-info>
</attributes-list>
<num-records>1</num-records>
</results>
""" % {
'ipspace': IPSPACE_NAME,
'node': NODE_NAME,
'port': VLAN_PORT,
'vserver': VSERVER_NAME
})
NET_INTERFACE_GET_ITER_RESPONSE = etree.XML("""
<results status="passed">

View File

@ -140,7 +140,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.assertFalse(result)
def test_create_vserver(self):
def test_create_vserver_no_ipspace(self):
self.mock_object(self.client, 'send_request')
@ -160,12 +160,52 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.create_vserver(fake.VSERVER_NAME,
fake.ROOT_VOLUME_AGGREGATE_NAME,
fake.ROOT_VOLUME_NAME,
fake.SHARE_AGGREGATE_NAMES)
fake.SHARE_AGGREGATE_NAMES,
None)
self.client.send_request.assert_has_calls([
mock.call('vserver-create', vserver_create_args),
mock.call('vserver-modify', vserver_modify_args)])
def test_create_vserver_with_ipspace(self):
self.client.features.add_feature('IPSPACES')
self.mock_object(self.client, 'send_request')
vserver_create_args = {
'vserver-name': fake.VSERVER_NAME,
'root-volume-security-style': 'unix',
'root-volume-aggregate': fake.ROOT_VOLUME_AGGREGATE_NAME,
'root-volume': fake.ROOT_VOLUME_NAME,
'name-server-switch': {'nsswitch': 'file'},
'ipspace': fake.IPSPACE_NAME,
}
vserver_modify_args = {
'aggr-list': [{'aggr-name': aggr_name} for aggr_name
in fake.SHARE_AGGREGATE_NAMES],
'vserver-name': fake.VSERVER_NAME
}
self.client.create_vserver(fake.VSERVER_NAME,
fake.ROOT_VOLUME_AGGREGATE_NAME,
fake.ROOT_VOLUME_NAME,
fake.SHARE_AGGREGATE_NAMES,
fake.IPSPACE_NAME)
self.client.send_request.assert_has_calls([
mock.call('vserver-create', vserver_create_args),
mock.call('vserver-modify', vserver_modify_args)])
def test_create_vserver_ipspaces_not_supported(self):
self.assertRaises(exception.NetAppException,
self.client.create_vserver,
fake.VSERVER_NAME,
fake.ROOT_VOLUME_AGGREGATE_NAME,
fake.ROOT_VOLUME_NAME,
fake.SHARE_AGGREGATE_NAMES,
fake.IPSPACE_NAME)
def test_get_vserver_root_volume_name(self):
api_response = netapp_api.NaElement(
@ -187,8 +227,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
def test_get_vserver_root_volume_name_not_found(self):
api_response = netapp_api.NaElement(
fake.NO_RECORDS_RESPONSE)
api_response = netapp_api.NaElement(fake.NO_RECORDS_RESPONSE)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
@ -197,6 +236,96 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.get_vserver_root_volume_name,
fake.VSERVER_NAME)
def test_get_vserver_ipspace(self):
self.client.features.add_feature('IPSPACES')
api_response = netapp_api.NaElement(
fake.VSERVER_GET_IPSPACE_NAME_RESPONSE)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
result = self.client.get_vserver_ipspace(fake.VSERVER_NAME)
vserver_get_iter_args = {
'query': {
'vserver-info': {
'vserver-name': fake.VSERVER_NAME,
},
},
'desired-attributes': {
'vserver-info': {
'ipspace': None,
},
},
}
self.client.send_request.assert_has_calls([
mock.call('vserver-get-iter', vserver_get_iter_args)])
self.assertEqual(fake.IPSPACE_NAME, result)
def test_get_vserver_ipspace_not_supported(self):
result = self.client.get_vserver_ipspace(fake.IPSPACE_NAME)
self.assertIsNone(result)
def test_get_vserver_ipspace_not_found(self):
self.client.features.add_feature('IPSPACES')
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_vserver_ipspace,
fake.IPSPACE_NAME)
def test_ipspace_has_data_vservers(self):
self.client.features.add_feature('IPSPACES')
api_response = netapp_api.NaElement(fake.VSERVER_GET_ITER_RESPONSE)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
result = self.client.ipspace_has_data_vservers(fake.IPSPACE_NAME)
vserver_get_iter_args = {
'query': {
'vserver-info': {
'ipspace': fake.IPSPACE_NAME,
'vserver-type': 'data'
},
},
'desired-attributes': {
'vserver-info': {
'vserver-name': None,
},
},
}
self.client.send_request.assert_has_calls([
mock.call('vserver-get-iter', vserver_get_iter_args)])
self.assertTrue(result)
def test_ipspace_has_data_vservers_not_supported(self):
result = self.client.ipspace_has_data_vservers(fake.IPSPACE_NAME)
self.assertFalse(result)
def test_ipspace_has_data_vservers_not_found(self):
self.client.features.add_feature('IPSPACES')
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.ipspace_has_data_vservers(fake.IPSPACE_NAME)
self.assertFalse(result)
def test_list_vservers(self):
api_response = netapp_api.NaElement(
@ -615,7 +744,8 @@ class NetAppClientCmodeTestCase(test.TestCase):
fake.NODE_NAME, fake.PORT,
fake.VSERVER_NAME,
fake.NET_ALLOCATION_ID,
fake.LIF_NAME_TEMPLATE)
fake.LIF_NAME_TEMPLATE,
fake.IPSPACE_NAME)
if use_vlans:
self.client._create_vlan.assert_called_with(
@ -625,7 +755,8 @@ class NetAppClientCmodeTestCase(test.TestCase):
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)
fake.NODE_NAME, fake.VLAN_PORT if use_vlans else fake.PORT,
ipspace=fake.IPSPACE_NAME)
else:
self.assertFalse(
self.client._ensure_broadcast_domain_for_port.called)
@ -678,54 +809,15 @@ class NetAppClientCmodeTestCase(test.TestCase):
fake.PORT,
fake.VLAN)
def test_ensure_broadcast_domain_for_port_has_domain(self):
def test_ensure_broadcast_domain_for_port_domain_match(self):
port_info = {
'ipspace': fake.IPSPACE_NAME,
'broadcast-domain': fake.BROADCAST_DOMAIN,
}
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))
mock.Mock(return_value=port_info))
self.mock_object(self.client,
'_broadcast_domain_exists',
mock.Mock(return_value=True))
@ -734,16 +826,76 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client._ensure_broadcast_domain_for_port(
fake.NODE_NAME, fake.PORT, domain=fake.BROADCAST_DOMAIN,
ipspace=fake.IPSPACE)
ipspace=fake.IPSPACE_NAME)
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_other_domain(self):
port_info = {
'ipspace': fake.IPSPACE_NAME,
'broadcast-domain': 'other_domain',
}
self.mock_object(self.client,
'_get_broadcast_domain_for_port',
mock.Mock(return_value=port_info))
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, '_remove_port_from_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_NAME)
self.client._get_broadcast_domain_for_port.assert_has_calls([
mock.call(fake.NODE_NAME, fake.PORT)])
self.client._remove_port_from_broadcast_domain.assert_has_calls([
mock.call(fake.NODE_NAME, fake.PORT, 'other_domain',
fake.IPSPACE_NAME)])
self.client._broadcast_domain_exists.assert_has_calls([
mock.call(fake.BROADCAST_DOMAIN, fake.IPSPACE)])
mock.call(fake.BROADCAST_DOMAIN, fake.IPSPACE_NAME)])
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)])
fake.IPSPACE_NAME)])
def test_ensure_broadcast_domain_for_port_no_domain(self):
port_info = {
'ipspace': fake.IPSPACE_NAME,
'broadcast-domain': None,
}
self.mock_object(self.client,
'_get_broadcast_domain_for_port',
mock.Mock(return_value=port_info))
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, '_remove_port_from_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_NAME)
self.client._get_broadcast_domain_for_port.assert_has_calls([
mock.call(fake.NODE_NAME, fake.PORT)])
self.assertFalse(self.client._remove_port_from_broadcast_domain.called)
self.client._broadcast_domain_exists.assert_has_calls([
mock.call(fake.BROADCAST_DOMAIN, fake.IPSPACE_NAME)])
self.client._create_broadcast_domain.assert_has_calls([
mock.call(fake.BROADCAST_DOMAIN, fake.IPSPACE_NAME)])
self.client._add_port_to_broadcast_domain.assert_has_calls([
mock.call(fake.NODE_NAME, fake.PORT, fake.BROADCAST_DOMAIN,
fake.IPSPACE_NAME)])
def test_get_broadcast_domain_for_port(self):
@ -763,15 +915,20 @@ class NetAppClientCmodeTestCase(test.TestCase):
'desired-attributes': {
'net-port-info': {
'broadcast-domain': None,
'ipspace': None,
},
},
}
result = self.client._get_broadcast_domain_for_port(fake.NODE_NAME,
fake.PORT)
expected = {
'broadcast-domain': fake.BROADCAST_DOMAIN,
'ipspace': fake.IPSPACE_NAME,
}
self.client.send_request.assert_has_calls([
mock.call('net-port-get-iter', net_port_get_iter_args)])
self.assertEqual(fake.BROADCAST_DOMAIN, result)
self.assertEqual(expected, result)
def test_get_broadcast_domain_for_port_port_not_found(self):
@ -797,7 +954,11 @@ class NetAppClientCmodeTestCase(test.TestCase):
result = self.client._get_broadcast_domain_for_port(fake.NODE_NAME,
fake.PORT)
self.assertIsNone(result)
expected = {
'broadcast-domain': None,
'ipspace': fake.IPSPACE_NAME,
}
self.assertEqual(expected, result)
def test_broadcast_domain_exists(self):
@ -808,12 +969,12 @@ class NetAppClientCmodeTestCase(test.TestCase):
mock.Mock(return_value=api_response))
result = self.client._broadcast_domain_exists(fake.BROADCAST_DOMAIN,
fake.IPSPACE)
fake.IPSPACE_NAME)
net_port_broadcast_domain_get_iter_args = {
'query': {
'net-port-broadcast-domain-info': {
'ipspace': fake.IPSPACE,
'ipspace': fake.IPSPACE_NAME,
'broadcast-domain': fake.BROADCAST_DOMAIN,
},
},
@ -835,7 +996,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
mock.Mock(return_value=api_response))
result = self.client._broadcast_domain_exists(fake.BROADCAST_DOMAIN,
fake.IPSPACE)
fake.IPSPACE_NAME)
self.assertFalse(result)
@ -844,11 +1005,11 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.mock_object(self.client, 'send_request')
result = self.client._create_broadcast_domain(fake.BROADCAST_DOMAIN,
fake.IPSPACE,
fake.IPSPACE_NAME,
mtu=fake.MTU)
net_port_broadcast_domain_create_args = {
'ipspace': fake.IPSPACE,
'ipspace': fake.IPSPACE_NAME,
'broadcast-domain': fake.BROADCAST_DOMAIN,
'mtu': fake.MTU,
}
@ -857,12 +1018,55 @@ class NetAppClientCmodeTestCase(test.TestCase):
mock.call('net-port-broadcast-domain-create',
net_port_broadcast_domain_create_args)])
def test_delete_broadcast_domain(self):
self.mock_object(self.client, 'send_request')
result = self.client._delete_broadcast_domain(fake.BROADCAST_DOMAIN,
fake.IPSPACE_NAME)
net_port_broadcast_domain_delete_args = {
'ipspace': fake.IPSPACE_NAME,
'broadcast-domain': fake.BROADCAST_DOMAIN,
}
self.assertIsNone(result)
self.client.send_request.assert_has_calls([
mock.call('net-port-broadcast-domain-destroy',
net_port_broadcast_domain_delete_args)])
def test_delete_broadcast_domains_for_ipspace_not_found(self):
self.mock_object(self.client,
'get_ipspaces',
mock.Mock(return_value=[]))
self.mock_object(self.client, '_delete_broadcast_domain')
self.client._delete_broadcast_domains_for_ipspace(fake.IPSPACE_NAME)
self.client.get_ipspaces.assert_called_once_with(
ipspace_name=fake.IPSPACE_NAME)
self.assertFalse(self.client._delete_broadcast_domain.called)
def test_delete_broadcast_domains_for_ipspace(self):
self.mock_object(self.client,
'get_ipspaces',
mock.Mock(return_value=fake.IPSPACES))
self.mock_object(self.client, '_delete_broadcast_domain')
self.client._delete_broadcast_domains_for_ipspace(fake.IPSPACE_NAME)
self.client.get_ipspaces.assert_called_once_with(
ipspace_name=fake.IPSPACE_NAME)
self.client._delete_broadcast_domain.assert_called_once_with(
fake.IPSPACES[0]['broadcast-domains'][0], fake.IPSPACE_NAME)
def test_add_port_to_broadcast_domain(self):
self.mock_object(self.client, 'send_request')
add_port_to_broadcast_domain_args = {
'ipspace': fake.IPSPACE,
'ipspace': fake.IPSPACE_NAME,
'broadcast-domain': fake.BROADCAST_DOMAIN,
'ports': {
'net-qualified-port-name': ':'.join([fake.NODE_NAME,
@ -871,7 +1075,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
}
result = self.client._add_port_to_broadcast_domain(
fake.NODE_NAME, fake.VLAN_PORT, fake.BROADCAST_DOMAIN,
fake.IPSPACE)
fake.IPSPACE_NAME)
self.assertIsNone(result)
self.client.send_request.assert_has_calls([
@ -886,7 +1090,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
result = self.client._add_port_to_broadcast_domain(
fake.NODE_NAME, fake.VLAN_PORT, fake.BROADCAST_DOMAIN,
fake.IPSPACE)
fake.IPSPACE_NAME)
self.assertIsNone(result)
@ -899,7 +1103,28 @@ class NetAppClientCmodeTestCase(test.TestCase):
fake.NODE_NAME,
fake.VLAN_PORT,
fake.BROADCAST_DOMAIN,
fake.IPSPACE)
fake.IPSPACE_NAME)
def test_remove_port_from_broadcast_domain(self):
self.mock_object(self.client, 'send_request')
result = self.client._remove_port_from_broadcast_domain(
fake.NODE_NAME, fake.VLAN_PORT, fake.BROADCAST_DOMAIN,
fake.IPSPACE_NAME)
net_port_broadcast_domain_remove_ports_args = {
'ipspace': fake.IPSPACE_NAME,
'broadcast-domain': fake.BROADCAST_DOMAIN,
'ports': {
'net-qualified-port-name': ':'.join([fake.NODE_NAME,
fake.VLAN_PORT])
}
}
self.assertIsNone(result)
self.client.send_request.assert_has_calls([
mock.call('net-port-broadcast-domain-remove-ports',
net_port_broadcast_domain_remove_ports_args)])
def test_network_interface_exists(self):
@ -1059,6 +1284,128 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.send_request.assert_has_calls([
mock.call('net-interface-delete', net_interface_delete_args)])
def test_get_ipspaces(self):
self.client.features.add_feature('IPSPACES')
api_response = netapp_api.NaElement(
fake.NET_IPSPACES_GET_ITER_RESPONSE)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
result = self.client.get_ipspaces(ipspace_name=fake.IPSPACE_NAME,
max_records=500)
net_ipspaces_get_iter_args = {
'max-records': 500,
'query': {
'net-ipspaces-info': {
'ipspace': fake.IPSPACE_NAME,
},
},
}
self.client.send_request.assert_has_calls([
mock.call('net-ipspaces-get-iter', net_ipspaces_get_iter_args)])
self.assertEqual(fake.IPSPACES, result)
def test_get_ipspaces_not_found(self):
self.client.features.add_feature('IPSPACES')
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.get_ipspaces()
net_ipspaces_get_iter_args = {'max-records': 1000}
self.client.send_request.assert_has_calls([
mock.call('net-ipspaces-get-iter', net_ipspaces_get_iter_args)])
self.assertEqual([], result)
def test_get_ipspaces_not_supported(self):
self.mock_object(self.client, 'send_request')
result = self.client.get_ipspaces()
self.assertFalse(self.client.send_request.called)
self.assertEqual([], result)
@ddt.data((fake.NET_IPSPACES_GET_ITER_RESPONSE, True),
(fake.NO_RECORDS_RESPONSE, False))
@ddt.unpack
def test_ipspace_exists(self, api_response, expected):
self.client.features.add_feature('IPSPACES')
api_response = netapp_api.NaElement(api_response)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
result = self.client.ipspace_exists(fake.IPSPACE_NAME)
net_ipspaces_get_iter_args = {
'query': {
'net-ipspaces-info': {
'ipspace': fake.IPSPACE_NAME,
},
},
'desired-attributes': {
'net-ipspaces-info': {
'ipspace': None,
},
},
}
self.client.send_request.assert_has_calls([
mock.call('net-ipspaces-get-iter', net_ipspaces_get_iter_args)])
self.assertEqual(expected, result)
def test_ipspace_exists_not_supported(self):
result = self.client.ipspace_exists(fake.IPSPACE_NAME)
self.assertFalse(result)
def test_create_ipspace(self):
self.mock_object(self.client, 'send_request')
self.client.create_ipspace(fake.IPSPACE_NAME)
net_ipspaces_create_args = {'ipspace': fake.IPSPACE_NAME}
self.client.send_request.assert_has_calls([
mock.call('net-ipspaces-create', net_ipspaces_create_args)])
def test_delete_ipspace(self):
mock_delete_broadcast_domains_for_ipspace = self.mock_object(
self.client, '_delete_broadcast_domains_for_ipspace')
self.mock_object(self.client, 'send_request')
self.client.delete_ipspace(fake.IPSPACE_NAME)
net_ipspaces_destroy_args = {'ipspace': fake.IPSPACE_NAME}
mock_delete_broadcast_domains_for_ipspace.assert_called_once_with(
fake.IPSPACE_NAME)
self.client.send_request.assert_has_calls([
mock.call('net-ipspaces-destroy', net_ipspaces_destroy_args)])
def test_add_vserver_to_ipspace(self):
self.mock_object(self.client, 'send_request')
self.client.add_vserver_to_ipspace(fake.IPSPACE_NAME,
fake.VSERVER_NAME)
net_ipspaces_assign_vserver_args = {
'ipspace': fake.IPSPACE_NAME,
'vserver': fake.VSERVER_NAME
}
self.client.send_request.assert_has_calls([
mock.call('net-ipspaces-assign-vserver',
net_ipspaces_assign_vserver_args)])
def test_get_node_for_aggregate(self):
api_response = netapp_api.NaElement(

View File

@ -199,9 +199,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
'_get_vserver_name',
mock.Mock(return_value=fake.VSERVER1))
mock_create_vserver = self.mock_object(
self.library,
'_create_vserver_if_nonexistent')
mock_create_vserver = self.mock_object(self.library, '_create_vserver')
mock_validate_network_type = self.mock_object(
self.library,
@ -224,7 +222,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
fake_exception = exception.ManilaException("fake")
mock_create_vserver = self.mock_object(
self.library,
'_create_vserver_if_nonexistent',
'_create_vserver',
mock.Mock(side_effect=fake_exception))
mock_validate_network_type = self.mock_object(
@ -270,7 +268,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertEqual(vserver_name, actual_result)
def test_create_vserver_if_nonexistent(self):
def test_create_vserver(self):
vserver_id = fake.NETWORK_INFO['server_id']
vserver_name = fake.VSERVER_NAME_TEMPLATE % vserver_id
@ -288,27 +286,26 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.mock_object(self.library,
'_find_matching_aggregates',
mock.Mock(return_value=fake.AGGREGATES))
self.mock_object(self.library,
'_create_ipspace',
mock.Mock(return_value=fake.IPSPACE))
self.mock_object(self.library, '_create_vserver_lifs')
self.library._create_vserver_if_nonexistent(vserver_name,
fake.NETWORK_INFO)
self.library._get_api_client.assert_called_with(vserver=vserver_name)
self.library._create_vserver(vserver_name, fake.NETWORK_INFO)
self.library._create_ipspace.assert_called_with(fake.NETWORK_INFO)
self.library._client.create_vserver.assert_called_with(
vserver_name,
fake.ROOT_VOLUME_AGGREGATE,
fake.ROOT_VOLUME,
fake.AGGREGATES)
vserver_name, fake.ROOT_VOLUME_AGGREGATE, fake.ROOT_VOLUME,
fake.AGGREGATES, fake.IPSPACE)
self.library._get_api_client.assert_called_with(vserver=vserver_name)
self.library._create_vserver_lifs.assert_called_with(
vserver_name,
vserver_client,
fake.NETWORK_INFO)
vserver_name, vserver_client, fake.NETWORK_INFO, fake.IPSPACE)
self.assertTrue(vserver_client.enable_nfs.called)
self.library._client.setup_security_services.assert_called_with(
fake.NETWORK_INFO['security_services'],
vserver_client,
fake.NETWORK_INFO['security_services'], vserver_client,
vserver_name)
def test_create_vserver_if_nonexistent_already_present(self):
def test_create_vserver_already_present(self):
vserver_id = fake.NETWORK_INFO['server_id']
vserver_name = fake.VSERVER_NAME_TEMPLATE % vserver_id
@ -321,13 +318,12 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock.Mock(return_value=True))
self.assertRaises(exception.NetAppException,
self.library._create_vserver_if_nonexistent,
self.library._create_vserver,
vserver_name,
fake.NETWORK_INFO)
@ddt.data(netapp_api.NaApiError, exception.NetAppException)
def test_create_vserver_if_nonexistent_lif_creation_failure(self,
lif_exception):
def test_create_vserver_lif_creation_failure(self, lif_exception):
vserver_id = fake.NETWORK_INFO['server_id']
vserver_name = fake.VSERVER_NAME_TEMPLATE % vserver_id
@ -345,12 +341,16 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.mock_object(self.library,
'_find_matching_aggregates',
mock.Mock(return_value=fake.AGGREGATES))
self.mock_object(self.library,
'_create_ipspace',
mock.Mock(return_value=fake.IPSPACE))
self.mock_object(self.library,
'_create_vserver_lifs',
mock.Mock(side_effect=lif_exception))
self.mock_object(self.library, '_delete_vserver')
self.assertRaises(lif_exception,
self.library._create_vserver_if_nonexistent,
self.library._create_vserver,
vserver_name,
fake.NETWORK_INFO)
@ -359,13 +359,85 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library._create_vserver_lifs.assert_called_with(
vserver_name,
vserver_client,
fake.NETWORK_INFO)
self.library._client.delete_vserver.assert_called_once_with(
fake.NETWORK_INFO,
fake.IPSPACE)
self.library._delete_vserver.assert_called_once_with(
vserver_name,
vserver_client)
security_services=None)
self.assertFalse(vserver_client.enable_nfs.called)
self.assertEqual(1, lib_multi_svm.LOG.error.call_count)
def test_get_valid_ipspace_name(self):
result = self.library._get_valid_ipspace_name(fake.IPSPACE_ID)
expected = 'ipspace_' + fake.IPSPACE_ID.replace('-', '_')
self.assertEqual(expected, result)
def test_create_ipspace_not_supported(self):
self.library._client.features.IPSPACES = False
result = self.library._create_ipspace(fake.NETWORK_INFO)
self.assertIsNone(result)
@ddt.data(None, 'flat')
def test_create_ipspace_not_vlan(self, network_type):
self.library._client.features.IPSPACES = True
network_info = copy.deepcopy(fake.NETWORK_INFO)
network_info['segmentation_id'] = None
network_info['network_type'] = network_type
result = self.library._create_ipspace(network_info)
self.assertEqual('Default', result)
def test_create_ipspace_not_neutron(self):
self.library._client.features.IPSPACES = True
network_info = copy.deepcopy(fake.NETWORK_INFO)
network_info['neutron_subnet_id'] = None
network_info['nova_net_id'] = 'fake_nova_net_id'
result = self.library._create_ipspace(network_info)
self.assertEqual('Default', result)
def test_create_ipspace_already_present(self):
self.library._client.features.IPSPACES = True
self.mock_object(self.library._client,
'ipspace_exists',
mock.Mock(return_value=True))
result = self.library._create_ipspace(fake.NETWORK_INFO)
expected = self.library._get_valid_ipspace_name(
fake.NETWORK_INFO['neutron_subnet_id'])
self.assertEqual(expected, result)
self.library._client.ipspace_exists.assert_has_calls([
mock.call(expected)])
self.assertFalse(self.library._client.create_ipspace.called)
def test_create_ipspace(self):
self.library._client.features.IPSPACES = True
self.mock_object(self.library._client,
'ipspace_exists',
mock.Mock(return_value=False))
result = self.library._create_ipspace(fake.NETWORK_INFO)
expected = self.library._get_valid_ipspace_name(
fake.NETWORK_INFO['neutron_subnet_id'])
self.assertEqual(expected, result)
self.library._client.ipspace_exists.assert_has_calls([
mock.call(expected)])
self.library._client.create_ipspace.assert_has_calls([
mock.call(expected)])
def test_create_vserver_lifs(self):
self.mock_object(self.library._client,
@ -378,7 +450,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library._create_vserver_lifs(fake.VSERVER1,
'fake_vserver_client',
fake.NETWORK_INFO)
fake.NETWORK_INFO,
fake.IPSPACE)
self.library._create_lif_if_nonexistent.assert_has_calls([
mock.call(
@ -389,6 +462,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
fake.NODE_DATA_PORT,
fake.NETWORK_INFO['network_allocations'][0]['ip_address'],
fake.NETWORK_INFO_NETMASK,
fake.IPSPACE,
'fake_vserver_client'),
mock.call(
fake.VSERVER1,
@ -398,6 +472,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
fake.NODE_DATA_PORT,
fake.NETWORK_INFO['network_allocations'][1]['ip_address'],
fake.NETWORK_INFO_NETMASK,
fake.IPSPACE,
'fake_vserver_client')])
def test_get_node_data_port(self):
@ -437,6 +512,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
'fake_port',
'fake_ip',
'fake_netmask',
fake.IPSPACE,
vserver_client)
self.library._client.create_network_interface.assert_has_calls([
@ -448,7 +524,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
'fake_port',
'fake_vserver',
'fake_allocation_id',
fake.LIF_NAME_TEMPLATE)])
fake.LIF_NAME_TEMPLATE,
fake.IPSPACE)])
def test_create_lif_if_nonexistent_already_present(self):
@ -463,6 +540,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
'fake_port',
'fake_ip',
'fake_netmask',
fake.IPSPACE,
vserver_client)
self.assertFalse(self.library._client.create_network_interface.called)
@ -478,11 +556,9 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
def test_teardown_server(self):
vserver_client = mock.Mock()
self.mock_object(self.library,
'_get_api_client',
mock.Mock(return_value=vserver_client))
self.library._client.vserver_exists.return_value = True
mock_delete_vserver = self.mock_object(self.library,
'_delete_vserver')
self.library.teardown_server(
fake.SHARE_SERVER['backend_details'],
@ -490,22 +566,26 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library._client.vserver_exists.assert_called_once_with(
fake.VSERVER1)
self.library._client.delete_vserver.assert_called_once_with(
mock_delete_vserver.assert_called_once_with(
fake.VSERVER1,
vserver_client,
security_services=fake.NETWORK_INFO['security_services'])
@ddt.data(None, {}, {'vserver_name': None})
def test_teardown_server_no_share_server(self, server_details):
mock_delete_vserver = self.mock_object(self.library,
'_delete_vserver')
self.library.teardown_server(server_details)
self.assertFalse(self.library._client.delete_vserver.called)
self.assertFalse(mock_delete_vserver.called)
self.assertTrue(lib_multi_svm.LOG.warning.called)
def test_teardown_server_no_vserver(self):
self.library._client.vserver_exists.return_value = False
mock_delete_vserver = self.mock_object(self.library,
'_delete_vserver')
self.library.teardown_server(
fake.SHARE_SERVER['backend_details'],
@ -513,5 +593,72 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library._client.vserver_exists.assert_called_once_with(
fake.VSERVER1)
self.assertFalse(self.library._client.delete_vserver.called)
self.assertFalse(mock_delete_vserver.called)
self.assertTrue(lib_multi_svm.LOG.warning.called)
def test_delete_vserver_no_ipspace(self):
self.mock_object(self.library._client,
'get_vserver_ipspace',
mock.Mock(return_value=None))
vserver_client = mock.Mock()
self.mock_object(self.library,
'_get_api_client',
mock.Mock(return_value=vserver_client))
security_services = fake.NETWORK_INFO['security_services']
self.library._delete_vserver(fake.VSERVER1,
security_services=security_services)
self.library._client.get_vserver_ipspace.assert_called_once_with(
fake.VSERVER1)
self.library._client.delete_vserver.assert_called_once_with(
fake.VSERVER1, vserver_client, security_services=security_services)
self.assertFalse(self.library._client.delete_ipspace.called)
def test_delete_vserver_ipspace_has_data_vservers(self):
self.mock_object(self.library._client,
'get_vserver_ipspace',
mock.Mock(return_value=fake.IPSPACE))
vserver_client = mock.Mock()
self.mock_object(self.library,
'_get_api_client',
mock.Mock(return_value=vserver_client))
self.mock_object(self.library._client,
'ipspace_has_data_vservers',
mock.Mock(return_value=True))
security_services = fake.NETWORK_INFO['security_services']
self.library._delete_vserver(fake.VSERVER1,
security_services=security_services)
self.library._client.get_vserver_ipspace.assert_called_once_with(
fake.VSERVER1)
self.library._client.delete_vserver.assert_called_once_with(
fake.VSERVER1, vserver_client, security_services=security_services)
self.assertFalse(self.library._client.delete_ipspace.called)
def test_delete_vserver_with_ipspace(self):
self.mock_object(self.library._client,
'get_vserver_ipspace',
mock.Mock(return_value=fake.IPSPACE))
vserver_client = mock.Mock()
self.mock_object(self.library,
'_get_api_client',
mock.Mock(return_value=vserver_client))
self.mock_object(self.library._client,
'ipspace_has_data_vservers',
mock.Mock(return_value=False))
security_services = fake.NETWORK_INFO['security_services']
self.library._delete_vserver(fake.VSERVER1,
security_services=security_services)
self.library._client.get_vserver_ipspace.assert_called_once_with(
fake.VSERVER1)
self.library._client.delete_vserver.assert_called_once_with(
fake.VSERVER1, vserver_client, security_services=security_services)
self.library._client.delete_ipspace.assert_called_once_with(
fake.IPSPACE)

View File

@ -62,6 +62,8 @@ NODE_DATA_PORTS = ('e0c', 'e0d')
LIF_NAME_TEMPLATE = 'os_%(net_allocation_id)s'
SHARE_TYPE_ID = '26e89a5b-960b-46bb-a8cf-0778e653098f'
SHARE_TYPE_NAME = 'fake_share_type'
IPSPACE = 'fake_ipspace'
IPSPACE_ID = '27d38c27-3e8b-4d7d-9d91-fcf295e3ac8f'
CLIENT_KWARGS = {
'username': 'admin',
@ -203,7 +205,8 @@ NETWORK_INFO = {
'ip_address': '10.10.10.10'},
{'id': '7eabdeed-bad2-46ea-bd0f-a33884c869e0',
'ip_address': '10.10.10.20'}
]
],
'neutron_subnet_id': '62bf1c2c-18eb-421b-8983-48a6d39aafe0',
}
NETWORK_INFO_NETMASK = '255.255.255.0'