import time
import eventlet
+import netaddr
from oslo.config import cfg
from neutron.agent import l2population_rpc
if not self.enable_tunneling:
return
tunnel_ip = kwargs.get('tunnel_ip')
- tunnel_id = kwargs.get('tunnel_id', tunnel_ip)
+ tunnel_id = kwargs.get('tunnel_id', self.get_ip_in_hex(tunnel_ip))
if not tunnel_id:
- tunnel_id = tunnel_ip
+ return
tunnel_type = kwargs.get('tunnel_type')
if not tunnel_type:
LOG.error(_("No tunnel_type specified, cannot create tunnels"))
ofport = self.tun_br_ofports[
lvm.network_type].get(agent_ip)
if not ofport:
- port_name = '%s-%s' % (lvm.network_type, agent_ip)
+ remote_ip_hex = self.get_ip_in_hex(agent_ip)
+ if not remote_ip_hex:
+ continue
+ port_name = '%s-%s' % (lvm.network_type, remote_ip_hex)
ofport = self.setup_tunnel_port(port_name, agent_ip,
lvm.network_type)
if ofport == 0:
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)
+ port_name = '%s-%s' % (tunnel_type,
+ self.get_ip_in_hex(remote_ip))
self.tun_br.delete_port(port_name)
self.tun_br_ofports[tunnel_type].pop(remote_ip, None)
# If one of the above opertaions fails => resync with plugin
return (resync_a | resync_b)
+ def get_ip_in_hex(self, ip_address):
+ try:
+ return '%08x' % netaddr.IPAddress(ip_address, version=4)
+ except Exception:
+ LOG.warn(_("Unable to create tunnel port. Invalid remote IP: %s"),
+ ip_address)
+ return
+
def tunnel_sync(self):
resync = False
try:
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)
+ tunnel_id = tunnel.get('id')
+ # Unlike the OVS plugin, ML2 doesn't return an id
+ # key. So use ip_address to form port name instead.
+ # Port name must be <=15 chars, so use shorter hex.
+ remote_ip = tunnel['ip_address']
+ remote_ip_hex = self.get_ip_in_hex(remote_ip)
+ if not tunnel_id and not remote_ip_hex:
+ continue
+ tun_name = '%s-%s' % (tunnel_type,
+ tunnel_id or remote_ip_hex)
self.setup_tunnel_port(tun_name,
tunnel['ip_address'],
tunnel_type)
lvm2.tun_ofports = set(['1', '2'])
self.agent.local_vlan_map = {'net1': lvm1, 'net2': lvm2}
self.agent.tun_br_ofports = {'gre':
- {'ip_agent_1': '1', 'ip_agent_2': '2'}}
+ {'1.1.1.1': '1', '2.2.2.2': '2'}}
def test_fdb_ignore_network(self):
self._prepare_l2_pop_ofports()
{'network_type': 'gre',
'segment_id': 'tun1',
'ports':
- {'ip_agent_2':
+ {'2.2.2.2':
[['mac', 'ip'],
n_const.FLOODING_ENTRY]}}}
with contextlib.nested(
{'network_type': 'gre',
'segment_id': 'tun2',
'ports':
- {'ip_agent_2':
+ {'2.2.2.2':
[['mac', 'ip'],
n_const.FLOODING_ENTRY]}}}
with contextlib.nested(
fdb_entry = {'net1':
{'network_type': 'gre',
'segment_id': 'tun1',
- 'ports': {'ip_agent_1': [['mac', 'ip']]}}}
+ 'ports': {'1.1.1.1': [['mac', 'ip']]}}}
with contextlib.nested(
mock.patch.object(self.agent.tun_br, 'add_flow'),
mock.patch.object(self.agent.tun_br, 'mod_flow'),
) as (add_flow_fn, mod_flow_fn, add_tun_fn):
self.agent.fdb_add(None, fdb_entry)
self.assertFalse(add_tun_fn.called)
- fdb_entry['net1']['ports']['ip_agent_3'] = [['mac', 'ip']]
+ fdb_entry['net1']['ports']['10.10.10.10'] = [['mac', 'ip']]
self.agent.fdb_add(None, fdb_entry)
- add_tun_fn.assert_called_with('gre-ip_agent_3', 'ip_agent_3',
- 'gre')
+ add_tun_fn.assert_called_with('gre-0a0a0a0a', '10.10.10.10', 'gre')
def test_fdb_del_port(self):
self._prepare_l2_pop_ofports()
fdb_entry = {'net2':
{'network_type': 'gre',
'segment_id': 'tun2',
- 'ports': {'ip_agent_2': [n_const.FLOODING_ENTRY]}}}
+ 'ports': {'2.2.2.2': [n_const.FLOODING_ENTRY]}}}
with contextlib.nested(
mock.patch.object(self.agent.tun_br, 'delete_flows'),
mock.patch.object(self.agent.tun_br, 'delete_port')
) as (del_flow_fn, del_port_fn):
self.agent.fdb_remove(None, fdb_entry)
- del_port_fn.assert_called_once_with('gre-ip_agent_2')
+ del_port_fn.assert_called_once_with('gre-02020202')
def test_recl_lv_port_to_preserve(self):
self._prepare_l2_pop_ofports()
mock.patch.object(self.agent.tun_br, 'delete_flows')
) as (del_port_fn, del_flow_fn):
self.agent.reclaim_local_vlan('net2')
- del_port_fn.assert_called_once_with('gre-ip_agent_2')
+ del_port_fn.assert_called_once_with('gre-02020202')
def test_daemon_loop_uses_polling_manager(self):
with mock.patch(
{'type': p_const.TYPE_GRE, 'ip': 'remote_ip'})
self.assertEqual(ofport, 0)
+ def test_tunnel_sync_with_ovs_plugin(self):
+ fake_tunnel_details = {'tunnels': [{'id': '42',
+ 'ip_address': '100.101.102.103'}]}
+ 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_types = ['gre']
+ self.agent.tunnel_sync()
+ expected_calls = [mock.call('gre-42', '100.101.102.103', 'gre')]
+ setup_tunnel_port_fn.assert_has_calls(expected_calls)
+
+ def test_tunnel_sync_with_ml2_plugin(self):
+ fake_tunnel_details = {'tunnels': [{'ip_address': '100.101.31.15'}]}
+ 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_types = ['vxlan']
+ self.agent.tunnel_sync()
+ expected_calls = [mock.call('vxlan-64651f0f',
+ '100.101.31.15', 'vxlan')]
+ setup_tunnel_port_fn.assert_has_calls(expected_calls)
+
+ def test_tunnel_sync_invalid_ip_address(self):
+ fake_tunnel_details = {'tunnels': [{'ip_address': '300.300.300.300'},
+ {'ip_address': '100.100.100.100'}]}
+ 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_types = ['vxlan']
+ self.agent.tunnel_sync()
+ setup_tunnel_port_fn.assert_called_once_with('vxlan-64646464',
+ '100.100.100.100',
+ 'vxlan')
+
+ def test_tunnel_update(self):
+ kwargs = {'tunnel_ip': '10.10.10.10',
+ 'tunnel_type': 'gre'}
+ self.agent.setup_tunnel_port = mock.Mock()
+ self.agent.enable_tunneling = True
+ self.agent.tunnel_types = ['gre']
+ self.agent.l2_pop = False
+ self.agent.tunnel_update(context=None, **kwargs)
+ expected_calls = [mock.call('gre-0a0a0a0a', '10.10.10.10', 'gre')]
+ self.agent.setup_tunnel_port.assert_has_calls(expected_calls)
+
class AncillaryBridgesTest(base.BaseTestCase):