From: fumihiko kakuma Date: Tue, 18 Mar 2014 02:36:29 +0000 (+0900) Subject: OFA agent: use hexadecimal IP address in tunnel port name X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=c506ebf317e64766369c0bfa97b6af182656ac4c;p=openstack-build%2Fneutron-build.git OFA agent: use hexadecimal IP address in tunnel port name The remote IP address is used to form the tunnel port name With some OVS/Linux combinations there is a 15 character limit on port names, so a name like 'gre-192.168.10.10' does not work. This fix uses the shorter hexadecimal representation of the IP address instead. This problem was reported and fixed for OVS agent. Closes-Bug: 1294474 Change-Id: Ieab1eb8a5c430f69aff61a317cc28f23ba95ad2d --- diff --git a/neutron/plugins/ofagent/agent/ofa_neutron_agent.py b/neutron/plugins/ofagent/agent/ofa_neutron_agent.py index 7c5beee17..0158e87e9 100644 --- a/neutron/plugins/ofagent/agent/ofa_neutron_agent.py +++ b/neutron/plugins/ofagent/agent/ofa_neutron_agent.py @@ -19,6 +19,7 @@ import time +import netaddr from oslo.config import cfg from ryu.app.ofctl import api as ryu_api from ryu.base import app_manager @@ -297,6 +298,14 @@ class OFANeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin): except Exception: LOG.exception(_("Failed reporting state!")) + def _create_tunnel_port_name(self, tunnel_type, ip_address): + try: + ip_hex = '%08x' % netaddr.IPAddress(ip_address, version=4) + return '%s-%s' % (tunnel_type, ip_hex) + except Exception: + LOG.warn(_("Unable to create tunnel port. Invalid remote IP: %s"), + ip_address) + def ryu_send_msg(self, msg): result = ryu_api.send_msg(self.ryuapp, msg) LOG.info(_("ryu send_msg() result: %s"), result) @@ -376,7 +385,6 @@ class OFANeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin): if not self.enable_tunneling: return tunnel_ip = kwargs.get('tunnel_ip') - tunnel_id = kwargs.get('tunnel_id', tunnel_ip) tunnel_type = kwargs.get('tunnel_type') if not tunnel_type: LOG.error(_("No tunnel_type specified, cannot create tunnels")) @@ -386,7 +394,9 @@ class OFANeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin): return if tunnel_ip == self.local_ip: return - tun_name = '%s-%s' % (tunnel_type, tunnel_id) + tun_name = self._create_tunnel_port_name(tunnel_type, tunnel_ip) + if not tun_name: + return self.setup_tunnel_port(tun_name, tunnel_ip, tunnel_type) def create_rpc_dispatcher(self): @@ -1050,19 +1060,6 @@ class OFANeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin): self.ryu_send_msg(msg) return ofport - def cleanup_tunnel_port(self, tun_ofport, tunnel_type): - # Check if this tunnel port is still used - for lvm in self.local_vlan_map.values(): - if tun_ofport in lvm.tun_ofports: - break - # If not, remove it - else: - for remote_ip, ofport in self.tun_br_ofports[tunnel_type].items(): - if ofport == tun_ofport: - port_name = '%s-%s' % (tunnel_type, remote_ip) - self.tun_br.delete_port(port_name) - self.tun_br_ofports[tunnel_type].pop(remote_ip, None) - def treat_devices_added(self, devices): resync = False self.sg_agent.prepare_devices_filter(devices) @@ -1214,8 +1211,10 @@ class OFANeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin): tunnels = details['tunnels'] for tunnel in tunnels: if self.local_ip != tunnel['ip_address']: - tunnel_id = tunnel.get('id', tunnel['ip_address']) - tun_name = '%s-%s' % (tunnel_type, tunnel_id) + tun_name = self._create_tunnel_port_name( + tunnel_type, tunnel['ip_address']) + if not tun_name: + continue self.setup_tunnel_port(tun_name, tunnel['ip_address'], tunnel_type) diff --git a/neutron/tests/unit/ofagent/test_ofa_neutron_agent.py b/neutron/tests/unit/ofagent/test_ofa_neutron_agent.py index 71afcd213..34ade5994 100644 --- a/neutron/tests/unit/ofagent/test_ofa_neutron_agent.py +++ b/neutron/tests/unit/ofagent/test_ofa_neutron_agent.py @@ -19,6 +19,7 @@ import contextlib import mock +import netaddr from oslo.config import cfg import testtools @@ -42,11 +43,11 @@ class OFAAgentTestCase(base.BaseTestCase): def setUp(self): super(OFAAgentTestCase, self).setUp() + self.fake_oflib_of = fake_oflib.patch_fake_oflib_of().start() + self.mod_agent = importutils.import_module(self._AGENT_NAME) cfg.CONF.set_default('firewall_driver', 'neutron.agent.firewall.NoopFirewallDriver', group='SECURITYGROUP') - self.fake_oflib_of = fake_oflib.patch_fake_oflib_of().start() - self.mod_agent = importutils.import_module(self._AGENT_NAME) self.ryuapp = mock.Mock() cfg.CONF.register_cli_opts([ cfg.StrOpt('ofp-listen-host', default='', @@ -375,17 +376,14 @@ class TestOFANeutronAgent(OFAAgentTestCase): ) def test_network_delete(self): - with contextlib.nested( - mock.patch.object(self.agent, "reclaim_local_vlan"), - mock.patch.object(self.agent.tun_br, "cleanup_tunnel_port") - ) as (recl_fn, clean_tun_fn): + with mock.patch.object(self.agent, + "reclaim_local_vlan") as recl_fn: self.agent.network_delete("unused_context", network_id="123") self.assertFalse(recl_fn.called) self.agent.local_vlan_map["123"] = "LVM object" self.agent.network_delete("unused_context", network_id="123") - self.assertFalse(clean_tun_fn.called) recl_fn.assert_called_with("123") def test_port_update(self): @@ -616,6 +614,73 @@ class TestOFANeutronAgent(OFAAgentTestCase): {'type': p_const.TYPE_GRE, 'ip': 'remote_ip'}) self.assertEqual(ofport, 0) + def _create_tunnel_port_name(self, tunnel_ip, tunnel_type): + tunnel_ip_hex = '%08x' % netaddr.IPAddress(tunnel_ip, version=4) + return '%s-%s' % (tunnel_type, tunnel_ip_hex) + + def test_tunnel_sync_with_valid_ip_address_and_gre_type(self): + tunnel_ip = '100.101.102.103' + self.agent.tunnel_types = ['gre'] + tun_name = self._create_tunnel_port_name(tunnel_ip, + self.agent.tunnel_types[0]) + fake_tunnel_details = {'tunnels': [{'ip_address': tunnel_ip}]} + with contextlib.nested( + mock.patch.object(self.agent.plugin_rpc, 'tunnel_sync', + return_value=fake_tunnel_details), + mock.patch.object(self.agent, 'setup_tunnel_port') + ) as (tunnel_sync_rpc_fn, setup_tunnel_port_fn): + self.agent.tunnel_sync() + expected_calls = [mock.call(tun_name, tunnel_ip, + self.agent.tunnel_types[0])] + setup_tunnel_port_fn.assert_has_calls(expected_calls) + + def test_tunnel_sync_with_valid_ip_address_and_vxlan_type(self): + tunnel_ip = '100.101.31.15' + self.agent.tunnel_types = ['vxlan'] + tun_name = self._create_tunnel_port_name(tunnel_ip, + self.agent.tunnel_types[0]) + fake_tunnel_details = {'tunnels': [{'ip_address': tunnel_ip}]} + with contextlib.nested( + mock.patch.object(self.agent.plugin_rpc, 'tunnel_sync', + return_value=fake_tunnel_details), + mock.patch.object(self.agent, 'setup_tunnel_port') + ) as (tunnel_sync_rpc_fn, setup_tunnel_port_fn): + self.agent.tunnel_sync() + expected_calls = [mock.call(tun_name, tunnel_ip, + self.agent.tunnel_types[0])] + setup_tunnel_port_fn.assert_has_calls(expected_calls) + + def test_tunnel_sync_invalid_ip_address(self): + tunnel_ip = '100.100.100.100' + self.agent.tunnel_types = ['vxlan'] + tun_name = self._create_tunnel_port_name(tunnel_ip, + self.agent.tunnel_types[0]) + fake_tunnel_details = {'tunnels': [{'ip_address': '300.300.300.300'}, + {'ip_address': tunnel_ip}]} + with contextlib.nested( + mock.patch.object(self.agent.plugin_rpc, 'tunnel_sync', + return_value=fake_tunnel_details), + mock.patch.object(self.agent, 'setup_tunnel_port') + ) as (tunnel_sync_rpc_fn, setup_tunnel_port_fn): + self.agent.tunnel_sync() + setup_tunnel_port_fn.assert_called_once_with( + tun_name, tunnel_ip, self.agent.tunnel_types[0]) + + def test_tunnel_update(self): + tunnel_ip = '10.10.10.10' + self.agent.tunnel_types = ['gre'] + tun_name = self._create_tunnel_port_name(tunnel_ip, + self.agent.tunnel_types[0]) + kwargs = {'tunnel_ip': tunnel_ip, + 'tunnel_type': self.agent.tunnel_types[0]} + self.agent.setup_tunnel_port = mock.Mock() + self.agent.enable_tunneling = True + self.agent.l2_pop = False + self.agent.tunnel_update(context=None, **kwargs) + expected_calls = [mock.call(tun_name, tunnel_ip, + self.agent.tunnel_types[0])] + self.agent.setup_tunnel_port.assert_has_calls(expected_calls) + class AncillaryBridgesTest(OFAAgentTestCase):