diff --git a/manila/share/drivers/netapp/dataontap/client/client_cmode.py b/manila/share/drivers/netapp/dataontap/client/client_cmode.py
index 6522fec87b..62b11cee6e 100644
--- a/manila/share/drivers/netapp/dataontap/client/client_cmode.py
+++ b/manila/share/drivers/netapp/dataontap/client/client_cmode.py
@@ -608,6 +608,31 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
}
raise exception.NetAppException(msg % msg_args)
+ @na_utils.trace
+ def create_route(self, gateway, destination='0.0.0.0/0'):
+ try:
+ api_args = {
+ 'destination': destination,
+ 'gateway': gateway,
+ 'return-record': 'true',
+ }
+ self.send_request('net-routes-create', api_args)
+ except netapp_api.NaApiError as e:
+ p = re.compile('.*Duplicate route exists.*', re.IGNORECASE)
+ if (e.code == netapp_api.EAPIERROR and re.match(p, e.message)):
+ LOG.debug('Route to %(destination)s via gateway %(gateway)s '
+ 'exists.',
+ {'destination': destination, 'gateway': gateway})
+ else:
+ msg = _('Failed to create a route to %(destination)s via '
+ 'gateway %(gateway)s: %(err_msg)s')
+ msg_args = {
+ 'destination': destination,
+ 'gateway': gateway,
+ 'err_msg': e.message,
+ }
+ raise exception.NetAppException(msg % msg_args)
+
@na_utils.trace
def _ensure_broadcast_domain_for_port(self, node, port, mtu,
ipspace=DEFAULT_IPSPACE):
diff --git a/manila/share/drivers/netapp/dataontap/cluster_mode/lib_multi_svm.py b/manila/share/drivers/netapp/dataontap/cluster_mode/lib_multi_svm.py
index 29aa1456f8..365aeabe7b 100644
--- a/manila/share/drivers/netapp/dataontap/cluster_mode/lib_multi_svm.py
+++ b/manila/share/drivers/netapp/dataontap/cluster_mode/lib_multi_svm.py
@@ -181,6 +181,9 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
network_info,
ipspace_name)
+ self._create_vserver_routes(vserver_client,
+ network_info)
+
vserver_client.enable_nfs(
self.configuration.netapp_enabled_share_protocols)
@@ -254,6 +257,20 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
self._create_lif(vserver_client, vserver_name, ipspace_name,
node_name, lif_name, network_allocation)
+ @na_utils.trace
+ def _create_vserver_routes(self, vserver_client, network_info):
+ """Create Vserver route and set gateways."""
+ route_gateways = []
+ # NOTE(gouthamr): Use the gateway from the tenant subnet/s
+ # for the static routes. Do not configure a route for the admin
+ # subnet because fast path routing will work for incoming
+ # connections and there are no requirements for outgoing
+ # connections on the admin network yet.
+ for net_allocation in (network_info['network_allocations']):
+ if net_allocation['gateway'] not in route_gateways:
+ vserver_client.create_route(net_allocation['gateway'])
+ route_gateways.append(net_allocation['gateway'])
+
@na_utils.trace
def _get_node_data_port(self, node):
port_names = self._client.list_node_data_ports(node)
@@ -358,7 +375,6 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
@na_utils.trace
def _delete_vserver_vlans(self, network_interfaces_on_vlans):
"""Delete Vserver's VLAN configuration from ports"""
-
for interface in network_interfaces_on_vlans:
try:
home_port = interface['home-port']
diff --git a/manila/tests/share/drivers/netapp/dataontap/client/fakes.py b/manila/tests/share/drivers/netapp/dataontap/client/fakes.py
index 4da5042583..0054b5e6ad 100644
--- a/manila/tests/share/drivers/netapp/dataontap/client/fakes.py
+++ b/manila/tests/share/drivers/netapp/dataontap/client/fakes.py
@@ -75,6 +75,8 @@ VLAN = '1001'
VLAN_PORT = 'e0a-1001'
IP_ADDRESS = '10.10.10.10'
NETMASK = '255.255.255.0'
+GATEWAY = '10.10.10.1'
+SUBNET = '10.10.10.0/24'
NET_ALLOCATION_ID = 'fake_allocation_id'
LIF_NAME_TEMPLATE = 'os_%(net_allocation_id)s'
LIF_NAME = LIF_NAME_TEMPLATE % {'net_allocation_id': NET_ALLOCATION_ID}
@@ -2313,6 +2315,23 @@ PERF_OBJECT_INSTANCE_LIST_INFO_RESPONSE = etree.XML("""
""")
+NET_ROUTES_CREATE_RESPONSE = etree.XML("""
+
+
+
+ ipv4
+ %(subnet)s
+ %(gateway)s
+ 20
+ %(vserver)s
+
+
+ """ % {
+ 'gateway': GATEWAY,
+ 'vserver': VSERVER_NAME,
+ 'subnet': SUBNET,
+})
+
FAKE_VOL_XML = """
open123
online
diff --git a/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py b/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py
index 33728b7369..b54d7574d3 100644
--- a/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py
+++ b/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py
@@ -1045,6 +1045,57 @@ class NetAppClientCmodeTestCase(test.TestCase):
fake.PORT,
fake.VLAN)
+ def test_create_route(self):
+ api_response = netapp_api.NaElement(
+ fake.NET_ROUTES_CREATE_RESPONSE)
+ expected_api_args = {
+ 'destination': fake.SUBNET,
+ 'gateway': fake.GATEWAY,
+ 'return-record': 'true',
+ }
+ self.mock_object(
+ self.client, 'send_request', mock.Mock(return_value=api_response))
+
+ self.client.create_route(fake.GATEWAY, destination=fake.SUBNET)
+
+ self.client.send_request.assert_called_once_with(
+ 'net-routes-create', expected_api_args)
+
+ def test_create_route_duplicate(self):
+ self.mock_object(client_cmode.LOG, 'debug')
+ expected_api_args = {
+ 'destination': fake.SUBNET,
+ 'gateway': fake.GATEWAY,
+ 'return-record': 'true',
+ }
+ self.mock_object(
+ self.client, 'send_request',
+ mock.Mock(side_effect=self._mock_api_error(
+ code=netapp_api.EAPIERROR, message='Duplicate route exists.')))
+
+ self.client.create_route(fake.GATEWAY, destination=fake.SUBNET)
+
+ self.client.send_request.assert_called_once_with(
+ 'net-routes-create', expected_api_args)
+ self.assertEqual(1, client_cmode.LOG.debug.call_count)
+
+ def test_create_route_api_error(self):
+ expected_api_args = {
+ 'destination': fake.SUBNET,
+ 'gateway': fake.GATEWAY,
+ 'return-record': 'true',
+ }
+ self.mock_object(
+ self.client, 'send_request',
+ mock.Mock(side_effect=self._mock_api_error()))
+
+ self.assertRaises(exception.NetAppException,
+ self.client.create_route,
+ fake.GATEWAY, destination=fake.SUBNET)
+
+ self.client.send_request.assert_called_once_with(
+ 'net-routes-create', expected_api_args)
+
def test_ensure_broadcast_domain_for_port_domain_match(self):
port_info = {
diff --git a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_multi_svm.py b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_multi_svm.py
index b3699a3f6c..286947020b 100644
--- a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_multi_svm.py
+++ b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_multi_svm.py
@@ -313,20 +313,24 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock.Mock(return_value=fake.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_ipspace.assert_called_with(fake.NETWORK_INFO)
- self.library._client.create_vserver.assert_called_with(
+ self.library._create_ipspace.assert_called_once_with(fake.NETWORK_INFO)
+ self.library._client.create_vserver.assert_called_once_with(
vserver_name, fake.ROOT_VOLUME_AGGREGATE, fake.ROOT_VOLUME,
fake.AGGREGATES, fake.IPSPACE)
- self.library._get_api_client.assert_called_with(vserver=vserver_name)
- self.library._create_vserver_lifs.assert_called_with(
+ 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_with(
+ self.library._create_vserver_admin_lif.assert_called_once_with(
vserver_name, vserver_client, fake.NETWORK_INFO, fake.IPSPACE)
+ self.library._create_vserver_routes.assert_called_once_with(
+ vserver_client, fake.NETWORK_INFO)
vserver_client.enable_nfs.assert_called_once_with(versions)
- self.library._client.setup_security_services.assert_called_with(
+ self.library._client.setup_security_services.assert_called_once_with(
fake.NETWORK_INFO['security_services'], vserver_client,
vserver_name)
@@ -515,6 +519,22 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertFalse(self.library._create_lif.called)
+ @ddt.data(
+ fake.get_network_info(fake.USER_NETWORK_ALLOCATIONS,
+ fake.ADMIN_NETWORK_ALLOCATIONS),
+ fake.get_network_info(fake.USER_NETWORK_ALLOCATIONS_IPV6,
+ fake.ADMIN_NETWORK_ALLOCATIONS))
+ def test_create_vserver_routes(self, network_info):
+ expected_gateway = network_info['network_allocations'][0]['gateway']
+ vserver_client = mock.Mock()
+ self.mock_object(vserver_client, 'create_route')
+
+ retval = self.library._create_vserver_routes(
+ vserver_client, network_info)
+
+ self.assertIsNone(retval)
+ vserver_client.create_route.assert_called_once_with(expected_gateway)
+
def test_get_node_data_port(self):
self.mock_object(self.client,
diff --git a/manila/tests/share/drivers/netapp/dataontap/fakes.py b/manila/tests/share/drivers/netapp/dataontap/fakes.py
index 8b28eed1f0..1c57a176ba 100644
--- a/manila/tests/share/drivers/netapp/dataontap/fakes.py
+++ b/manila/tests/share/drivers/netapp/dataontap/fakes.py
@@ -230,6 +230,7 @@ USER_NETWORK_ALLOCATIONS = [
'network_type': 'vlan',
'label': 'user',
'mtu': MTU,
+ 'gateway': '10.10.10.1',
},
{
'id': '7eabdeed-bad2-46ea-bd0f-a33884c869e0',
@@ -239,6 +240,30 @@ USER_NETWORK_ALLOCATIONS = [
'network_type': 'vlan',
'label': 'user',
'mtu': MTU,
+ 'gateway': '10.10.10.1',
+ }
+]
+
+USER_NETWORK_ALLOCATIONS_IPV6 = [
+ {
+ 'id': '234dbb10-9a36-46f2-8d89-3d909830c356',
+ 'ip_address': 'fd68:1a09:66ab:8d51:0:10:0:1',
+ 'cidr': 'fd68:1a09:66ab:8d51::/64',
+ 'segmentation_id': '2000',
+ 'network_type': 'vlan',
+ 'label': 'user',
+ 'mtu': MTU,
+ 'gateway': 'fd68:1a09:66ab:8d51:0:0:0:1',
+ },
+ {
+ 'id': '6677deed-bad2-46ea-bd0f-a33884c869e0',
+ 'ip_address': 'fd68:1a09:66ab:8d51:0:10:0:2',
+ 'cidr': 'fd68:1a09:66ab:8d51::/64',
+ 'segmentation_id': '2000',
+ 'network_type': 'vlan',
+ 'label': 'user',
+ 'mtu': MTU,
+ 'gateway': 'fd68:1a09:66ab:8d51:0:0:0:1',
}
]
@@ -251,6 +276,7 @@ ADMIN_NETWORK_ALLOCATIONS = [
'network_type': 'flat',
'label': 'admin',
'mtu': MTU,
+ 'gateway': '10.10.20.1'
},
]
@@ -1199,3 +1225,11 @@ def get_config_cmode():
config.netapp_volume_snapshot_reserve_percent = 8
config.netapp_vserver = VSERVER1
return config
+
+
+def get_network_info(user_network_allocation, admin_network_allocation):
+ net_info = copy.deepcopy(NETWORK_INFO)
+ net_info['network_allocations'] = user_network_allocation
+ net_info['admin_network_allocations'] = admin_network_allocation
+
+ return net_info
diff --git a/releasenotes/notes/bug-1698258-netapp-fix-tenant-network-gateways-85935582e89a72a0.yaml b/releasenotes/notes/bug-1698258-netapp-fix-tenant-network-gateways-85935582e89a72a0.yaml
new file mode 100644
index 0000000000..289faa08da
--- /dev/null
+++ b/releasenotes/notes/bug-1698258-netapp-fix-tenant-network-gateways-85935582e89a72a0.yaml
@@ -0,0 +1,7 @@
+---
+fixes:
+ - The NetApp DHSS=True driver now creates static routes with the gateway
+ specified on the tenant networks. Potential beneficiaries of this bug-fix
+ are deployers/users whose CIFS security service (e.g. Active Directory)
+ is not part of the tenant network, but a route exists via the tenant
+ network gateway.