Fix member address selection in lbaas driver

Currently when Senlin adds a member to load balancer's pool, it just uses
the first address of the node. In a dual stack network, if the first
address does not match with the subnet ip version, the member will become
unaccessible from the load balancer. This patch ensures the right address
will be picked according to the subnet ip version.

Change-Id: I0322184dfdb46a7560c20e85452d84d2ffb83a1c
Closes-Bug: #1813089
This commit is contained in:
Hang Yang 2019-01-23 15:25:12 -08:00
parent 20e40cb4fe
commit ec3c42a2ad
2 changed files with 57 additions and 12 deletions

View File

@ -291,8 +291,17 @@ class LoadBalancerDriver(base.DriverBase):
LOG.error('Node is not in subnet %(subnet)s', {'subnet': subnet})
return None
# Use the first IP address if more than one are found in target network
address = addresses[net_name][0]['addr']
# Use the first IP address that match with the subnet ip_version
# if more than one are found in target network
address = None
for ip in addresses[net_name]:
if ip['version'] == subnet_obj.ip_version:
address = ip['addr']
break
if not address:
LOG.error("Node does not match with subnet's (%s) ip version (%s)"
% (subnet, subnet_obj.ip_version))
return None
try:
# FIXME(Yanyan Hu): Currently, Neutron lbaasv2 service can not
# handle concurrent lb member operations well: new member creation

View File

@ -445,6 +445,7 @@ class TestOctaviaLBaaSDriver(base.SenlinTestCase):
port = '80'
subnet = 'subnet'
subnet_obj = mock.Mock(id='SUBNET_ID', network_id='NETWORK_ID')
subnet_obj.ip_version = '4'
subnet_obj.name = 'subnet'
network_obj = mock.Mock(id='NETWORK_ID')
network_obj.name = 'network1'
@ -452,8 +453,9 @@ class TestOctaviaLBaaSDriver(base.SenlinTestCase):
node_detail = {
'name': 'node-01',
'addresses': {
'network1': [{'addr': 'ipaddr_net1'}],
'network2': [{'addr': 'ipaddr_net2'}]
'network1': [{'addr': 'ipaddr1_net1', 'version': '6'},
{'addr': 'ipaddr2_net1', 'version': '4'}],
'network2': [{'addr': 'ipaddr_net2', 'version': '4'}]
}
}
mock_load.return_value = node
@ -469,8 +471,9 @@ class TestOctaviaLBaaSDriver(base.SenlinTestCase):
self.assertEqual('MEMBER_ID', res)
self.nc.subnet_get.assert_called_once_with(subnet)
self.nc.network_get.assert_called_once_with('NETWORK_ID')
# Make sure the ip matches with subnet ip_version
self.oc.pool_member_create.assert_called_once_with(
pool_id, 'ipaddr_net1', port, 'SUBNET_ID')
pool_id, 'ipaddr2_net1', port, 'SUBNET_ID')
self.lb_driver._wait_for_lb_ready.assert_has_calls(
[mock.call('LB_ID'), mock.call('LB_ID')])
mock_load.assert_called_once_with(fake_context, db_node=node)
@ -505,13 +508,14 @@ class TestOctaviaLBaaSDriver(base.SenlinTestCase):
node = mock.Mock()
subnet_obj = mock.Mock(id='SUBNET_ID', network_id='NETWORK_ID')
subnet_obj.name = 'subnet'
subnet_obj.ip_version = '4'
network_obj = mock.Mock(id='NETWORK_ID')
network_obj.name = 'network1'
node_detail = {
'name': 'node-01',
'addresses': {
'network1': [{'addr': 'ipaddr_net1'}],
'network2': [{'addr': 'ipaddr_net2'}]
'network1': [{'addr': 'ipaddr_net1', 'version': '4'}],
'network2': [{'addr': 'ipaddr_net2', 'version': '4'}]
}
}
mock_load.return_value = node
@ -536,13 +540,14 @@ class TestOctaviaLBaaSDriver(base.SenlinTestCase):
node = mock.Mock()
subnet_obj = mock.Mock(id='SUBNET_ID', network_id='NETWORK_ID')
subnet_obj.name = 'subnet'
subnet_obj.ip_version = '4'
network_obj = mock.Mock(id='NETWORK_ID')
network_obj.name = 'network1'
node_detail = {
'name': 'node-01',
'addresses': {
'network1': [{'addr': 'ipaddr_net1'}],
'network2': [{'addr': 'ipaddr_net2'}]
'network1': [{'addr': 'ipaddr_net1', 'version': '4'}],
'network2': [{'addr': 'ipaddr_net2', 'version': '4'}]
}
}
mock_load.return_value = node
@ -561,17 +566,48 @@ class TestOctaviaLBaaSDriver(base.SenlinTestCase):
@mock.patch.object(nodem.Node, 'load')
@mock.patch.object(oslo_context, 'get_current')
def test_member_add_wait_for_lb_timeout(self, mock_get_current, mock_load):
def test_member_add_ip_version_match_failed(self, mock_get_current,
mock_load):
node = mock.Mock()
subnet_obj = mock.Mock(id='SUBNET_ID', network_id='NETWORK_ID')
subnet_obj.name = 'subnet'
subnet_obj.ip_version = '4'
network_obj = mock.Mock(id='NETWORK_ID')
network_obj.name = 'network1'
node_detail = {
'name': 'node-01',
'addresses': {
'network1': [{'addr': 'ipaddr_net1'}],
'network2': [{'addr': 'ipaddr_net2'}]
'network1': [{'addr': 'ipaddr_net1', 'version': '6'}],
'network2': [{'addr': 'ipaddr_net2', 'version': '6'}]
}
}
mock_load.return_value = node
node.get_details.return_value = node_detail
# Node does not match with subnet ip_version
self.lb_driver._wait_for_lb_ready = mock.Mock()
self.lb_driver._wait_for_lb_ready.return_value = True
self.nc.subnet_get.return_value = subnet_obj
self.nc.network_get.return_value = network_obj
self.oc.pool_member_create = mock.Mock(id='MEMBER_ID')
res = self.lb_driver.member_add(node, 'LB_ID', 'POOL_ID', 80,
'subnet')
self.assertIsNone(res)
@mock.patch.object(nodem.Node, 'load')
@mock.patch.object(oslo_context, 'get_current')
def test_member_add_wait_for_lb_timeout(self, mock_get_current, mock_load):
node = mock.Mock()
subnet_obj = mock.Mock(id='SUBNET_ID', network_id='NETWORK_ID')
subnet_obj.name = 'subnet'
subnet_obj.ip_version = '4'
network_obj = mock.Mock(id='NETWORK_ID')
network_obj.name = 'network1'
node_detail = {
'name': 'node-01',
'addresses': {
'network1': [{'addr': 'ipaddr_net1', 'version': '4'}],
'network2': [{'addr': 'ipaddr_net2', 'version': '4'}]
}
}
mock_load.return_value = node