NetApp cDOT: Add gateway information to create static routes
Add tenant routes/gateway to Vservers created by the driver. Change-Id: Id33c0e13d265d50f74f86ab8fb2c533eefa4b783 Closes-Bug: #1698258 Closes-Bug: #1612655 (cherry picked from commitf88df34e31
) (cherry picked from commit11c8145b96
)
This commit is contained in:
parent
6b14188ac6
commit
546f3c8478
@ -552,6 +552,31 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
}
|
}
|
||||||
raise exception.NetAppException(msg % msg_args)
|
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
|
@na_utils.trace
|
||||||
def _ensure_broadcast_domain_for_port(self, node, port, mtu,
|
def _ensure_broadcast_domain_for_port(self, node, port, mtu,
|
||||||
ipspace=DEFAULT_IPSPACE):
|
ipspace=DEFAULT_IPSPACE):
|
||||||
|
@ -173,6 +173,9 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
|
|||||||
network_info,
|
network_info,
|
||||||
ipspace_name)
|
ipspace_name)
|
||||||
|
|
||||||
|
self._create_vserver_routes(vserver_client,
|
||||||
|
network_info)
|
||||||
|
|
||||||
vserver_client.enable_nfs(
|
vserver_client.enable_nfs(
|
||||||
self.configuration.netapp_enabled_share_protocols)
|
self.configuration.netapp_enabled_share_protocols)
|
||||||
|
|
||||||
@ -246,6 +249,20 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
|
|||||||
self._create_lif(vserver_client, vserver_name, ipspace_name,
|
self._create_lif(vserver_client, vserver_name, ipspace_name,
|
||||||
node_name, lif_name, network_allocation)
|
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
|
@na_utils.trace
|
||||||
def _get_node_data_port(self, node):
|
def _get_node_data_port(self, node):
|
||||||
port_names = self._client.list_node_data_ports(node)
|
port_names = self._client.list_node_data_ports(node)
|
||||||
@ -350,7 +367,6 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
|
|||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def _delete_vserver_vlans(self, network_interfaces_on_vlans):
|
def _delete_vserver_vlans(self, network_interfaces_on_vlans):
|
||||||
"""Delete Vserver's VLAN configuration from ports"""
|
"""Delete Vserver's VLAN configuration from ports"""
|
||||||
|
|
||||||
for interface in network_interfaces_on_vlans:
|
for interface in network_interfaces_on_vlans:
|
||||||
try:
|
try:
|
||||||
home_port = interface['home-port']
|
home_port = interface['home-port']
|
||||||
|
@ -73,6 +73,8 @@ VLAN = '1001'
|
|||||||
VLAN_PORT = 'e0a-1001'
|
VLAN_PORT = 'e0a-1001'
|
||||||
IP_ADDRESS = '10.10.10.10'
|
IP_ADDRESS = '10.10.10.10'
|
||||||
NETMASK = '255.255.255.0'
|
NETMASK = '255.255.255.0'
|
||||||
|
GATEWAY = '10.10.10.1'
|
||||||
|
SUBNET = '10.10.10.0/24'
|
||||||
NET_ALLOCATION_ID = 'fake_allocation_id'
|
NET_ALLOCATION_ID = 'fake_allocation_id'
|
||||||
LIF_NAME_TEMPLATE = 'os_%(net_allocation_id)s'
|
LIF_NAME_TEMPLATE = 'os_%(net_allocation_id)s'
|
||||||
LIF_NAME = LIF_NAME_TEMPLATE % {'net_allocation_id': NET_ALLOCATION_ID}
|
LIF_NAME = LIF_NAME_TEMPLATE % {'net_allocation_id': NET_ALLOCATION_ID}
|
||||||
@ -2079,6 +2081,23 @@ SNAPMIRROR_INITIALIZE_RESULT = etree.XML("""
|
|||||||
</results>
|
</results>
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
NET_ROUTES_CREATE_RESPONSE = etree.XML("""
|
||||||
|
<results status="passed">
|
||||||
|
<result>
|
||||||
|
<net-vs-routes-info>
|
||||||
|
<address-family>ipv4</address-family>
|
||||||
|
<destination>%(subnet)s</destination>
|
||||||
|
<gateway>%(gateway)s</gateway>
|
||||||
|
<metric>20</metric>
|
||||||
|
<vserver>%(vserver)s</vserver>
|
||||||
|
</net-vs-routes-info>
|
||||||
|
</result>
|
||||||
|
</results>""" % {
|
||||||
|
'gateway': GATEWAY,
|
||||||
|
'vserver': VSERVER_NAME,
|
||||||
|
'subnet': SUBNET,
|
||||||
|
})
|
||||||
|
|
||||||
FAKE_VOL_XML = """<volume-info xmlns='http://www.netapp.com/filer/admin'>
|
FAKE_VOL_XML = """<volume-info xmlns='http://www.netapp.com/filer/admin'>
|
||||||
<name>open123</name>
|
<name>open123</name>
|
||||||
<state>online</state>
|
<state>online</state>
|
||||||
|
@ -981,6 +981,57 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
fake.PORT,
|
fake.PORT,
|
||||||
fake.VLAN)
|
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):
|
def test_ensure_broadcast_domain_for_port_domain_match(self):
|
||||||
|
|
||||||
port_info = {
|
port_info = {
|
||||||
|
@ -296,20 +296,24 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
mock.Mock(return_value=fake.IPSPACE))
|
mock.Mock(return_value=fake.IPSPACE))
|
||||||
self.mock_object(self.library, '_create_vserver_lifs')
|
self.mock_object(self.library, '_create_vserver_lifs')
|
||||||
self.mock_object(self.library, '_create_vserver_admin_lif')
|
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)
|
||||||
|
|
||||||
self.library._create_ipspace.assert_called_with(fake.NETWORK_INFO)
|
self.library._create_ipspace.assert_called_once_with(fake.NETWORK_INFO)
|
||||||
self.library._client.create_vserver.assert_called_with(
|
self.library._client.create_vserver.assert_called_once_with(
|
||||||
vserver_name, fake.ROOT_VOLUME_AGGREGATE, fake.ROOT_VOLUME,
|
vserver_name, fake.ROOT_VOLUME_AGGREGATE, fake.ROOT_VOLUME,
|
||||||
fake.AGGREGATES, fake.IPSPACE)
|
fake.AGGREGATES, fake.IPSPACE)
|
||||||
self.library._get_api_client.assert_called_with(vserver=vserver_name)
|
self.library._get_api_client.assert_called_once_with(
|
||||||
self.library._create_vserver_lifs.assert_called_with(
|
vserver=vserver_name)
|
||||||
|
self.library._create_vserver_lifs.assert_called_once_with(
|
||||||
vserver_name, vserver_client, fake.NETWORK_INFO, fake.IPSPACE)
|
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)
|
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)
|
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,
|
fake.NETWORK_INFO['security_services'], vserver_client,
|
||||||
vserver_name)
|
vserver_name)
|
||||||
|
|
||||||
@ -509,6 +513,22 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
|
|
||||||
self.assertFalse(self.library._create_lif.called)
|
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):
|
def test_get_node_data_port(self):
|
||||||
|
|
||||||
self.mock_object(self.client,
|
self.mock_object(self.client,
|
||||||
|
@ -227,6 +227,7 @@ USER_NETWORK_ALLOCATIONS = [
|
|||||||
'network_type': 'vlan',
|
'network_type': 'vlan',
|
||||||
'label': 'user',
|
'label': 'user',
|
||||||
'mtu': MTU,
|
'mtu': MTU,
|
||||||
|
'gateway': '10.10.10.1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': '7eabdeed-bad2-46ea-bd0f-a33884c869e0',
|
'id': '7eabdeed-bad2-46ea-bd0f-a33884c869e0',
|
||||||
@ -236,6 +237,30 @@ USER_NETWORK_ALLOCATIONS = [
|
|||||||
'network_type': 'vlan',
|
'network_type': 'vlan',
|
||||||
'label': 'user',
|
'label': 'user',
|
||||||
'mtu': MTU,
|
'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',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -248,6 +273,7 @@ ADMIN_NETWORK_ALLOCATIONS = [
|
|||||||
'network_type': 'flat',
|
'network_type': 'flat',
|
||||||
'label': 'admin',
|
'label': 'admin',
|
||||||
'mtu': MTU,
|
'mtu': MTU,
|
||||||
|
'gateway': '10.10.20.1'
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -611,3 +637,11 @@ def get_config_cmode():
|
|||||||
config.netapp_volume_snapshot_reserve_percent = 8
|
config.netapp_volume_snapshot_reserve_percent = 8
|
||||||
config.netapp_vserver = VSERVER1
|
config.netapp_vserver = VSERVER1
|
||||||
return config
|
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
|
||||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user