Uniquely identify tunnel interfaces for fullstack tests
OVS agent tunnel interfaces are named via: '%s-%s' % (tunnel_type, destination_ip) This means that the tunnel interface name is not unique if two OVS agents on the same machine try to form a tunnel with a third agent. This happens during full stack tests that start multiple copies of the OVS agent on the test machine. Thus, for full stack tests, to make sure that the tunnel interface names created by ovs agents are globally unique, they will have the following format : '%s-%s-%s' % (tunnel_type, hash of source IP, hash of dest IP) Since this patch centralizes the formation of the tunnel interface name in a dedicated method that is monkey patched by the full stack framework, a unit test has been added for this method. Co-Authored-By: Mathieu Rohon <mathieu.rohon@gmail.com> Closes-Bug: #1467633 Change-Id: I991af6a5f982746cc297f0248454f803dfbb2daf
This commit is contained in:
parent
f40538e629
commit
1a1ccb8c94
|
@ -451,9 +451,6 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||||
if not self.enable_tunneling:
|
if not self.enable_tunneling:
|
||||||
return
|
return
|
||||||
tunnel_ip = kwargs.get('tunnel_ip')
|
tunnel_ip = kwargs.get('tunnel_ip')
|
||||||
tunnel_ip_hex = self.get_ip_in_hex(tunnel_ip)
|
|
||||||
if not tunnel_ip_hex:
|
|
||||||
return
|
|
||||||
tunnel_type = kwargs.get('tunnel_type')
|
tunnel_type = kwargs.get('tunnel_type')
|
||||||
if not tunnel_type:
|
if not tunnel_type:
|
||||||
LOG.error(_LE("No tunnel_type specified, cannot create tunnels"))
|
LOG.error(_LE("No tunnel_type specified, cannot create tunnels"))
|
||||||
|
@ -464,7 +461,9 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||||
return
|
return
|
||||||
if tunnel_ip == self.local_ip:
|
if tunnel_ip == self.local_ip:
|
||||||
return
|
return
|
||||||
tun_name = '%s-%s' % (tunnel_type, tunnel_ip_hex)
|
tun_name = self.get_tunnel_name(tunnel_type, self.local_ip, tunnel_ip)
|
||||||
|
if tun_name is None:
|
||||||
|
return
|
||||||
if not self.l2_pop:
|
if not self.l2_pop:
|
||||||
self._setup_tunnel_port(self.tun_br, tun_name, tunnel_ip,
|
self._setup_tunnel_port(self.tun_br, tun_name, tunnel_ip,
|
||||||
tunnel_type)
|
tunnel_type)
|
||||||
|
@ -1388,10 +1387,10 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||||
return ofport
|
return ofport
|
||||||
|
|
||||||
def setup_tunnel_port(self, br, remote_ip, network_type):
|
def setup_tunnel_port(self, br, remote_ip, network_type):
|
||||||
remote_ip_hex = self.get_ip_in_hex(remote_ip)
|
port_name = self.get_tunnel_name(
|
||||||
if not remote_ip_hex:
|
network_type, self.local_ip, remote_ip)
|
||||||
|
if port_name is None:
|
||||||
return 0
|
return 0
|
||||||
port_name = '%s-%s' % (network_type, remote_ip_hex)
|
|
||||||
ofport = self._setup_tunnel_port(br,
|
ofport = self._setup_tunnel_port(br,
|
||||||
port_name,
|
port_name,
|
||||||
remote_ip,
|
remote_ip,
|
||||||
|
@ -1408,8 +1407,8 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||||
items = list(self.tun_br_ofports[tunnel_type].items())
|
items = list(self.tun_br_ofports[tunnel_type].items())
|
||||||
for remote_ip, ofport in items:
|
for remote_ip, ofport in items:
|
||||||
if ofport == tun_ofport:
|
if ofport == tun_ofport:
|
||||||
port_name = '%s-%s' % (tunnel_type,
|
port_name = self.get_tunnel_name(
|
||||||
self.get_ip_in_hex(remote_ip))
|
tunnel_type, self.local_ip, remote_ip)
|
||||||
br.delete_port(port_name)
|
br.delete_port(port_name)
|
||||||
br.cleanup_tunnel_port(ofport)
|
br.cleanup_tunnel_port(ofport)
|
||||||
self.tun_br_ofports[tunnel_type].pop(remote_ip, None)
|
self.tun_br_ofports[tunnel_type].pop(remote_ip, None)
|
||||||
|
@ -1613,7 +1612,8 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||||
'elapsed': time.time() - start})
|
'elapsed': time.time() - start})
|
||||||
return failed_devices
|
return failed_devices
|
||||||
|
|
||||||
def get_ip_in_hex(self, ip_address):
|
@classmethod
|
||||||
|
def get_ip_in_hex(cls, ip_address):
|
||||||
try:
|
try:
|
||||||
return '%08x' % netaddr.IPAddress(ip_address, version=4)
|
return '%08x' % netaddr.IPAddress(ip_address, version=4)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -1632,10 +1632,10 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||||
for tunnel in tunnels:
|
for tunnel in tunnels:
|
||||||
if self.local_ip != tunnel['ip_address']:
|
if self.local_ip != tunnel['ip_address']:
|
||||||
remote_ip = tunnel['ip_address']
|
remote_ip = tunnel['ip_address']
|
||||||
remote_ip_hex = self.get_ip_in_hex(remote_ip)
|
tun_name = self.get_tunnel_name(
|
||||||
if not remote_ip_hex:
|
tunnel_type, self.local_ip, remote_ip)
|
||||||
|
if tun_name is None:
|
||||||
continue
|
continue
|
||||||
tun_name = '%s-%s' % (tunnel_type, remote_ip_hex)
|
|
||||||
self._setup_tunnel_port(self.tun_br,
|
self._setup_tunnel_port(self.tun_br,
|
||||||
tun_name,
|
tun_name,
|
||||||
tunnel['ip_address'],
|
tunnel['ip_address'],
|
||||||
|
@ -1646,6 +1646,13 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_tunnel_name(cls, network_type, local_ip, remote_ip):
|
||||||
|
remote_ip_hex = cls.get_ip_in_hex(remote_ip)
|
||||||
|
if not remote_ip_hex:
|
||||||
|
return None
|
||||||
|
return '%s-%s' % (network_type, remote_ip_hex)
|
||||||
|
|
||||||
def _agent_has_updates(self, polling_manager):
|
def _agent_has_updates(self, polling_manager):
|
||||||
return (polling_manager.is_polling_required or
|
return (polling_manager.is_polling_required or
|
||||||
self.updated_ports or
|
self.updated_ports or
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
from neutron.cmd.eventlet.plugins.ovs_neutron_agent import main as _main
|
||||||
|
from neutron.common import constants as n_const
|
||||||
|
from neutron.plugins.ml2.drivers.openvswitch.agent.ovs_neutron_agent \
|
||||||
|
import OVSNeutronAgent
|
||||||
|
|
||||||
|
|
||||||
|
def get_tunnel_name_full(cls, network_type, local_ip, remote_ip):
|
||||||
|
network_type = network_type[:3]
|
||||||
|
remote_ip_hex = cls.get_ip_in_hex(remote_ip)
|
||||||
|
if not remote_ip_hex:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Remove length of network_type and two dashes
|
||||||
|
hashlen = (n_const.DEVICE_NAME_MAX_LEN - len(network_type) - 2) // 2
|
||||||
|
remote_ip_hex = remote_ip_hex.encode('utf-8')
|
||||||
|
remote_ip_hash = hashlib.sha1(remote_ip_hex).hexdigest()[0:hashlen]
|
||||||
|
local_ip_hex = cls.get_ip_in_hex(local_ip).encode('utf-8')
|
||||||
|
source_ip_hash = hashlib.sha1(local_ip_hex).hexdigest()[0:hashlen]
|
||||||
|
return '%s-%s-%s' % (network_type, source_ip_hash, remote_ip_hash)
|
||||||
|
|
||||||
|
OVSNeutronAgent.get_tunnel_name = get_tunnel_name_full
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
_main()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
|
@ -151,7 +151,9 @@ class OVSAgentFixture(fixtures.Fixture):
|
||||||
self.process_fixture = self.useFixture(ProcessFixture(
|
self.process_fixture = self.useFixture(ProcessFixture(
|
||||||
test_name=self.test_name,
|
test_name=self.test_name,
|
||||||
process_name=self.NEUTRON_OVS_AGENT,
|
process_name=self.NEUTRON_OVS_AGENT,
|
||||||
exec_name=self.NEUTRON_OVS_AGENT,
|
exec_name=spawn.find_executable(
|
||||||
|
'ovs_agent.py',
|
||||||
|
path=os.path.join(base.ROOTDIR, 'common', 'agents')),
|
||||||
config_filenames=config_filenames))
|
config_filenames=config_filenames))
|
||||||
|
|
||||||
|
|
||||||
|
@ -173,7 +175,6 @@ class L3AgentFixture(fixtures.Fixture):
|
||||||
|
|
||||||
config_filenames = [self.neutron_cfg_fixture.filename,
|
config_filenames = [self.neutron_cfg_fixture.filename,
|
||||||
self.l3_agent_cfg_fixture.filename]
|
self.l3_agent_cfg_fixture.filename]
|
||||||
|
|
||||||
self.process_fixture = self.useFixture(ProcessFixture(
|
self.process_fixture = self.useFixture(ProcessFixture(
|
||||||
test_name=self.test_name,
|
test_name=self.test_name,
|
||||||
process_name=self.NEUTRON_L3_AGENT,
|
process_name=self.NEUTRON_L3_AGENT,
|
||||||
|
|
|
@ -45,7 +45,7 @@ class TestConnectivitySameNetwork(base.BaseFullStackTestCase):
|
||||||
# agent types present on machines.
|
# agent types present on machines.
|
||||||
environment.HostDescription(
|
environment.HostDescription(
|
||||||
l3_agent=self.l2_pop,
|
l3_agent=self.l2_pop,
|
||||||
of_interface=self.of_interface) for _ in range(2)]
|
of_interface=self.of_interface) for _ in range(3)]
|
||||||
env = environment.Environment(
|
env = environment.Environment(
|
||||||
environment.EnvironmentDescription(
|
environment.EnvironmentDescription(
|
||||||
network_type=self.network_type,
|
network_type=self.network_type,
|
||||||
|
@ -67,9 +67,11 @@ class TestConnectivitySameNetwork(base.BaseFullStackTestCase):
|
||||||
network['id'],
|
network['id'],
|
||||||
tenant_uuid,
|
tenant_uuid,
|
||||||
self.safe_client))
|
self.safe_client))
|
||||||
for i in range(2)]
|
for i in range(3)]
|
||||||
|
|
||||||
for vm in vms:
|
for vm in vms:
|
||||||
vm.block_until_boot()
|
vm.block_until_boot()
|
||||||
|
|
||||||
vms[0].block_until_ping(vms[1].ip)
|
vms[0].block_until_ping(vms[1].ip)
|
||||||
|
vms[0].block_until_ping(vms[2].ip)
|
||||||
|
vms[1].block_until_ping(vms[2].ip)
|
||||||
|
|
|
@ -107,6 +107,7 @@ class TestOvsNeutronAgent(object):
|
||||||
group='SECURITYGROUP')
|
group='SECURITYGROUP')
|
||||||
cfg.CONF.set_default('quitting_rpc_timeout', 10, 'AGENT')
|
cfg.CONF.set_default('quitting_rpc_timeout', 10, 'AGENT')
|
||||||
cfg.CONF.set_default('prevent_arp_spoofing', False, 'AGENT')
|
cfg.CONF.set_default('prevent_arp_spoofing', False, 'AGENT')
|
||||||
|
cfg.CONF.set_default('local_ip', '127.0.0.1', 'OVS')
|
||||||
mock.patch(
|
mock.patch(
|
||||||
'neutron.agent.common.ovs_lib.OVSBridge.get_ports_attributes',
|
'neutron.agent.common.ovs_lib.OVSBridge.get_ports_attributes',
|
||||||
return_value=[]).start()
|
return_value=[]).start()
|
||||||
|
@ -2910,3 +2911,21 @@ class TestValidateTunnelLocalIP(base.BaseTestCase):
|
||||||
with testtools.ExpectedException(SystemExit):
|
with testtools.ExpectedException(SystemExit):
|
||||||
ovs_agent.validate_local_ip(FAKE_IP1)
|
ovs_agent.validate_local_ip(FAKE_IP1)
|
||||||
mock_get_device_by_ip.assert_called_once_with(FAKE_IP1)
|
mock_get_device_by_ip.assert_called_once_with(FAKE_IP1)
|
||||||
|
|
||||||
|
|
||||||
|
class TestOvsAgentTunnelName(base.BaseTestCase):
|
||||||
|
def test_get_ip_in_hex_invalid_address(self):
|
||||||
|
self.assertIsNone(
|
||||||
|
ovs_agent.OVSNeutronAgent.get_ip_in_hex('a.b.c.d'))
|
||||||
|
|
||||||
|
def test_get_tunnel_name_vxlan(self):
|
||||||
|
self.assertEqual(
|
||||||
|
'vxlan-7f000002',
|
||||||
|
ovs_agent.OVSNeutronAgent.get_tunnel_name(
|
||||||
|
'vxlan', '127.0.0.1', '127.0.0.2'))
|
||||||
|
|
||||||
|
def test_get_tunnel_name_gre(self):
|
||||||
|
self.assertEqual(
|
||||||
|
'gre-7f000002',
|
||||||
|
ovs_agent.OVSNeutronAgent.get_tunnel_name(
|
||||||
|
'gre', '127.0.0.1', '127.0.0.2'))
|
||||||
|
|
Loading…
Reference in New Issue