Update default gateway in the fip namespace after subnet-update
The gateway does not get updated after a subnet-update --gateway
because the router is already subscribed to the fip namespace.
On a restart, the fip namespace has no subscribers, which causes
the gateway to be created. You shouldn't have to restart the L3
agent in order to update the gateway.
In order for the default gateway in the fip namespace to be updated
after a subnet-update, the underlying code which sets the default
gateway needs to be called even if the router is already subscribed to
the fip namespace.
The bit of code which is responsible for updating the default route has
been refactored into its own method, update_gateway_port, which is then
called after a 'subnet-update --gateway ...'. If the gateway has
actually changed, the default route in the fip namespace will be
updated.
Conflicts:
neutron/tests/unit/agent/l3/test_dvr_fip_ns.py
Change-Id: I0552c4a50ae09a6606e88395f7545179d02733dd
Closes-Bug: #1512858
(cherry picked from commit a0f7153f95
)
This commit is contained in:
parent
4b1d8dca9b
commit
a21da84e16
|
@ -111,22 +111,7 @@ class FipNamespace(namespaces.Namespace):
|
|||
self.driver.init_l3(interface_name, ip_cidrs, namespace=ns_name,
|
||||
clean_connections=True)
|
||||
|
||||
for fixed_ip in ex_gw_port['fixed_ips']:
|
||||
ip_lib.send_ip_addr_adv_notif(ns_name,
|
||||
interface_name,
|
||||
fixed_ip['ip_address'],
|
||||
self.agent_conf)
|
||||
|
||||
for subnet in ex_gw_port['subnets']:
|
||||
gw_ip = subnet.get('gateway_ip')
|
||||
if gw_ip:
|
||||
is_gateway_not_in_subnet = not ipam_utils.check_subnet_ip(
|
||||
subnet.get('cidr'), gw_ip)
|
||||
ipd = ip_lib.IPDevice(interface_name,
|
||||
namespace=ns_name)
|
||||
if is_gateway_not_in_subnet:
|
||||
ipd.route.add_route(gw_ip, scope='link')
|
||||
ipd.route.add_gateway(gw_ip)
|
||||
self.update_gateway_port(ex_gw_port)
|
||||
|
||||
cmd = ['sysctl', '-w', 'net.ipv4.conf.%s.proxy_arp=1' % interface_name]
|
||||
# TODO(Carl) mlavelle's work has self.ip_wrapper
|
||||
|
@ -194,13 +179,53 @@ class FipNamespace(namespaces.Namespace):
|
|||
Request port creation from Plugin then creates
|
||||
Floating IP namespace and adds gateway port.
|
||||
"""
|
||||
self.agent_gateway_port = agent_gateway_port
|
||||
|
||||
self.create()
|
||||
|
||||
iface_name = self.get_ext_device_name(agent_gateway_port['id'])
|
||||
self._gateway_added(agent_gateway_port, iface_name)
|
||||
|
||||
def _check_for_gateway_ip_change(self, new_agent_gateway_port):
|
||||
|
||||
def get_gateway_ips(gateway_port):
|
||||
gw_ips = {}
|
||||
if gateway_port:
|
||||
for subnet in gateway_port.get('subnets', []):
|
||||
gateway_ip = subnet.get('gateway_ip', None)
|
||||
if gateway_ip:
|
||||
ip_version = ip_lib.get_ip_version(gateway_ip)
|
||||
gw_ips[ip_version] = gateway_ip
|
||||
return gw_ips
|
||||
|
||||
new_gw_ips = get_gateway_ips(new_agent_gateway_port)
|
||||
old_gw_ips = get_gateway_ips(self.agent_gateway_port)
|
||||
|
||||
return new_gw_ips != old_gw_ips
|
||||
|
||||
def update_gateway_port(self, agent_gateway_port):
|
||||
gateway_ip_not_changed = self.agent_gateway_port and (
|
||||
not self._check_for_gateway_ip_change(agent_gateway_port))
|
||||
self.agent_gateway_port = agent_gateway_port
|
||||
if gateway_ip_not_changed:
|
||||
return
|
||||
|
||||
ns_name = self.get_name()
|
||||
interface_name = self.get_ext_device_name(agent_gateway_port['id'])
|
||||
for fixed_ip in agent_gateway_port['fixed_ips']:
|
||||
ip_lib.send_ip_addr_adv_notif(ns_name,
|
||||
interface_name,
|
||||
fixed_ip['ip_address'],
|
||||
self.agent_conf)
|
||||
|
||||
ipd = ip_lib.IPDevice(interface_name, namespace=ns_name)
|
||||
for subnet in agent_gateway_port['subnets']:
|
||||
gw_ip = subnet.get('gateway_ip')
|
||||
if gw_ip:
|
||||
is_gateway_not_in_subnet = not ipam_utils.check_subnet_ip(
|
||||
subnet.get('cidr'), gw_ip)
|
||||
if is_gateway_not_in_subnet:
|
||||
ipd.route.add_route(gw_ip, scope='link')
|
||||
ipd.route.add_gateway(gw_ip)
|
||||
|
||||
def _internal_ns_interface_added(self, ip_cidr,
|
||||
interface_name, ns_name):
|
||||
ip_wrapper = ip_lib.IPWrapper(namespace=ns_name)
|
||||
|
|
|
@ -461,11 +461,14 @@ class DvrLocalRouter(dvr_router_base.DvrRouterBase):
|
|||
LOG.error(_LE("No FloatingIP agent gateway port "
|
||||
"returned from server for 'network-id': "
|
||||
"%s"), ex_gw_port['network_id'])
|
||||
if is_first and fip_agent_port:
|
||||
if fip_agent_port:
|
||||
if 'subnets' not in fip_agent_port:
|
||||
LOG.error(_LE('Missing subnet/agent_gateway_port'))
|
||||
else:
|
||||
self.fip_ns.create_gateway_port(fip_agent_port)
|
||||
if is_first:
|
||||
self.fip_ns.create_gateway_port(fip_agent_port)
|
||||
else:
|
||||
self.fip_ns.update_gateway_port(fip_agent_port)
|
||||
|
||||
if (self.fip_ns.agent_gateway_port and
|
||||
(self.dist_fip_count == 0 or is_first)):
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import mock
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
|
@ -69,65 +70,94 @@ class TestDvrFipNs(base.BaseTestCase):
|
|||
self.assertNotIn('20.0.0.30', self.fip_ns._rule_priorities.allocations)
|
||||
self.assertIn(pr, self.fip_ns._rule_priorities.pool)
|
||||
|
||||
@mock.patch.object(ip_lib, 'IPWrapper')
|
||||
@mock.patch.object(ip_lib, 'IPDevice')
|
||||
@mock.patch.object(ip_lib, 'send_ip_addr_adv_notif')
|
||||
@mock.patch.object(ip_lib, 'device_exists')
|
||||
def test_gateway_added(self, device_exists, send_adv_notif,
|
||||
IPDevice, IPWrapper):
|
||||
subnet_id = _uuid()
|
||||
def _get_agent_gw_port(self):
|
||||
v4_subnet_id = _uuid()
|
||||
v6_subnet_id = _uuid()
|
||||
agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30',
|
||||
'prefixlen': 24,
|
||||
'subnet_id': subnet_id}],
|
||||
'subnets': [{'id': subnet_id,
|
||||
'subnet_id': v4_subnet_id},
|
||||
{'ip_address': 'cafe:dead:beef::3',
|
||||
'prefixlen': 64,
|
||||
'subnet_id': v6_subnet_id}],
|
||||
'subnets': [{'id': v4_subnet_id,
|
||||
'cidr': '20.0.0.0/24',
|
||||
'gateway_ip': '20.0.0.1'}],
|
||||
'gateway_ip': '20.0.0.1'},
|
||||
{'id': v6_subnet_id,
|
||||
'cidr': 'cafe:dead:beef::/64',
|
||||
'gateway_ip': 'cafe:dead:beef::1'}],
|
||||
'id': _uuid(),
|
||||
'network_id': self.net_id,
|
||||
'mac_address': 'ca:fe:de:ad:be:ef'}
|
||||
|
||||
device_exists.return_value = False
|
||||
self.fip_ns._gateway_added(agent_gw_port,
|
||||
mock.sentinel.interface_name)
|
||||
self.assertEqual(self.driver.plug.call_count, 1)
|
||||
self.assertEqual(self.driver.init_l3.call_count, 1)
|
||||
send_adv_notif.assert_called_once_with(self.fip_ns.get_name(),
|
||||
mock.sentinel.interface_name,
|
||||
'20.0.0.30',
|
||||
mock.ANY)
|
||||
return agent_gw_port
|
||||
|
||||
@mock.patch.object(ip_lib, 'IPWrapper')
|
||||
@mock.patch.object(ip_lib, 'IPDevice')
|
||||
@mock.patch.object(ip_lib, 'send_ip_addr_adv_notif')
|
||||
@mock.patch.object(ip_lib, 'device_exists')
|
||||
def test_gateway_outside_subnet_added(self, device_exists, send_adv_notif,
|
||||
IPDevice, IPWrapper):
|
||||
device = mock.Mock()
|
||||
IPDevice.return_value = device
|
||||
|
||||
subnet_id = _uuid()
|
||||
agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30',
|
||||
'prefixlen': 24,
|
||||
'subnet_id': subnet_id}],
|
||||
'subnets': [{'id': subnet_id,
|
||||
'cidr': '20.0.0.0/24',
|
||||
'gateway_ip': '20.0.1.1'}],
|
||||
'id': _uuid(),
|
||||
'network_id': self.net_id,
|
||||
'mac_address': 'ca:fe:de:ad:be:ef'}
|
||||
def test_gateway_added(self, device_exists, ip_wrapper):
|
||||
agent_gw_port = self._get_agent_gw_port()
|
||||
|
||||
device_exists.return_value = False
|
||||
self.fip_ns.update_gateway_port = mock.Mock()
|
||||
self.fip_ns._gateway_added(agent_gw_port,
|
||||
mock.sentinel.interface_name)
|
||||
self.assertEqual(1, self.driver.plug.call_count)
|
||||
self.assertEqual(1, self.driver.init_l3.call_count)
|
||||
send_adv_notif.assert_called_once_with(self.fip_ns.get_name(),
|
||||
mock.sentinel.interface_name,
|
||||
'20.0.0.30',
|
||||
mock.ANY)
|
||||
device.route.add_route.assert_called_once_with('20.0.1.1',
|
||||
scope='link')
|
||||
device.route.add_gateway.assert_called_once_with('20.0.1.1')
|
||||
self.fip_ns.update_gateway_port.assert_called_once_with(agent_gw_port)
|
||||
|
||||
@mock.patch.object(ip_lib, 'IPDevice')
|
||||
@mock.patch.object(ip_lib, 'send_ip_addr_adv_notif')
|
||||
def test_update_gateway_port(self, send_adv_notif, IPDevice):
|
||||
self.fip_ns._check_for_gateway_ip_change = mock.Mock(return_value=True)
|
||||
self.fip_ns.agent_gateway_port = None
|
||||
agent_gw_port = self._get_agent_gw_port()
|
||||
self.fip_ns.update_gateway_port(agent_gw_port)
|
||||
expected = [
|
||||
mock.call(self.fip_ns.get_name(),
|
||||
self.fip_ns.get_ext_device_name(agent_gw_port['id']),
|
||||
agent_gw_port['fixed_ips'][0]['ip_address'],
|
||||
mock.ANY),
|
||||
mock.call(self.fip_ns.get_name(),
|
||||
self.fip_ns.get_ext_device_name(agent_gw_port['id']),
|
||||
agent_gw_port['fixed_ips'][1]['ip_address'],
|
||||
mock.ANY)]
|
||||
send_adv_notif.assert_has_calls(expected)
|
||||
gw_ipv4 = agent_gw_port['subnets'][0]['gateway_ip']
|
||||
gw_ipv6 = agent_gw_port['subnets'][1]['gateway_ip']
|
||||
expected = [mock.call(gw_ipv4), mock.call(gw_ipv6)]
|
||||
IPDevice().route.add_gateway.assert_has_calls(expected)
|
||||
|
||||
@mock.patch.object(ip_lib, 'IPDevice')
|
||||
@mock.patch.object(ip_lib, 'send_ip_addr_adv_notif')
|
||||
def test_update_gateway_port_gateway_outside_subnet_added(
|
||||
self, send_adv_notif, IPDevice):
|
||||
self.fip_ns.agent_gateway_port = None
|
||||
agent_gw_port = self._get_agent_gw_port()
|
||||
agent_gw_port['subnets'][0]['gateway_ip'] = '20.0.1.1'
|
||||
|
||||
self.fip_ns.update_gateway_port(agent_gw_port)
|
||||
|
||||
IPDevice().route.add_route.assert_called_once_with('20.0.1.1',
|
||||
scope='link')
|
||||
|
||||
def test_check_gateway_ip_changed_no_change(self):
|
||||
agent_gw_port = self._get_agent_gw_port()
|
||||
self.fip_ns.agent_gateway_port = copy.deepcopy(agent_gw_port)
|
||||
agent_gw_port['mac_address'] = 'aa:bb:cc:dd:ee:ff'
|
||||
self.assertFalse(self.fip_ns._check_for_gateway_ip_change(
|
||||
agent_gw_port))
|
||||
|
||||
def test_check_gateway_ip_changed_v4(self):
|
||||
agent_gw_port = self._get_agent_gw_port()
|
||||
self.fip_ns.agent_gateway_port = copy.deepcopy(agent_gw_port)
|
||||
agent_gw_port['subnets'][0]['gateway_ip'] = '20.0.0.2'
|
||||
self.assertTrue(self.fip_ns._check_for_gateway_ip_change(
|
||||
agent_gw_port))
|
||||
|
||||
def test_check_gateway_ip_changed_v6(self):
|
||||
agent_gw_port = self._get_agent_gw_port()
|
||||
self.fip_ns.agent_gateway_port = copy.deepcopy(agent_gw_port)
|
||||
agent_gw_port['subnets'][1]['gateway_ip'] = 'cafe:dead:beef::2'
|
||||
self.assertTrue(self.fip_ns._check_for_gateway_ip_change(
|
||||
agent_gw_port))
|
||||
|
||||
@mock.patch.object(iptables_manager, 'IptablesManager')
|
||||
@mock.patch.object(utils, 'execute')
|
||||
|
|
|
@ -157,6 +157,19 @@ class TestDvrRouterOperations(base.BaseTestCase):
|
|||
mock.Mock(),
|
||||
**kwargs)
|
||||
|
||||
def test_create_dvr_fip_interfaces_update(self):
|
||||
ri = self._create_router()
|
||||
fip_agent_port = {'subnets': []}
|
||||
ri.get_floating_agent_gw_interface = mock.Mock(
|
||||
return_value=fip_agent_port)
|
||||
ri.get_floating_ips = mock.Mock(return_value=True)
|
||||
ri.fip_ns = mock.Mock()
|
||||
ri.fip_ns.subscribe.return_value = False
|
||||
ex_gw_port = {'network_id': 'fake_net_id'}
|
||||
ri.create_dvr_fip_interfaces(ex_gw_port)
|
||||
ri.fip_ns.update_gateway_port.assert_called_once_with(
|
||||
fip_agent_port)
|
||||
|
||||
def test_get_floating_ips_dvr(self):
|
||||
router = mock.MagicMock()
|
||||
router.get.return_value = [{'host': HOSTNAME},
|
||||
|
|
Loading…
Reference in New Issue