NetApp ONTAP: Add support to multiple subnets per AZ

This patch adds support to multiple subnets in a same network
segment (and AZ). All subnets assigned to a share must have the
same VLAN id, otherwise the ONTAP driver will fail on share server
creation.

Depends-On: I7de9de4ae509182e9494bba604979cce03acceec
Implements: blueprint ontap-multiple-subnets-same-segment-id
Change-Id: If5db09627a2a9a98951a972e15b8af0857598d1e
This commit is contained in:
Fernando Ferraz 2022-01-18 15:03:36 -03:00
parent 2b57d15c64
commit e3c12b49f5
9 changed files with 691 additions and 132 deletions

View File

@ -690,15 +690,13 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
return home_port_name
@na_utils.trace
def create_network_interface(self, ip, netmask, vlan, node, port,
vserver_name, lif_name, ipspace_name, mtu):
def create_network_interface(self, ip, netmask, node, port,
vserver_name, lif_name):
"""Creates LIF on VLAN port."""
home_port_name = self.create_port_and_broadcast_domain(
node, port, vlan, mtu, ipspace_name)
LOG.debug('Creating LIF %(lif)s for Vserver %(vserver)s ',
{'lif': lif_name, 'vserver': vserver_name})
LOG.debug('Creating LIF %(lif)s for Vserver %(vserver)s '
'node/port %(node)s:%(port)s.',
{'lif': lif_name, 'vserver': vserver_name, 'node': node,
'port': port})
api_args = {
'address': ip,
@ -708,7 +706,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
{'data-protocol': 'cifs'},
],
'home-node': node,
'home-port': home_port_name,
'home-port': port,
'netmask': netmask,
'interface-name': lif_name,
'role': 'data',
@ -972,18 +970,17 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
@na_utils.trace
def network_interface_exists(self, vserver_name, node, port, ip, netmask,
vlan):
vlan=None, home_port=None):
"""Checks if LIF exists."""
home_port_name = (port if not vlan else
'%(port)s-%(tag)s' % {'port': port, 'tag': vlan})
if not home_port:
home_port = port if not vlan else f'{port}-{vlan}'
api_args = {
'query': {
'net-interface-info': {
'address': ip,
'home-node': node,
'home-port': home_port_name,
'home-port': home_port,
'netmask': netmask,
'vserver': vserver_name,
},

View File

@ -42,6 +42,9 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
'nfs': None,
'cifs': ['active_directory', ]
}
# NetApp driver supports multiple subnets including update existing
# share servers.
self.network_allocation_update_support = True
def do_setup(self, context):
self.library.do_setup(context)
@ -135,8 +138,6 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
self.admin_network_api)
def _setup_server(self, network_info, metadata=None):
# NOTE(felipe_rodrigues): keep legacy network_info support as a dict.
network_info = network_info[0]
return self.library.setup_server(network_info, metadata)
def _teardown_server(self, server_details, **kwargs):
@ -361,3 +362,19 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
return self.library.check_update_share_server_security_service(
context, share_server, network_info, new_security_service,
current_security_service=current_security_service)
def check_update_share_server_network_allocations(
self, context, share_server, current_network_allocations,
new_share_network_subnet, security_services, share_instances,
share_instances_rules):
return self.library.check_update_share_server_network_allocations(
context, share_server, current_network_allocations,
new_share_network_subnet, security_services, share_instances,
share_instances_rules)
def update_share_server_network_allocations(
self, context, share_server, current_network_allocations,
new_network_allocations, security_services, shares, snapshots):
return self.library.update_share_server_network_allocations(
context, share_server, current_network_allocations,
new_network_allocations, security_services, shares, snapshots)

View File

@ -332,3 +332,14 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver):
share_instance_rules, new_security_service,
current_security_service=None):
raise NotImplementedError
def check_update_share_server_network_allocations(
self, context, share_server, current_network_allocations,
new_share_network_subnet, security_services, share_instances,
share_instances_rules):
raise NotImplementedError
def update_share_server_network_allocations(
self, context, share_server, current_network_allocations,
new_network_allocations, security_services, shares, snapshots):
raise NotImplementedError

View File

@ -510,6 +510,7 @@ class NetAppCmodeFileStorageLibrary(object):
'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': self._revert_to_snapshot_support,
'security_service_update_support': True,
'share_server_multiple_subnet_support': True
}
# Add storage service catalog data.

View File

