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:
parent
2b57d15c64
commit
e3c12b49f5
@ -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,
|
||||
},
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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).
|
Loading…
x
Reference in New Issue
Block a user