@ -19,7 +19,6 @@ functionality needed by the cDOT multi-SVM Manila driver. This library
variant creates Data ONTAP storage virtual machines (i.e. 'vservers')
as needed to provision shares.
"""
import re
from oslo_log import log
@ -27,6 +26,7 @@ from oslo_serialization import jsonutils
from oslo_utils import excutils
from oslo_utils import units
from manila.common import constants
from manila import exception
from manila.i18n import _
from manila.message import message_field
@ -146,10 +146,13 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
@na_utils.trace
def setup_server(self, network_info, metadata=None):
"""Creates and configures new Vserver."""
vlan = network_info['segmentation_id']
ports = {}
for network_allocation in network_info['network_allocations']:
ports[network_allocation['id']] = network_allocation['ip_address']
server_id = network_info[0]['server_id']
LOG.debug("Setting up server %s.", server_id)
for network in network_info:
for network_allocation in network['network_allocations']:
ports[network_allocation['id']] = (
network_allocation['ip_address'])
nfs_config = self._default_nfs_config
if (self.is_nfs_config_supported and metadata and
@ -159,12 +162,16 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
self._check_nfs_config_extra_specs_validity(extra_specs)
nfs_config = self._get_nfs_config_provisioning_options(extra_specs)
vlan = network_info[0]['segmentation_id']
@utils.synchronized('netapp-VLAN-%s' % vlan, external=True)
def setup_server_with_lock():
LOG.debug('Creating server %s', network_info['server_id'])
self._validate_network_type(network_info)
vserver_name = self._get_vserver_name(network_info['server_id'])
# Before proceeding, make sure subnet configuration is valid
self._validate_share_network_subnets(network_info)
vserver_name = self._get_vserver_name(server_id)
server_details = {
'vserver_name': vserver_name,
'ports': jsonutils.dumps(ports),
@ -219,16 +226,32 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
@na_utils.trace
def _validate_network_type(self, network_info):
"""Raises exception if the segmentation type is incorrect."""
if network_info['network_type'] not in SUPPORTED_NETWORK_TYPES:
unsupported_nets = [network for network in network_info
if network['network_type']
not in SUPPORTED_NETWORK_TYPES]
if unsupported_nets:
msg = _('The specified network type %s is unsupported by the '
'NetApp clustered Data ONTAP driver')
raise exception.NetworkBadConfigurationException(
reason=msg % network_info['network_type'])
reason=msg % unsupported_nets[0]['network_type'])
@na_utils.trace
def _get_vserver_name(self, server_id):
return self.configuration.netapp_vserver_name_template % server_id
@na_utils.trace
def _validate_share_network_subnets(self, network_info):
"""Raises exception if subnet configuration isn't valid."""
# Driver supports multiple subnets only if in the same network segment
ref_vlan = network_info[0]['segmentation_id']
if not all([network['segmentation_id'] == ref_vlan
for network in network_info]):
msg = _("The specified network configuration isn't supported by "
"the NetApp clustered Data ONTAP driver. All subnets must "
"reside in the same network segment.")
raise exception.NetworkBadConfigurationException(reason=msg)
@na_utils.trace
def _create_vserver(self, vserver_name, network_info, metadata=None,
nfs_config=None):
@ -250,9 +273,12 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
# server's neutron subnet id.
node_name = self._client.list_cluster_nodes()[0]
port = self._get_node_data_port(node_name)
vlan = network_info['segmentation_id']
# NOTE(sfernand): ONTAP driver currently supports multiple subnets
# only in a same network segment. A validation is performed in a
# earlier step to make sure all subnets have the same segmentation_id.
vlan = network_info[0]['segmentation_id']
ipspace_name = self._client.get_ipspace_name_for_vlan_port(
node_name, port, vlan) or self._create_ipspace(network_info)
node_name, port, vlan) or self._create_ipspace(network_info[0])
aggregate_names = self._find_matching_aggregates()
if is_dp_destination:
@ -264,7 +290,8 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
aggregate_names,
ipspace_name)
# Set up port and broadcast domain for the current ipspace
self._create_port_and_broadcast_domain(ipspace_name, network_info)
self._create_port_and_broadcast_domain(
ipspace_name, network_info[0])
else:
LOG.debug('Vserver %s does not exist, creating.', vserver_name)
aggr_set = set(aggregate_names).union(
@ -278,7 +305,7 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
vserver_client = self._get_api_client(vserver=vserver_name)
security_services = network_info.get('security_services')
security_services = network_info[0].get('security_services')
try:
self._setup_network_for_vserver(
vserver_name, vserver_client, network_info, ipspace_name,
@ -296,18 +323,38 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
network_info, ipspace_name,
enable_nfs=True, security_services=None,
nfs_config=None):
self._create_vserver_lifs(vserver_name,
vserver_client,
network_info,
ipspace_name)
"""Setup Vserver network configuration"""
# segmentation_id and mtu are the same for all allocations and can be
# extracted from the first index, as subnets were previously checked
# at this point to ensure they are all in the same network segment and
# consequently belongs to the same Neutron network (which holds L2
# information).
ref_subnet_allocation = network_info[0]['network_allocations'][0]
vlan = ref_subnet_allocation['segmentation_id']
mtu = ref_subnet_allocation['mtu'] or DEFAULT_MTU
home_ports = {}
nodes = self._client.list_cluster_nodes()
for node in nodes:
port = self._get_node_data_port(node)
vlan_port_name = self._client.create_port_and_broadcast_domain(
node, port, vlan, mtu, ipspace_name)
home_ports[node] = vlan_port_name
for network in network_info:
self._create_vserver_lifs(vserver_name,
vserver_client,
network,
ipspace_name,
lif_home_ports=home_ports)
self._create_vserver_routes(vserver_client, network)
self._create_vserver_admin_lif(vserver_name,
vserver_client,
network_info,
ipspace_name)
self._create_vserver_routes(vserver_client,
network_info)
network_info[0],
ipspace_name,
lif_home_ports=home_ports)
if enable_nfs:
vserver_client.enable_nfs(
self.configuration.netapp_enabled_share_protocols,
@ -349,34 +396,48 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
@na_utils.trace
def _create_vserver_lifs(self, vserver_name, vserver_client, network_info,
ipspace_name):
ipspace_name, lif_home_ports=None):
"""Create Vserver data logical interfaces (LIFs)."""
# We can get node names directly from lif_home_ports in case
# it was passed as parameter, otherwise a request to the cluster is
nodes = (list(lif_home_ports.keys()) if lif_home_ports
else self._client.list_cluster_nodes())
# required
nodes = self._client.list_cluster_nodes()
node_network_info = zip(nodes, network_info['network_allocations'])
# Creating LIF per node
for node_name, network_allocation in node_network_info:
lif_home_port = (lif_home_ports[node_name] if
lif_home_ports else None)
lif_name = self._get_lif_name(node_name, network_allocation)
self._create_lif(vserver_client, vserver_name, ipspace_name,
node_name, lif_name, network_allocation)
node_name, lif_name, network_allocation,
lif_home_port=lif_home_port)
@na_utils.trace
def _create_vserver_admin_lif(self, vserver_name, vserver_client,
network_info, ipspace_name):
network_info, ipspace_name,
lif_home_ports=None):
"""Create Vserver admin LIF, if defined."""
network_allocations = network_info.get('admin_network_allocations')
if not network_allocations:
LOG.info('No admin network defined for Vserver %s.',
vserver_name)
return
LOG.info('Admin network defined for Vserver %s.', vserver_name)
home_port = None
if lif_home_ports:
node_name, home_port = list(lif_home_ports.items())[0]
else:
nodes = self._client.list_cluster_nodes()
node_name = nodes[0]
node_name = self._client.list_cluster_nodes()[0]
network_allocation = network_allocations[0]
lif_name = self._get_lif_name(node_name, network_allocation)
self._create_lif(vserver_client, vserver_name, ipspace_name,
node_name, lif_name, network_allocation)
node_name, lif_name, network_allocation,
lif_home_port=home_port)
@na_utils.trace
def _create_vserver_routes(self, vserver_client, network_info):
@ -414,21 +475,39 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
@na_utils.trace
def _create_lif(self, vserver_client, vserver_name, ipspace_name,
node_name, lif_name, network_allocation):
node_name, lif_name, network_allocation,
lif_home_port=None):
"""Creates LIF for Vserver."""
port = self._get_node_data_port(node_name)
port = lif_home_port or self._get_node_data_port(node_name)
vlan = network_allocation['segmentation_id']
ip_address = network_allocation['ip_address']
netmask = utils.cidr_to_netmask(network_allocation['cidr'])
vlan = network_allocation['segmentation_id']
network_mtu = network_allocation.get('mtu')
mtu = network_mtu or DEFAULT_MTU
if not vserver_client.network_interface_exists(
vserver_name, node_name, port, ip_address, netmask, vlan):
self._client.create_network_interface(
ip_address, netmask, vlan, node_name, port, vserver_name,
lif_name, ipspace_name, mtu)
# We can skip the operation if an lif already exists with the same
# configuration
if vserver_client.network_interface_exists(
vserver_name, node_name, port, ip_address, netmask, vlan,
home_port=lif_home_port):
msg = ('LIF %(ip)s netmask %(mask)s already exists for '
'node %(node)s port %(port)s in vserver %(vserver)s.' % {
'ip': ip_address,
'mask': netmask,
'node': node_name,
'vserver': vserver_name,
'port': '%(port)s-%(vlan)s' % {'port': port,
'vlan': vlan}})
LOG.debug(msg)
return
if not lif_home_port:
mtu = network_allocation.get('mtu') or DEFAULT_MTU
lif_home_port = (
self._client.create_port_and_broadcast_domain(
node_name, port, vlan, mtu, ipspace_name))
self._client.create_network_interface(
ip_address, netmask, node_name, lif_home_port,
vserver_name, lif_name)
@na_utils.trace
def _create_port_and_broadcast_domain(self, ipspace_name, network_info):
@ -1526,7 +1605,7 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
# NOTE(dviroel): Security service and NFS configuration should be
# handled by SVM DR, so no changes will be made here.
vlan = new_net_allocations['segmentation_id']
vlan = new_net_allocations[0]['segmentation_id']
@utils.synchronized('netapp-VLAN-%s' % vlan, external=True)
def setup_network_for_destination_vserver():
@ -1658,7 +1737,6 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
2. Build the list of export_locations for each share
3. Release all resources from the source share server
"""
new_network_alloc = new_network_alloc[0]
src_backend_name = share_utils.extract_host(
source_share_server['host'], level='backend_name')
src_vserver, src_client = self._get_vserver(
@ -2085,3 +2163,85 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
LOG.info(msg)
return False
return True
def check_update_share_server_network_allocations(
self, context, share_server, current_network_allocations,
new_share_network_subnet, security_services, share_instances,
share_instances_rules):
"""Check if new network configuration is valid."""
LOG.debug('Checking if network configuration is valid to update share'
'server %s.', share_server['id'])
# Get segmentation_id from current allocations to check if added
# subnet is in the same network segment as the others.
ref_subnet = current_network_allocations['subnets'][0]
ref_subnet_allocation = ref_subnet['network_allocations'][0]
seg_id = ref_subnet_allocation['segmentation_id']
new_subnet_seg_id = new_share_network_subnet['segmentation_id']
network_info = [dict(segmentation_id=seg_id),
dict(segmentation_id=new_subnet_seg_id)]
is_valid_configuration = True
try:
self._validate_network_type([new_share_network_subnet])
self._validate_share_network_subnets(network_info)
except exception.NetworkBadConfigurationException as e:
LOG.error('Invalid share server network allocation. %s', e)
is_valid_configuration = False
return is_valid_configuration
def _build_model_update(self, current_network_allocations,
new_network_allocations, export_locations=None):
"""Updates server details for a new set of network allocations"""
ports = {}
for subnet in current_network_allocations['subnets']:
for alloc in subnet['network_allocations']:
ports[alloc['id']] = alloc['ip_address']
for alloc in new_network_allocations['network_allocations']:
ports[alloc['id']] = alloc['ip_address']
model_update = {'server_details': {'ports': jsonutils.dumps(ports)}}
if export_locations:
model_update.update({'share_updates': export_locations})
return model_update
def update_share_server_network_allocations(
self, context, share_server, current_network_allocations,
new_network_allocations, security_services, shares, snapshots):
"""Update network allocations for the share server."""
vserver_name = self._get_vserver_name(share_server['id'])
vserver_client = self._get_api_client(vserver=vserver_name)
ipspace_name = self._client.get_vserver_ipspace(vserver_name)
network_info = [new_network_allocations]
LOG.debug('Adding new subnet allocations to share server %s',
share_server['id'])
try:
self._setup_network_for_vserver(
vserver_name, vserver_client, network_info, ipspace_name,
enable_nfs=False, security_services=None, nfs_config=None)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.error("Failed to update vserver network configuration.")
updates = self._build_model_update(
current_network_allocations, new_network_allocations,
export_locations=None)
e.detail_data = updates
updated_export_locations = {}
for share in shares:
if share['replica_state'] == constants.REPLICA_STATE_ACTIVE:
host = share['host']
export_locations = self._create_export(
share, share_server, vserver_name, vserver_client,
clear_current_export_policy=False,
ensure_share_already_exists=True,
share_host=host)
updated_export_locations.update(
{share['id']: export_locations})
updates = self._build_model_update(
current_network_allocations, new_network_allocations,
updated_export_locations)
return updates

View File

@ -1079,15 +1079,8 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.assertListEqual([], result)
@ddt.data((True, True), (True, False), (False, True), (False, False))
@ddt.unpack
def test_create_network_interface(self, broadcast_domains_supported,
use_vlans):
def test_create_network_interface(self):
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')
lif_create_args = {
@ -1098,7 +1091,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
{'data-protocol': 'cifs'}
],
'home-node': fake.NODE_NAME,
'home-port': fake.VLAN_PORT if use_vlans else fake.PORT,
'home-port': fake.VLAN_PORT,
'netmask': fake.NETMASK,
'interface-name': fake.LIF_NAME,
'role': 'data',
@ -1106,30 +1099,43 @@ class NetAppClientCmodeTestCase(test.TestCase):
}
self.client.create_network_interface(fake.IP_ADDRESS,
fake.NETMASK,
fake.VLAN if use_vlans else None,
fake.NODE_NAME,
fake.PORT,
fake.VLAN_PORT,
fake.VSERVER_NAME,
fake.LIF_NAME,
fake.IPSPACE_NAME,
fake.MTU)
fake.LIF_NAME)
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_called_once_with(
'net-interface-create', lif_create_args)
@ddt.data((None, True), (fake.VLAN, True), (None, False),
(fake.VLAN, False))
@ddt.unpack
def test_create_port_and_broadcast_domain(self, fake_vlan,
broadcast_domains_supported):
self.client.features.add_feature(
'BROADCAST_DOMAINS', broadcast_domains_supported)
mock_create_vlan = self.mock_object(
self.client, '_create_vlan')
mock_ensure_broadcast = self.mock_object(
self.client, '_ensure_broadcast_domain_for_port')
result = self.client.create_port_and_broadcast_domain(
fake.NODE_NAME, fake.PORT, fake_vlan, fake.MTU, fake.IPSPACE_NAME)
if fake_vlan:
mock_create_vlan.assert_called_once_with(
fake.NODE_NAME, fake.PORT, fake_vlan)
fake_home_port_name = (
f'{fake.PORT}-{fake_vlan}' if fake_vlan else fake.PORT)
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.MTU, ipspace=fake.IPSPACE_NAME)
else:
self.assertFalse(
self.client._ensure_broadcast_domain_for_port.called)
mock_ensure_broadcast.assert_called_once_with(
fake.NODE_NAME, fake_home_port_name, fake.MTU,
ipspace=fake.IPSPACE_NAME)
self.client.send_request.assert_has_calls([
mock.call('net-interface-create', lif_create_args)])
self.assertEqual(fake_home_port_name, result)
def test_create_vlan(self):

View File

@ -457,6 +457,9 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock_validate_network_type = self.mock_object(
self.library,
'_validate_network_type')
mock_validate_share_network_subnets = self.mock_object(
self.library,
'_validate_share_network_subnets')
self.library.is_nfs_config_supported = nfs_config_support
mock_get_extra_spec = self.mock_object(
share_types, "get_share_type_extra_specs",
@ -470,7 +473,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
"_get_nfs_config_provisioning_options",
mock.Mock(return_value=nfs_config))
result = self.library.setup_server(fake.NETWORK_INFO,
result = self.library.setup_server(fake.NETWORK_INFO_LIST,
fake.SERVER_METADATA)
ports = {}
@ -478,6 +481,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
ports[network_allocation['id']] = network_allocation['ip_address']
self.assertTrue(mock_validate_network_type.called)
self.assertTrue(mock_validate_share_network_subnets.called)
self.assertTrue(mock_get_vserver_name.called)
self.assertTrue(mock_create_vserver.called)
if nfs_config_support:
@ -517,10 +521,14 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library,
'_validate_network_type')
mock_validate_share_network_subnets = self.mock_object(
self.library,
'_validate_share_network_subnets')
self.assertRaises(
exception.ManilaException,
self.library.setup_server,
fake.NETWORK_INFO,
fake.NETWORK_INFO_LIST,
fake.SERVER_METADATA)
ports = {}
@ -528,6 +536,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
ports[network_allocation['id']] = network_allocation['ip_address']
self.assertTrue(mock_validate_network_type.called)
self.assertTrue(mock_validate_share_network_subnets.called)
self.assertTrue(mock_get_vserver_name.called)
self.assertTrue(mock_create_vserver.called)
@ -538,18 +547,61 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
}},
fake_exception.detail_data)
def test_setup_server_invalid_subnet(self):
invalid_subnet_exception = exception.NetworkBadConfigurationException(
reason='This is a fake message')
self.mock_object(self.library, '_get_vserver_name',
mock.Mock(return_value=fake.VSERVER1))
self.mock_object(self.library, '_validate_network_type')
self.mock_object(self.library, '_validate_share_network_subnets',
mock.Mock(side_effect=invalid_subnet_exception))
self.assertRaises(
exception.NetworkBadConfigurationException,
self.library.setup_server,
fake.NETWORK_INFO_LIST)
self.library._validate_share_network_subnets.assert_called_once_with(
fake.NETWORK_INFO_LIST)
def test_validate_share_network_subnets(self):
fake_vlan = fake.NETWORK_INFO['segmentation_id']
network_info_different_seg_id = copy.deepcopy(fake.NETWORK_INFO)
network_info_different_seg_id['segmentation_id'] = fake_vlan
allocations = network_info_different_seg_id['network_allocations']
allocations[0]['segmentation_id'] = fake_vlan
fake.NETWORK_INFO_LIST.append(network_info_different_seg_id)
result = self.library._validate_share_network_subnets(
fake.NETWORK_INFO_LIST)
self.assertIsNone(result)
def test_validate_share_network_subnets_invalid_vlan_config(self):
network_info_different_seg_id = copy.deepcopy(fake.NETWORK_INFO)
network_info_different_seg_id['segmentation_id'] = 4004
allocations = network_info_different_seg_id['network_allocations']
allocations[0]['segmentation_id'] = 4004
fake.NETWORK_INFO_LIST.append(network_info_different_seg_id)
self.assertRaises(
exception.NetworkBadConfigurationException,
self.library._validate_share_network_subnets,
fake.NETWORK_INFO_LIST)
@ddt.data(
{'network_info': {'network_type': 'vlan', 'segmentation_id': 1000}},
{'network_info': {'network_type': None, 'segmentation_id': None}},
{'network_info': {'network_type': 'flat', 'segmentation_id': None}})
{'network_info': [{'network_type': 'vlan', 'segmentation_id': 1000}]},
{'network_info': [{'network_type': None, 'segmentation_id': None}]},
{'network_info': [{'network_type': 'flat', 'segmentation_id': None}]})
@ddt.unpack
def test_validate_network_type_with_valid_network_types(self,
network_info):
self.library._validate_network_type(network_info)
result = self.library._validate_network_type(network_info)
self.assertIsNone(result)
@ddt.data(
{'network_info': {'network_type': 'vxlan', 'segmentation_id': 1000}},
{'network_info': {'network_type': 'gre', 'segmentation_id': 100}})
{'network_info': [{'network_type': 'vxlan', 'segmentation_id': 1000}]},
{'network_info': [{'network_type': 'gre', 'segmentation_id': 100}]})
@ddt.unpack
def test_validate_network_type_with_invalid_network_types(self,
network_info):
@ -575,8 +627,10 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library.configuration.netapp_enabled_share_protocols = versions
vserver_id = fake.NETWORK_INFO['server_id']
vserver_name = fake.VSERVER_NAME_TEMPLATE % vserver_id
vserver_client = mock.Mock()
fake_lif_home_ports = {fake.CLUSTER_NODES[0]: 'fake_port',
fake.CLUSTER_NODES[1]: 'another_fake_port'}
vserver_client = mock.Mock()
self.mock_object(self.library._client,
'list_cluster_nodes',
mock.Mock(return_value=fake.CLUSTER_NODES))
@ -601,15 +655,22 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.mock_object(self.library,
'_create_ipspace',
mock.Mock(return_value=fake.IPSPACE))
self.mock_object(self.library,
'_create_vserver_lifs')
self.mock_object(self.library,
'_create_vserver_routes')
self.mock_object(self.library,
'_create_vserver_admin_lif')
self.mock_object(self.library._client,
'create_port_and_broadcast_domain',
mock.Mock(side_effect=['fake_port',
'another_fake_port']))
get_ipspace_name_for_vlan_port = self.mock_object(
self.library._client,
'get_ipspace_name_for_vlan_port',
mock.Mock(return_value=existing_ipspace))
self.mock_object(self.library, '_create_vserver_lifs')
self.mock_object(self.library, '_create_vserver_admin_lif')
self.mock_object(self.library, '_create_vserver_routes')
self.library._create_vserver(vserver_name, fake.NETWORK_INFO,
self.library._create_vserver(vserver_name, fake.NETWORK_INFO_LIST,
fake.NFS_CONFIG_TCP_UDP_MAX,
nfs_config=nfs_config)
@ -626,11 +687,13 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library._get_api_client.assert_called_once_with(
vserver=vserver_name)
self.library._create_vserver_lifs.assert_called_once_with(
vserver_name, vserver_client, fake.NETWORK_INFO, fake.IPSPACE)
self.library._create_vserver_admin_lif.assert_called_once_with(
vserver_name, vserver_client, fake.NETWORK_INFO, fake.IPSPACE)
vserver_name, vserver_client, fake.NETWORK_INFO, fake.IPSPACE,
lif_home_ports=fake_lif_home_ports)
self.library._create_vserver_routes.assert_called_once_with(
vserver_client, fake.NETWORK_INFO)
self.library._create_vserver_admin_lif.assert_called_once_with(
vserver_name, vserver_client, fake.NETWORK_INFO, fake.IPSPACE,
lif_home_ports=fake_lif_home_ports)
vserver_client.enable_nfs.assert_called_once_with(
versions, nfs_config=nfs_config)
self.library._client.setup_security_services.assert_called_once_with(
@ -673,7 +736,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock.Mock(return_value=existing_ipspace))
self.mock_object(self.library, '_create_port_and_broadcast_domain')
self.library._create_vserver(vserver_name, fake.NETWORK_INFO,
self.library._create_vserver(vserver_name, fake.NETWORK_INFO_LIST,
metadata={'migration_destination': True})
get_ipspace_name_for_vlan_port.assert_called_once_with(
@ -762,7 +825,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertRaises(network_exception,
self.library._create_vserver,
vserver_name,
fake.NETWORK_INFO,
fake.NETWORK_INFO_LIST,
fake.NFS_CONFIG_TCP_UDP_MAX)
self.library._get_api_client.assert_called_with(vserver=vserver_name)
@ -770,7 +833,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library._setup_network_for_vserver.assert_called_with(
vserver_name,
vserver_client,
fake.NETWORK_INFO,
fake.NETWORK_INFO_LIST,
fake.IPSPACE,
security_services=security_service,
nfs_config=None)
@ -840,10 +903,43 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library._create_lif.assert_has_calls([
mock.call('fake_vserver_client', fake.VSERVER1, fake.IPSPACE,
fake.CLUSTER_NODES[0], 'fake_lif1',
fake.NETWORK_INFO['network_allocations'][0]),
fake.NETWORK_INFO['network_allocations'][0],
lif_home_port=None),
mock.call('fake_vserver_client', fake.VSERVER1, fake.IPSPACE,
fake.CLUSTER_NODES[1], 'fake_lif2',
fake.NETWORK_INFO['network_allocations'][1])])
fake.NETWORK_INFO['network_allocations'][1],
lif_home_port=None)])
def test_create_vserver_lifs_pre_configured_home_ports(self):
self.mock_object(self.library._client,
'list_cluster_nodes',
mock.Mock(return_value=fake.CLUSTER_NODES))
self.mock_object(self.library,
'_get_lif_name',
mock.Mock(side_effect=['fake_lif1', 'fake_lif2']))
self.mock_object(self.library, '_create_lif')
lif_home_ports = {
fake.CLUSTER_NODES[0]: 'fake_port1',
fake.CLUSTER_NODES[1]: 'fake_port2'
}
self.library._create_vserver_lifs(fake.VSERVER1,
'fake_vserver_client',
fake.NETWORK_INFO,
fake.IPSPACE,
lif_home_ports=lif_home_ports)
self.library._create_lif.assert_has_calls([
mock.call('fake_vserver_client', fake.VSERVER1, fake.IPSPACE,
fake.CLUSTER_NODES[0], 'fake_lif1',
fake.NETWORK_INFO['network_allocations'][0],
lif_home_port=lif_home_ports[fake.CLUSTER_NODES[0]]),
mock.call('fake_vserver_client', fake.VSERVER1, fake.IPSPACE,
fake.CLUSTER_NODES[1], 'fake_lif2',
fake.NETWORK_INFO['network_allocations'][1],
lif_home_port=lif_home_ports[fake.CLUSTER_NODES[1]])])
def test_create_vserver_admin_lif(self):
@ -863,7 +959,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library._create_lif.assert_has_calls([
mock.call('fake_vserver_client', fake.VSERVER1, fake.IPSPACE,
fake.CLUSTER_NODES[0], 'fake_admin_lif',
fake.NETWORK_INFO['admin_network_allocations'][0])])
fake.NETWORK_INFO['admin_network_allocations'][0],
lif_home_port=None)])
def test_create_vserver_admin_lif_no_admin_network(self):
@ -947,23 +1044,78 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
network_allocations.pop('mtu')
vserver_client = mock.Mock()
self.mock_object(self.library, '_get_node_data_port',
mock.Mock(return_value='fake_port'))
vserver_client.network_interface_exists = mock.Mock(
return_value=False)
self.mock_object(self.library,
'_get_node_data_port',
mock.Mock(return_value='fake_port'))
self.library._client.create_port_and_broadcast_domain = (
mock.Mock(return_value='fake_port'))
self.library._create_lif(vserver_client,
'fake_vserver',
'fake_ipspace',
'fake_node',
'fake_lif',
network_allocations)
network_allocations,
lif_home_port=None)
self.library._client.create_network_interface.assert_has_calls([
mock.call('10.10.10.10', '255.255.255.0', '1000', 'fake_node',
'fake_port', 'fake_vserver', 'fake_lif',
'fake_ipspace', expected_mtu)])
self.library._get_node_data_port.assert_called_once_with(
'fake_node')
(vserver_client.network_interface_exists
.assert_called_once_with('fake_vserver', 'fake_node', 'fake_port',
'10.10.10.10', '255.255.255.0', '1000',
home_port=None))
(self.library._client.create_port_and_broadcast_domain
.assert_called_once_with('fake_node', 'fake_port', '1000',
expected_mtu, 'fake_ipspace'))
(self.library._client.create_network_interface
.assert_called_once_with('10.10.10.10', '255.255.255.0',
'fake_node', 'fake_port',
'fake_vserver', 'fake_lif'))
def test_create_lif_existent_home_port(self):
"""Tests case where a existent port is passed to the function"""
network_allocations = copy.deepcopy(
fake.NETWORK_INFO['network_allocations'][0])
vserver_client = mock.Mock()
mock_get_node_data_port = self.mock_object(
self.library, '_get_node_data_port')
vserver_client.network_interface_exists = mock.Mock(
return_value=False)
mock_create_port_and_broadcast_domain = (
self.library._client.create_port_and_broadcast_domain)
self.library._create_lif(vserver_client,
'fake_vserver',
'fake_ipspace',
'fake_node',
'fake_lif',
network_allocations,
lif_home_port='fake_port_from_param')
(vserver_client.network_interface_exists
.assert_called_once_with('fake_vserver', 'fake_node',
'fake_port_from_param',
'10.10.10.10', '255.255.255.0', '1000',
home_port='fake_port_from_param'))
mock_get_node_data_port.assert_not_called()
mock_create_port_and_broadcast_domain.assert_not_called()
(self.library._client.create_network_interface
.assert_called_once_with('10.10.10.10', '255.255.255.0',
'fake_node', 'fake_port_from_param',
'fake_vserver', 'fake_lif'))
def test_create_lif_if_nonexistent_already_present(self):
@ -979,7 +1131,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
fake.IPSPACE,
'fake_node',
'fake_lif',
fake.NETWORK_INFO['network_allocations'][0])
fake.NETWORK_INFO['network_allocations'][0],
lif_home_port=None)
self.assertFalse(self.library._client.create_network_interface.called)
@ -2737,12 +2890,12 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library._setup_networking_for_destination_vserver(
self.mock_dest_client, self.fake_vserver,
fake.NETWORK_INFO)
fake.NETWORK_INFO_LIST)
self.mock_dest_client.get_vserver_ipspace.assert_called_once_with(
self.fake_vserver)
self.library._setup_network_for_vserver.assert_called_once_with(
self.fake_vserver, self.mock_dest_client, fake.NETWORK_INFO,
self.fake_vserver, self.mock_dest_client, fake.NETWORK_INFO_LIST,
fake.IPSPACE, enable_nfs=False, security_services=None)
def test__migration_complete_svm_dr(self):
@ -2758,7 +2911,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library._share_server_migration_complete_svm_dr(
self.fake_src_share_server, self.fake_dest_share_server,
self.fake_src_vserver, self.mock_src_client,
[fake.SHARE_INSTANCE], fake.NETWORK_INFO
[fake.SHARE_INSTANCE], fake.NETWORK_INFO_LIST
)
self.library._get_vserver.assert_called_once_with(
@ -2784,7 +2937,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
(self.library._setup_networking_for_destination_vserver
.assert_called_once_with(
self.mock_dest_client, self.fake_dest_vserver,
fake.NETWORK_INFO))
fake.NETWORK_INFO_LIST))
self.mock_dest_client.start_vserver.assert_called_once_with(
self.fake_dest_vserver
)
@ -2852,7 +3005,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.fake_src_share_server,
self.fake_dest_share_server,
share_instances, [],
[fake.NETWORK_INFO]
fake.NETWORK_INFO_LIST
)
expected_share_updates = {
@ -2879,7 +3032,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock_complete_svm_dr.assert_called_once_with(
self.fake_src_share_server, self.fake_dest_share_server,
self.fake_src_vserver, self.mock_src_client,
share_instances, fake.NETWORK_INFO
share_instances, fake.NETWORK_INFO_LIST
)
self.library._delete_share.assert_called_once_with(
fake.SHARE_INSTANCE, self.fake_src_vserver,
@ -2899,7 +3052,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
(self.library._setup_networking_for_destination_vserver.
assert_called_once_with(self.mock_dest_client,
self.fake_src_vserver,
fake.NETWORK_INFO))
fake.NETWORK_INFO_LIST))
if should_recreate_export:
create_export_calls = [
mock.call(
@ -2977,7 +3130,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.fake_src_share_server,
self.fake_dest_share_server,
[fake.SHARE_INSTANCE], [],
[fake.NETWORK_INFO])
fake.NETWORK_INFO_LIST)
self.library._get_vserver.assert_has_calls([
mock.call(share_server=self.fake_src_share_server,
@ -3477,11 +3630,11 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
def test_update_share_server_security_service(self, current, new,
existing):
fake_context = mock.Mock()
fake_net_info = copy.deepcopy(fake.NETWORK_INFO)
fake_net_info = copy.deepcopy(fake.NETWORK_INFO_LIST)
new_sec_service = copy.deepcopy(new)
curr_sec_service = copy.deepcopy(current) if current else None
new_type = new_sec_service['type'].lower()
fake_net_info['security_services'] = existing
fake_net_info[0]['security_services'] = existing
if curr_sec_service:
# domain modification aren't support
@ -3513,7 +3666,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
fake_vserver_client, 'modify_active_directory_security_service')
self.library.update_share_server_security_service(
fake_context, fake.SHARE_SERVER, [fake_net_info],
fake_context, fake.SHARE_SERVER, fake_net_info,
new_sec_service, current_security_service=curr_sec_service)
dns_ips = set()
@ -3530,7 +3683,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock_get_vserver.assert_called_once_with(
share_server=fake.SHARE_SERVER)
mock_check_update.assert_called_once_with(
fake_context, fake.SHARE_SERVER, [fake_net_info], new_sec_service,
fake_context, fake.SHARE_SERVER, fake_net_info, new_sec_service,
current_security_service=curr_sec_service)
if curr_sec_service is None:
@ -3565,13 +3718,13 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertRaises(
exception.NetAppException,
self.library.update_share_server_security_service,
fake_context, fake.SHARE_SERVER, [fake_net_info],
fake_context, fake.SHARE_SERVER, fake_net_info,
new_sec_service, current_security_service=curr_sec_service)
mock_get_vserver.assert_called_once_with(
share_server=fake.SHARE_SERVER)
mock_check_update.assert_called_once_with(
fake_context, fake.SHARE_SERVER, [fake_net_info],
fake_context, fake.SHARE_SERVER, fake_net_info,
new_sec_service, current_security_service=curr_sec_service)
@ddt.data(
@ -3602,3 +3755,173 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
None, None, None, new, current_security_service=current)
self.assertEqual(expected, result)
def test_check_update_share_server_network_allocations(self):
net_alloc_seg_id = fake.USER_NETWORK_ALLOCATIONS[0]['segmentation_id']
network_segments = [
{'segmentation_id': net_alloc_seg_id},
{'segmentation_id': fake.SHARE_NETWORK_SUBNET['segmentation_id']}
]
mock__validate_network_type = self.mock_object(
self.library, '_validate_network_type')
mock__validate_share_network_subnets = self.mock_object(
self.library, '_validate_share_network_subnets')
result = self.library.check_update_share_server_network_allocations(
None, fake.SHARE_SERVER, fake.CURRENT_NETWORK_ALLOCATIONS,
fake.SHARE_NETWORK_SUBNET, None, None,
None)
self.assertTrue(result)
mock__validate_network_type.assert_called_once_with(
[fake.SHARE_NETWORK_SUBNET])
mock__validate_share_network_subnets.assert_called_once_with(
network_segments)
def test_check_update_share_server_network_allocations_fail_on_type(self):
network_exception = exception.NetworkBadConfigurationException(
reason='fake exception message')
mock_validate_network_type = self.mock_object(
self.library, '_validate_network_type',
mock.Mock(side_effect=network_exception))
mock_validate_share_network_subnets = self.mock_object(
self.library, '_validate_share_network_subnets')
result = self.library.check_update_share_server_network_allocations(
None, fake.SHARE_SERVER, fake.CURRENT_NETWORK_ALLOCATIONS,
fake.SHARE_NETWORK_SUBNET, None, None, None)
self.assertFalse(result)
mock_validate_network_type.assert_called_once_with(
[fake.SHARE_NETWORK_SUBNET])
mock_validate_share_network_subnets.assert_not_called()
def test_check_update_share_server_network_allocations_subnets_error(self):
net_alloc_seg_id = fake.USER_NETWORK_ALLOCATIONS[0]['segmentation_id']
network_segments = [
{'segmentation_id': net_alloc_seg_id},
{'segmentation_id': fake.SHARE_NETWORK_SUBNET['segmentation_id']}
]
network_exception = exception.NetworkBadConfigurationException(
reason='fake exception message')
mock__validate_network_type = self.mock_object(
self.library, '_validate_network_type')
mock__validate_share_network_subnets = self.mock_object(
self.library, '_validate_share_network_subnets',
mock.Mock(side_effect=network_exception))
result = self.library.check_update_share_server_network_allocations(
None, fake.SHARE_SERVER, fake.CURRENT_NETWORK_ALLOCATIONS,
fake.SHARE_NETWORK_SUBNET, None, None,
None)
self.assertFalse(result)
mock__validate_network_type.assert_called_once_with(
[fake.SHARE_NETWORK_SUBNET])
mock__validate_share_network_subnets.assert_called_once_with(
network_segments)
@ddt.data(True, False)
def test_build_model_update(self, has_export_locations):
server_model_update = copy.deepcopy(fake.SERVER_MODEL_UPDATE)
export_locations = server_model_update['share_updates']
if not has_export_locations:
export_locations = None
del server_model_update['share_updates']
result = self.library._build_model_update(
fake.CURRENT_NETWORK_ALLOCATIONS, fake.NEW_NETWORK_ALLOCATIONS,
export_locations=export_locations)
self.assertEqual(server_model_update, result)
@ddt.data('active', 'dr')
def test_update_share_server_network_allocations(self, replica_state):
fake_context = mock.Mock()
fake_share_server = fake.SHARE_SERVER
fake_current_network_allocations = fake.USER_NETWORK_ALLOCATIONS
fake_new_network_allocations = fake.USER_NETWORK_ALLOCATIONS
fake_share_instances = [copy.deepcopy(fake.SHARE_INSTANCE)]
fake_share_instances[0]['replica_state'] = replica_state
fake_vserver_name = fake.VSERVER1
fake_vserver_client = mock.Mock()
fake_ipspace_name = fake.IPSPACE
fake_export_locations = fake.NFS_EXPORTS[0]
fake_updates = fake.SERVER_MODEL_UPDATE
fake_updated_export_locations = {
fake_share_instances[0]['id']: fake_export_locations,
}
self.mock_object(self.library, '_get_vserver_name',
mock.Mock(return_value=fake_vserver_name))
self.mock_object(self.library, '_get_api_client',
mock.Mock(return_value=fake_vserver_client))
self.mock_object(self.library._client, 'get_vserver_ipspace',
mock.Mock(return_value=fake_ipspace_name))
self.mock_object(self.library, '_setup_network_for_vserver')
self.mock_object(self.library, '_create_export',
mock.Mock(return_value=fake_export_locations))
self.mock_object(self.library, '_build_model_update',
mock.Mock(return_value=fake_updates))
self.assertEqual(
fake_updates,
self.library.update_share_server_network_allocations(
fake_context, fake_share_server,
fake_current_network_allocations, fake_new_network_allocations,
None, fake_share_instances, None))
self.library._get_vserver_name.assert_called_once_with(
fake_share_server['id'])
self.library._get_api_client.assert_called_once_with(
vserver=fake_vserver_name)
self.library._client.get_vserver_ipspace.assert_called_once_with(
fake_vserver_name)
self.library._setup_network_for_vserver.assert_called_once_with(
fake_vserver_name, fake_vserver_client,
[fake_new_network_allocations], fake_ipspace_name,
enable_nfs=False, security_services=None, nfs_config=None)
if replica_state == 'active':
self.library._create_export.assert_called_once_with(
fake_share_instances[0], fake_share_server,
fake_vserver_name, fake_vserver_client,
clear_current_export_policy=False,
ensure_share_already_exists=True,
share_host=fake_share_instances[0]['host'])
else:
self.library._create_export.assert_not_called()
fake_updated_export_locations = {}
self.library._build_model_update.assert_called_once_with(
fake_current_network_allocations, fake_new_network_allocations,
fake_updated_export_locations)
def test_update_share_server_network_allocations_setup_network_fail(self):
fake_context = mock.Mock()
fake_share_server = fake.SHARE_SERVER
fake_current_network_allocations = fake.USER_NETWORK_ALLOCATIONS
fake_new_network_allocations = fake.USER_NETWORK_ALLOCATIONS
fake_share_instances = [fake.SHARE_INSTANCE]
fake_updates = fake.SERVER_MODEL_UPDATE
self.mock_object(self.library, '_get_vserver_name')
self.mock_object(self.library, '_get_api_client')
self.mock_object(self.library._client, 'get_vserver_ipspace')
self.mock_object(self.library, '_setup_network_for_vserver',
mock.Mock(side_effect=netapp_api.NaApiError))
self.mock_object(self.library, '_build_model_update',
mock.Mock(return_value=fake_updates))
self.assertRaises(netapp_api.NaApiError,
self.library.update_share_server_network_allocations,
fake_context, fake_share_server,
fake_current_network_allocations,
fake_new_network_allocations, None,
fake_share_instances, None)
self.library._build_model_update.assert_called_once_with(
fake_current_network_allocations, fake_new_network_allocations,
export_locations=None)

View File

@ -516,6 +516,7 @@ NETWORK_INFO = {
'neutron_subnet_id': '62bf1c2c-18eb-421b-8983-48a6d39aafe0',
'segmentation_id': '1000',
}
NETWORK_INFO_LIST = [NETWORK_INFO]
NETWORK_INFO_NETMASK = '255.255.255.0'
SHARE_SERVER = {
@ -985,6 +986,7 @@ POOLS = [
'revert_to_snapshot_support': True,
'qos': True,
'security_service_update_support': True,
'share_server_multiple_subnet_support': True,
'netapp_flexgroup': False,
},
{
@ -1011,6 +1013,7 @@ POOLS = [
'revert_to_snapshot_support': True,
'qos': True,
'security_service_update_support': True,
'share_server_multiple_subnet_support': True,
'netapp_flexgroup': False,
},
]
@ -1037,6 +1040,7 @@ POOLS_VSERVER_CREDS = [
'revert_to_snapshot_support': True,
'qos': False,
'security_service_update_support': True,
'share_server_multiple_subnet_support': True,
'netapp_flexgroup': False,
},
{
@ -1058,6 +1062,7 @@ POOLS_VSERVER_CREDS = [
'revert_to_snapshot_support': True,
'qos': False,
'security_service_update_support': True,
'share_server_multiple_subnet_support': True,
'netapp_flexgroup': False,
},
]
@ -1766,6 +1771,38 @@ CLIENT_GET_VOLUME_RESPONSE = {
'qos-policy-group-name': QOS_POLICY_GROUP_NAME,
}
SHARE_INSTANCE_LIST = [SHARE_INSTANCE]
CURRENT_NETWORK_ALLOCATIONS = {
'admin_network_allocations': ADMIN_NETWORK_ALLOCATIONS,
'subnets': [
{
'share_network_subnet_id': '0bdeaa8c6db3-3bc10d67',
'neutron_net_id': '2598-4122-bb62-0bdeaa8c6db3',
'neutron_subnet_id': '3bc10d67-2598-4122-bb62',
'network_allocations': USER_NETWORK_ALLOCATIONS
}
]
}
NEW_NETWORK_ALLOCATIONS = {
'share_network_subnet_id': '0bdeaa8c6db3-3bc10d67',
'neutron_net_id': '2598-4122-bb62-0bdeaa8c6db3',
'neutron_subnet_id': '3bc10d67-2598-4122-bb62',
'network_allocations': USER_NETWORK_ALLOCATIONS
}
SERVER_MODEL_UPDATE = {
'server_details': {
'ports': '{"%s": "%s", "%s": "%s"}' % (
USER_NETWORK_ALLOCATIONS[0]['id'],
USER_NETWORK_ALLOCATIONS[0]['ip_address'],
USER_NETWORK_ALLOCATIONS[1]['id'],
USER_NETWORK_ALLOCATIONS[1]['ip_address'])
},
'share_updates': {SHARE_INSTANCE['id']: NFS_EXPORTS[0]},
}
def get_config_cmode():
config = na_fakes.create_configuration_cmode()

View File

@ -0,0 +1,7 @@
---
features:
- |
NetApp ONTAP: Add support for multiple subnets per availability zone
when in the same network segment. In addition, new share network
subnets can now be added to share networks with in-use share
servers (that has one or more shares in place).