Add fullstack resources for linuxbridge agent

This patch adds resources, fixtures and base test_connectivity tests
for host with linuxbridge agent.

Model of host with linuxbridge agent is made in separate namespace
named "host-XXX". It has got connectivity with rabbit via ovs port.
Same port is used also to provide connectivity with different "hosts"

Co-Authored-By: Mathieu Rohon <mathieu.rohon@gmail.com>

Change-Id: I6230d8d09f77bd20674bf6c3be69fbc1627d66f8
Closes-bug: #1518675
This commit is contained in:
Sławek Kapłoński 2015-11-23 23:14:12 +00:00
parent 21d139d441
commit 2254acef06
10 changed files with 450 additions and 80 deletions

View File

@ -225,9 +225,12 @@ neutron/tests/fullstack/test_connectivity.py.
Full stack testing can simulate multi node testing by starting an agent Full stack testing can simulate multi node testing by starting an agent
multiple times. Specifically, each node would have its own copy of the multiple times. Specifically, each node would have its own copy of the
OVS/DHCP/L3 agents, all configured with the same "host" value. Each OVS agent OVS/LinuxBridge/DHCP/L3 agents, all configured with the same "host" value.
is connected to its own pair of br-int/br-ex, and those bridges are then Each OVS agent is connected to its own pair of br-int/br-ex, and those bridges
interconnected. are then interconnected.
For LinuxBridge agent each agent is started in its own namespace, called
"host-<some_random_value>". Such namespaces are connected with OVS "central"
bridge to eachother.
.. image:: images/fullstack_multinode_simulation.png .. image:: images/fullstack_multinode_simulation.png

View File

@ -21,3 +21,5 @@ VXLAN_MCAST = 'multicast_flooding'
VXLAN_UCAST = 'unicast_flooding' VXLAN_UCAST = 'unicast_flooding'
EXTENSION_DRIVER_TYPE = 'linuxbridge' EXTENSION_DRIVER_TYPE = 'linuxbridge'
RESOURCE_ID_LENGTH = 11

View File

@ -128,11 +128,13 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
return None return None
return self.bridge_mappings.get(physical_network) return self.bridge_mappings.get(physical_network)
def get_bridge_name(self, network_id): @staticmethod
def get_bridge_name(network_id):
if not network_id: if not network_id:
LOG.warning(_LW("Invalid Network ID, will lead to incorrect " LOG.warning(_LW("Invalid Network ID, will lead to incorrect "
"bridge name")) "bridge name"))
bridge_name = BRIDGE_NAME_PREFIX + network_id[0:11] bridge_name = BRIDGE_NAME_PREFIX + \
network_id[:lconst.RESOURCE_ID_LENGTH]
return bridge_name return bridge_name
def get_subinterface_name(self, physical_interface, vlan_id): def get_subinterface_name(self, physical_interface, vlan_id):
@ -142,11 +144,13 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
subinterface_name = '%s.%s' % (physical_interface, vlan_id) subinterface_name = '%s.%s' % (physical_interface, vlan_id)
return subinterface_name return subinterface_name
def get_tap_device_name(self, interface_id): @staticmethod
def get_tap_device_name(interface_id):
if not interface_id: if not interface_id:
LOG.warning(_LW("Invalid Interface ID, will lead to incorrect " LOG.warning(_LW("Invalid Interface ID, will lead to incorrect "
"tap device name")) "tap device name"))
tap_device_name = constants.TAP_DEVICE_PREFIX + interface_id[0:11] tap_device_name = constants.TAP_DEVICE_PREFIX + \
interface_id[:lconst.RESOURCE_ID_LENGTH]
return tap_device_name return tap_device_name
def get_vxlan_device_name(self, segmentation_id): def get_vxlan_device_name(self, segmentation_id):

View File

@ -39,6 +39,8 @@ from neutron.agent.linux import ip_lib
from neutron.agent.linux import utils from neutron.agent.linux import utils
from neutron.common import constants as n_const from neutron.common import constants as n_const
from neutron.db import db_base_plugin_common from neutron.db import db_base_plugin_common
from neutron.plugins.ml2.drivers.linuxbridge.agent import \
linuxbridge_neutron_agent as linuxbridge_agent
from neutron.tests import base as tests_base from neutron.tests import base as tests_base
from neutron.tests.common import base as common_base from neutron.tests.common import base as common_base
from neutron.tests import tools from neutron.tests import tools
@ -47,11 +49,18 @@ UNDEFINED = object()
NS_PREFIX = 'test-' NS_PREFIX = 'test-'
BR_PREFIX = 'test-br' BR_PREFIX = 'test-br'
PORT_PREFIX = 'test-port' PORT_PREFIX = 'port'
VETH0_PREFIX = 'test-veth0' VETH0_PREFIX = 'test-veth0'
VETH1_PREFIX = 'test-veth1' VETH1_PREFIX = 'test-veth1'
PATCH_PREFIX = 'patch' PATCH_PREFIX = 'patch'
# port name should be shorter than DEVICE_NAME_MAX_LEN because if this
# port is used to provide vlan connection between two linuxbridge
# agents then place for vlan ID is also required, Vlan ID can take max 4 digits
# and there is also additional "." in device name so it will in overall gives
# DEVICE_NAME_MAX_LEN = 15 chars
LB_DEVICE_NAME_MAX_LEN = 10
SS_SOURCE_PORT_PATTERN = re.compile( SS_SOURCE_PORT_PATTERN = re.compile(
r'^.*\s+\d+\s+.*:(?P<port>\d+)\s+[0-9:].*') r'^.*\s+\d+\s+.*:(?P<port>\d+)\s+[0-9:].*')
@ -473,7 +482,7 @@ class VethFixture(fixtures.Fixture):
"""Create a veth. """Create a veth.
:ivar ports: created veth ports :ivar ports: created veth ports
:type ports: IPDevice 2-uplet :type ports: tuple of 2 IPDevice
""" """
def _setUp(self): def _setUp(self):
@ -508,6 +517,32 @@ class VethFixture(fixtures.Fixture):
tools.fail('%s is not a valid VethFixture veth endpoint' % name) tools.fail('%s is not a valid VethFixture veth endpoint' % name)
class NamedVethFixture(VethFixture):
"""Create a veth with at least one specified name of a device
:ivar ports: created veth ports
:type ports: tuple of 2 IPDevice
"""
def __init__(self, veth0_prefix=VETH0_PREFIX, veth1_prefix=VETH1_PREFIX):
super(NamedVethFixture, self).__init__()
self.veth0_name = self.get_veth_name(veth0_prefix)
self.veth1_name = self.get_veth_name(veth1_prefix)
def _setUp(self):
ip_wrapper = ip_lib.IPWrapper()
self.ports = ip_wrapper.add_veth(self.veth0_name, self.veth1_name)
self.addCleanup(self.destroy)
@staticmethod
def get_veth_name(name):
if name.startswith(VETH0_PREFIX):
return tests_base.get_rand_device_name(VETH0_PREFIX)
if name.startswith(VETH1_PREFIX):
return tests_base.get_rand_device_name(VETH1_PREFIX)
return name
@six.add_metaclass(abc.ABCMeta) @six.add_metaclass(abc.ABCMeta)
class PortFixture(fixtures.Fixture): class PortFixture(fixtures.Fixture):
"""Create a port. """Create a port.
@ -541,7 +576,7 @@ class PortFixture(fixtures.Fixture):
if isinstance(bridge, ovs_lib.OVSBridge): if isinstance(bridge, ovs_lib.OVSBridge):
return OVSPortFixture(bridge, namespace, mac, port_id) return OVSPortFixture(bridge, namespace, mac, port_id)
if isinstance(bridge, bridge_lib.BridgeDevice): if isinstance(bridge, bridge_lib.BridgeDevice):
return LinuxBridgePortFixture(bridge, namespace) return LinuxBridgePortFixture(bridge, namespace, mac, port_id)
if isinstance(bridge, VethBridge): if isinstance(bridge, VethBridge):
return VethPortFixture(bridge, namespace) return VethPortFixture(bridge, namespace)
tools.fail('Unexpected bridge type: %s' % type(bridge)) tools.fail('Unexpected bridge type: %s' % type(bridge))
@ -578,7 +613,13 @@ class OVSPortFixture(PortFixture):
interface_config.register_opts(interface.OPTS) interface_config.register_opts(interface.OPTS)
ovs_interface = interface.OVSInterfaceDriver(interface_config) ovs_interface = interface.OVSInterfaceDriver(interface_config)
port_name = tests_base.get_rand_device_name(PORT_PREFIX) # because in some tests this port can be used to providing connection
# between linuxbridge agents and vlan_id can be also added to this
# device name it has to be max LB_DEVICE_NAME_MAX_LEN long
port_name = tests_base.get_rand_name(
LB_DEVICE_NAME_MAX_LEN,
PORT_PREFIX
)
ovs_interface.plug_new( ovs_interface.plug_new(
None, None,
self.port_id, self.port_id,
@ -598,22 +639,44 @@ class LinuxBridgeFixture(fixtures.Fixture):
:ivar namespace: created bridge namespace :ivar namespace: created bridge namespace
:type namespace: str :type namespace: str
""" """
def __init__(self, prefix=BR_PREFIX, namespace=UNDEFINED,
def __init__(self, prefix=BR_PREFIX, namespace=UNDEFINED): prefix_is_full_name=False):
super(LinuxBridgeFixture, self).__init__() super(LinuxBridgeFixture, self).__init__()
self.prefix = prefix self.prefix = prefix
self.prefix_is_full_name = prefix_is_full_name
self.namespace = namespace self.namespace = namespace
def _setUp(self): def _setUp(self):
if self.namespace is UNDEFINED: if self.namespace is UNDEFINED:
self.namespace = self.useFixture(NamespaceFixture()).name self.namespace = self.useFixture(NamespaceFixture()).name
self.bridge = common_base.create_resource( self.bridge = self._create_bridge()
self.prefix, self.addCleanup(self.safe_delete)
bridge_lib.BridgeDevice.addbr,
namespace=self.namespace)
self.addCleanup(self.bridge.delbr)
self.bridge.link.set_up() self.bridge.link.set_up()
self.addCleanup(self.bridge.link.set_down) self.addCleanup(self.safe_set_down)
def safe_set_down(self):
try:
self.bridge.link.set_down()
except RuntimeError:
pass
def safe_delete(self):
try:
self.bridge.delbr()
except RuntimeError:
pass
def _create_bridge(self):
if self.prefix_is_full_name:
return bridge_lib.BridgeDevice.addbr(
name=self.prefix,
namespace=self.namespace
)
else:
return common_base.create_resource(
self.prefix,
bridge_lib.BridgeDevice.addbr,
namespace=self.namespace)
class LinuxBridgePortFixture(PortFixture): class LinuxBridgePortFixture(PortFixture):
@ -625,12 +688,29 @@ class LinuxBridgePortFixture(PortFixture):
:type br_port: IPDevice :type br_port: IPDevice
""" """
def __init__(self, bridge, namespace=None, mac=None, port_id=None):
super(LinuxBridgePortFixture, self).__init__(
bridge, namespace, mac, port_id)
# we need to override port_id value here because in Port() class it is
# always generated as random. In LinuxBridgePortFixture we need to have
# it empty if it was not give because then proper veth_pair will be
# created (for example in some functional tests)
self.port_id = port_id
def _create_bridge_fixture(self): def _create_bridge_fixture(self):
return LinuxBridgeFixture() return LinuxBridgeFixture()
def _setUp(self): def _setUp(self):
super(LinuxBridgePortFixture, self)._setUp() super(LinuxBridgePortFixture, self)._setUp()
self.port, self.br_port = self.useFixture(VethFixture()).ports br_port_name = self._get_port_name()
if br_port_name:
self.br_port, self.port = self.useFixture(
NamedVethFixture(veth0_prefix=br_port_name)).ports
else:
self.br_port, self.port = self.useFixture(VethFixture()).ports
if self.mac:
self.port.link.set_address(self.mac)
# bridge side # bridge side
br_ip_wrapper = ip_lib.IPWrapper(self.bridge.namespace) br_ip_wrapper = ip_lib.IPWrapper(self.bridge.namespace)
@ -643,6 +723,12 @@ class LinuxBridgePortFixture(PortFixture):
ns_ip_wrapper.add_device_to_namespace(self.port) ns_ip_wrapper.add_device_to_namespace(self.port)
self.port.link.set_up() self.port.link.set_up()
def _get_port_name(self):
if self.port_id:
return linuxbridge_agent.LinuxBridgeManager.get_tap_device_name(
self.port_id)
return None
class VethBridge(object): class VethBridge(object):

View File

@ -88,7 +88,7 @@ class NeutronConfigFixture(ConfigFixture):
'oslo_messaging_rabbit': { 'oslo_messaging_rabbit': {
'rabbit_userid': rabbitmq_environment.user, 'rabbit_userid': rabbitmq_environment.user,
'rabbit_password': rabbitmq_environment.password, 'rabbit_password': rabbitmq_environment.password,
'rabbit_hosts': '127.0.0.1', 'rabbit_hosts': rabbitmq_environment.host,
'rabbit_virtual_host': rabbitmq_environment.vhost, 'rabbit_virtual_host': rabbitmq_environment.vhost,
} }
}) })
@ -114,14 +114,10 @@ class ML2ConfigFixture(ConfigFixture):
super(ML2ConfigFixture, self).__init__( super(ML2ConfigFixture, self).__init__(
env_desc, host_desc, temp_dir, base_filename='ml2_conf.ini') env_desc, host_desc, temp_dir, base_filename='ml2_conf.ini')
mechanism_drivers = 'openvswitch'
if self.env_desc.l2_pop:
mechanism_drivers += ',l2population'
self.config.update({ self.config.update({
'ml2': { 'ml2': {
'tenant_network_types': tenant_network_types, 'tenant_network_types': tenant_network_types,
'mechanism_drivers': mechanism_drivers, 'mechanism_drivers': self.mechanism_drivers,
}, },
'ml2_type_vlan': { 'ml2_type_vlan': {
'network_vlan_ranges': 'physnet1:1000:2999', 'network_vlan_ranges': 'physnet1:1000:2999',
@ -138,6 +134,16 @@ class ML2ConfigFixture(ConfigFixture):
self.config['ml2']['extension_drivers'] =\ self.config['ml2']['extension_drivers'] =\
qos_ext.QOS_EXT_DRIVER_ALIAS qos_ext.QOS_EXT_DRIVER_ALIAS
@property
def mechanism_drivers(self):
mechanism_drivers = set(['openvswitch'])
for host in self.host_desc:
if host.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE:
mechanism_drivers.add('linuxbridge')
if self.env_desc.l2_pop:
mechanism_drivers.add('l2population')
return ','.join(mechanism_drivers)
class OVSConfigFixture(ConfigFixture): class OVSConfigFixture(ConfigFixture):
@ -205,12 +211,59 @@ class OVSConfigFixture(ConfigFixture):
return self.config.ovs.tunnel_bridge return self.config.ovs.tunnel_bridge
class LinuxBridgeConfigFixture(ConfigFixture):
def __init__(self, env_desc, host_desc, temp_dir, local_ip,
physical_device_name):
super(LinuxBridgeConfigFixture, self).__init__(
env_desc, host_desc, temp_dir,
base_filename="linuxbridge_agent.ini"
)
self.config.update({
'VXLAN': {
'enable_vxlan': str(self.env_desc.tunneling_enabled),
'local_ip': local_ip,
'l2_population': str(self.env_desc.l2_pop),
}
})
if self.env_desc.tunneling_enabled:
self.config.update({
'LINUX_BRIDGE': {
'bridge_mappings': self._generate_bridge_mappings(
physical_device_name
)
}
})
else:
self.config.update({
'LINUX_BRIDGE': {
'physical_interface_mappings':
self._generate_bridge_mappings(
physical_device_name
)
}
})
def _generate_bridge_mappings(self, device_name):
return 'physnet1:%s' % device_name
class L3ConfigFixture(ConfigFixture): class L3ConfigFixture(ConfigFixture):
def __init__(self, env_desc, host_desc, temp_dir, integration_bridge): def __init__(self, env_desc, host_desc, temp_dir, integration_bridge=None):
super(L3ConfigFixture, self).__init__( super(L3ConfigFixture, self).__init__(
env_desc, host_desc, temp_dir, base_filename='l3_agent.ini') env_desc, host_desc, temp_dir, base_filename='l3_agent.ini')
if host_desc.l2_agent_type == constants.AGENT_TYPE_OVS:
self._prepare_config_with_ovs_agent(integration_bridge)
elif host_desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE:
self._prepare_config_with_linuxbridge_agent()
self.config['DEFAULT'].update({
'debug': 'True',
'verbose': 'True',
'test_namespace_suffix': self._generate_namespace_suffix(),
})
def _prepare_config_with_ovs_agent(self, integration_bridge):
self.config.update({ self.config.update({
'DEFAULT': { 'DEFAULT': {
'l3_agent_manager': ('neutron.agent.l3_agent.' 'l3_agent_manager': ('neutron.agent.l3_agent.'
@ -219,9 +272,14 @@ class L3ConfigFixture(ConfigFixture):
'OVSInterfaceDriver'), 'OVSInterfaceDriver'),
'ovs_integration_bridge': integration_bridge, 'ovs_integration_bridge': integration_bridge,
'external_network_bridge': self._generate_external_bridge(), 'external_network_bridge': self._generate_external_bridge(),
'debug': 'True', }
'verbose': 'True', })
'test_namespace_suffix': self._generate_namespace_suffix(),
def _prepare_config_with_linuxbridge_agent(self):
self.config.update({
'DEFAULT': {
'interface_driver': ('neutron.agent.linux.interface.'
'BridgeInterfaceDriver'),
} }
}) })

View File

@ -19,8 +19,12 @@ import netaddr
from neutronclient.common import exceptions as nc_exc from neutronclient.common import exceptions as nc_exc
from oslo_config import cfg from oslo_config import cfg
from neutron.agent.linux import ip_lib
from neutron.agent.linux import utils from neutron.agent.linux import utils
from neutron.common import constants
from neutron.common import utils as common_utils from neutron.common import utils as common_utils
from neutron.plugins.ml2.drivers.linuxbridge.agent import \
linuxbridge_neutron_agent as lb_agent
from neutron.tests.common import net_helpers from neutron.tests.common import net_helpers
from neutron.tests.fullstack.resources import config from neutron.tests.fullstack.resources import config
from neutron.tests.fullstack.resources import process from neutron.tests.fullstack.resources import process
@ -35,6 +39,7 @@ class EnvironmentDescription(object):
self.network_type = network_type self.network_type = network_type
self.l2_pop = l2_pop self.l2_pop = l2_pop
self.qos = qos self.qos = qos
self.network_range = None
@property @property
def tunneling_enabled(self): def tunneling_enabled(self):
@ -47,7 +52,9 @@ class HostDescription(object):
What agents should the host spawn? What mode should each agent operate What agents should the host spawn? What mode should each agent operate
under? under?
""" """
def __init__(self, l3_agent=False, of_interface='ovs-ofctl'): def __init__(self, l3_agent=False, of_interface='ovs-ofctl',
l2_agent_type=constants.AGENT_TYPE_OVS):
self.l2_agent_type = l2_agent_type
self.l3_agent = l3_agent self.l3_agent = l3_agent
self.of_interface = of_interface self.of_interface = of_interface
@ -75,12 +82,29 @@ class Host(fixtures.Fixture):
self.test_name = test_name self.test_name = test_name
self.neutron_config = neutron_config self.neutron_config = neutron_config
# Use reserved class E addresses # Use reserved class E addresses
self.local_ip = self.get_random_ip('240.0.0.1', '255.255.255.254') self.local_ip = self.allocate_local_ip()
self.central_data_bridge = central_data_bridge self.central_data_bridge = central_data_bridge
self.central_external_bridge = central_external_bridge self.central_external_bridge = central_external_bridge
self.host_namespace = None
self.agents = {} self.agents = {}
# we need to cache already created "per network" bridges if linuxbridge
# agent is used on host:
self.network_bridges = {}
def _setUp(self): def _setUp(self):
if self.host_desc.l2_agent_type == constants.AGENT_TYPE_OVS:
self.setup_host_with_ovs_agent()
elif self.host_desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE:
self.setup_host_with_linuxbridge_agent()
if self.host_desc.l3_agent:
self.l3_agent = self.useFixture(
process.L3AgentFixture(
self.env_desc, self.host_desc,
self.test_name,
self.neutron_config,
self.l3_agent_cfg_fixture))
def setup_host_with_ovs_agent(self):
agent_cfg_fixture = config.OVSConfigFixture( agent_cfg_fixture = config.OVSConfigFixture(
self.env_desc, self.host_desc, self.neutron_config.temp_dir, self.env_desc, self.host_desc, self.neutron_config.temp_dir,
self.local_ip) self.local_ip)
@ -103,21 +127,62 @@ class Host(fixtures.Fixture):
self.test_name, self.neutron_config, agent_cfg_fixture)) self.test_name, self.neutron_config, agent_cfg_fixture))
if self.host_desc.l3_agent: if self.host_desc.l3_agent:
l3_agent_cfg_fixture = self.useFixture( self.l3_agent_cfg_fixture = self.useFixture(
config.L3ConfigFixture( config.L3ConfigFixture(
self.env_desc, self.host_desc, self.env_desc, self.host_desc,
self.neutron_config.temp_dir, self.neutron_config.temp_dir,
self.ovs_agent.agent_cfg_fixture.get_br_int_name())) self.ovs_agent.agent_cfg_fixture.get_br_int_name()))
br_ex = self.useFixture( br_ex = self.useFixture(
net_helpers.OVSBridgeFixture( net_helpers.OVSBridgeFixture(
l3_agent_cfg_fixture.get_external_bridge())).bridge self.l3_agent_cfg_fixture.get_external_bridge())).bridge
self.connect_to_external_network(br_ex) self.connect_to_external_network(br_ex)
self.l3_agent = self.useFixture(
process.L3AgentFixture( def setup_host_with_linuxbridge_agent(self):
#First we need to provide connectivity for agent to prepare proper
#bridge mappings in agent's config:
self.host_namespace = self.useFixture(
net_helpers.NamespaceFixture(prefix="host-")
).name
self.connect_namespace_to_control_network()
agent_cfg_fixture = config.LinuxBridgeConfigFixture(
self.env_desc, self.host_desc,
self.neutron_config.temp_dir,
self.local_ip,
physical_device_name=self.host_port.name
)
self.useFixture(agent_cfg_fixture)
self.linuxbridge_agent = self.useFixture(
process.LinuxBridgeAgentFixture(
self.env_desc, self.host_desc,
self.test_name, self.neutron_config, agent_cfg_fixture,
namespace=self.host_namespace
)
)
if self.host_desc.l3_agent:
self.l3_agent_cfg_fixture = self.useFixture(
config.L3ConfigFixture(
self.env_desc, self.host_desc, self.env_desc, self.host_desc,
self.test_name, self.neutron_config.temp_dir))
self.neutron_config,
l3_agent_cfg_fixture)) def _connect_ovs_port(self, cidr_address):
ovs_device = self.useFixture(
net_helpers.OVSPortFixture(
bridge=self.central_data_bridge,
namespace=self.host_namespace)).port
# NOTE: This sets an IP address on the host's root namespace
# which is cleaned up when the device is deleted.
ovs_device.addr.add(cidr_address)
return ovs_device
def connect_namespace_to_control_network(self):
self.host_port = self._connect_ovs_port(
common_utils.ip_to_cidr(self.local_ip, 24)
)
self.host_port.link.set_up()
def connect_to_internal_network_via_tunneling(self): def connect_to_internal_network_via_tunneling(self):
veth_1, veth_2 = self.useFixture( veth_1, veth_2 = self.useFixture(
@ -140,6 +205,30 @@ class Host(fixtures.Fixture):
net_helpers.create_patch_ports( net_helpers.create_patch_ports(
self.central_external_bridge, host_external_bridge) self.central_external_bridge, host_external_bridge)
def allocate_local_ip(self):
if not self.env_desc.network_range:
return self.get_random_ip('240.0.0.1', '240.255.255.254')
return self.get_random_ip(
str(self.env_desc.network_range[2]),
str(self.env_desc.network_range[-1])
)
def get_bridge(self, network_id):
if "ovs" in self.agents.keys():
return self.ovs_agent.br_int
elif "linuxbridge" in self.agents.keys():
bridge = self.network_bridges.get(network_id, None)
if not bridge:
br_prefix = lb_agent.LinuxBridgeManager.get_bridge_name(
network_id)
bridge = self.useFixture(
net_helpers.LinuxBridgeFixture(
prefix=br_prefix,
namespace=self.host_namespace,
prefix_is_full_name=True)).bridge
self.network_bridges[network_id] = bridge
return bridge
@staticmethod @staticmethod
def get_random_ip(low, high): def get_random_ip(low, high):
parent_range = netaddr.IPRange(low, high) parent_range = netaddr.IPRange(low, high)
@ -165,6 +254,14 @@ class Host(fixtures.Fixture):
def ovs_agent(self, agent): def ovs_agent(self, agent):
self.agents['ovs'] = agent self.agents['ovs'] = agent
@property
def linuxbridge_agent(self):
return self.agents['linuxbridge']
@linuxbridge_agent.setter
def linuxbridge_agent(self, agent):
self.agents['linuxbridge'] = agent
class Environment(fixtures.Fixture): class Environment(fixtures.Fixture):
"""Represents a deployment topology. """Represents a deployment topology.
@ -215,12 +312,21 @@ class Environment(fixtures.Fixture):
def _setUp(self): def _setUp(self):
self.temp_dir = self.useFixture(fixtures.TempDir()).path self.temp_dir = self.useFixture(fixtures.TempDir()).path
#we need this bridge before rabbit and neutron service will start
self.central_data_bridge = self.useFixture(
net_helpers.OVSBridgeFixture('cnt-data')).bridge
self.central_external_bridge = self.useFixture(
net_helpers.OVSBridgeFixture('cnt-ex')).bridge
#Get rabbitmq address (and cnt-data network)
rabbitmq_ip_address = self._configure_port_for_rabbitmq()
self.rabbitmq_environment = self.useFixture( self.rabbitmq_environment = self.useFixture(
process.RabbitmqEnvironmentFixture()) process.RabbitmqEnvironmentFixture(host=rabbitmq_ip_address)
)
plugin_cfg_fixture = self.useFixture( plugin_cfg_fixture = self.useFixture(
config.ML2ConfigFixture( config.ML2ConfigFixture(
self.env_desc, None, self.temp_dir, self.env_desc, self.hosts_desc, self.temp_dir,
self.env_desc.network_type)) self.env_desc.network_type))
neutron_cfg_fixture = self.useFixture( neutron_cfg_fixture = self.useFixture(
config.NeutronConfigFixture( config.NeutronConfigFixture(
@ -231,11 +337,34 @@ class Environment(fixtures.Fixture):
self.env_desc, None, self.env_desc, None,
self.test_name, neutron_cfg_fixture, plugin_cfg_fixture)) self.test_name, neutron_cfg_fixture, plugin_cfg_fixture))
self.central_data_bridge = self.useFixture(
net_helpers.OVSBridgeFixture('cnt-data')).bridge
self.central_external_bridge = self.useFixture(
net_helpers.OVSBridgeFixture('cnt-ex')).bridge
self.hosts = [self._create_host(desc) for desc in self.hosts_desc] self.hosts = [self._create_host(desc) for desc in self.hosts_desc]
self.wait_until_env_is_up() self.wait_until_env_is_up()
def _configure_port_for_rabbitmq(self):
self.env_desc.network_range = self._get_network_range()
if not self.env_desc.network_range:
return "127.0.0.1"
rabbitmq_ip = str(self.env_desc.network_range[1])
rabbitmq_port = ip_lib.IPDevice(self.central_data_bridge.br_name)
rabbitmq_port.addr.add(common_utils.ip_to_cidr(rabbitmq_ip, 24))
rabbitmq_port.link.set_up()
return rabbitmq_ip
def _get_network_range(self):
#NOTE(slaweq): We need to choose IP address on which rabbitmq will be
# available because LinuxBridge agents are spawned in their own
# namespaces and need to know where the rabbitmq server is listening.
# For ovs agent it is not necessary because agents are spawned in
# globalscope together with rabbitmq server so default localhost
# address is fine for them
for desc in self.hosts_desc:
if desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE:
return self.get_random_network(
"240.0.0.0", "240.255.255.255", "24")
@staticmethod
def get_random_network(low, high, netmask):
ip = Host.get_random_ip(low, high)
return netaddr.IPNetwork("%s/%s" % (ip, netmask))

View File

@ -20,11 +20,11 @@ from neutron.tests.common import net_helpers
class FakeFullstackMachine(machine_fixtures.FakeMachineBase): class FakeFullstackMachine(machine_fixtures.FakeMachineBase):
def __init__(self, host, network_id, tenant_id, safe_client, def __init__(self, host, network_id, tenant_id, safe_client,
neutron_port=None): neutron_port=None):
super(FakeFullstackMachine, self).__init__() super(FakeFullstackMachine, self).__init__()
self.bridge = host.ovs_agent.br_int self.host = host
self.host_binding = host.hostname
self.tenant_id = tenant_id self.tenant_id = tenant_id
self.network_id = network_id self.network_id = network_id
self.safe_client = safe_client self.safe_client = safe_client
@ -33,18 +33,19 @@ class FakeFullstackMachine(machine_fixtures.FakeMachineBase):
def _setUp(self): def _setUp(self):
super(FakeFullstackMachine, self)._setUp() super(FakeFullstackMachine, self)._setUp()
self.bridge = self.host.get_bridge(self.network_id)
if not self.neutron_port: if not self.neutron_port:
self.neutron_port = self.safe_client.create_port( self.neutron_port = self.safe_client.create_port(
network_id=self.network_id, network_id=self.network_id,
tenant_id=self.tenant_id, tenant_id=self.tenant_id,
hostname=self.host_binding) hostname=self.host.hostname)
self.neutron_port_id = self.neutron_port['id']
mac_address = self.neutron_port['mac_address'] mac_address = self.neutron_port['mac_address']
self.port = self.useFixture( self.port = self.useFixture(
net_helpers.PortFixture.get( net_helpers.PortFixture.get(
self.bridge, self.namespace, mac_address, self.bridge, self.namespace, mac_address,
self.neutron_port_id)).port self.neutron_port['id'])).port
self._ip = self.neutron_port['fixed_ips'][0]['ip_address'] self._ip = self.neutron_port['fixed_ips'][0]['ip_address']
subnet_id = self.neutron_port['fixed_ips'][0]['subnet_id'] subnet_id = self.neutron_port['fixed_ips'][0]['subnet_id']
@ -69,6 +70,6 @@ class FakeFullstackMachine(machine_fixtures.FakeMachineBase):
def block_until_boot(self): def block_until_boot(self):
utils.wait_until_true( utils.wait_until_true(
lambda: (self.safe_client.client.show_port(self.neutron_port_id) lambda: (self.safe_client.client.show_port(self.neutron_port['id'])
['port']['status'] == 'ACTIVE'), ['port']['status'] == 'ACTIVE'),
sleep=3) sleep=3)

View File

@ -36,7 +36,7 @@ DEFAULT_LOG_DIR = '/tmp/dsvm-fullstack-logs/'
class ProcessFixture(fixtures.Fixture): class ProcessFixture(fixtures.Fixture):
def __init__(self, test_name, process_name, exec_name, config_filenames, def __init__(self, test_name, process_name, exec_name, config_filenames,
kill_signal=signal.SIGKILL): namespace=None, kill_signal=signal.SIGKILL):
super(ProcessFixture, self).__init__() super(ProcessFixture, self).__init__()
self.test_name = test_name self.test_name = test_name
self.process_name = process_name self.process_name = process_name
@ -44,6 +44,7 @@ class ProcessFixture(fixtures.Fixture):
self.config_filenames = config_filenames self.config_filenames = config_filenames
self.process = None self.process = None
self.kill_signal = kill_signal self.kill_signal = kill_signal
self.namespace = namespace
def _setUp(self): def _setUp(self):
self.start() self.start()
@ -62,7 +63,10 @@ class ProcessFixture(fixtures.Fixture):
'--log-file', log_file] '--log-file', log_file]
for filename in self.config_filenames: for filename in self.config_filenames:
cmd += ['--config-file', filename] cmd += ['--config-file', filename]
self.process = async_process.AsyncProcess(cmd) run_as_root = bool(self.namespace)
self.process = async_process.AsyncProcess(
cmd, run_as_root=run_as_root, namespace=self.namespace
)
self.process.start(block=True) self.process.start(block=True)
def stop(self): def stop(self):
@ -71,6 +75,10 @@ class ProcessFixture(fixtures.Fixture):
class RabbitmqEnvironmentFixture(fixtures.Fixture): class RabbitmqEnvironmentFixture(fixtures.Fixture):
def __init__(self, host="127.0.0.1"):
super(RabbitmqEnvironmentFixture, self).__init__()
self.host = host
def _setUp(self): def _setUp(self):
self.user = base.get_rand_name(prefix='user') self.user = base.get_rand_name(prefix='user')
self.password = base.get_rand_name(prefix='pass') self.password = base.get_rand_name(prefix='pass')
@ -97,6 +105,7 @@ class NeutronServerFixture(fixtures.Fixture):
def __init__(self, env_desc, host_desc, def __init__(self, env_desc, host_desc,
test_name, neutron_cfg_fixture, plugin_cfg_fixture): test_name, neutron_cfg_fixture, plugin_cfg_fixture):
super(NeutronServerFixture, self).__init__()
self.env_desc = env_desc self.env_desc = env_desc
self.host_desc = host_desc self.host_desc = host_desc
self.test_name = test_name self.test_name = test_name
@ -136,6 +145,7 @@ class OVSAgentFixture(fixtures.Fixture):
def __init__(self, env_desc, host_desc, def __init__(self, env_desc, host_desc,
test_name, neutron_cfg_fixture, agent_cfg_fixture): test_name, neutron_cfg_fixture, agent_cfg_fixture):
super(OVSAgentFixture, self).__init__()
self.env_desc = env_desc self.env_desc = env_desc
self.host_desc = host_desc self.host_desc = host_desc
self.test_name = test_name self.test_name = test_name
@ -161,31 +171,69 @@ class OVSAgentFixture(fixtures.Fixture):
config_filenames=config_filenames)) config_filenames=config_filenames))
class LinuxBridgeAgentFixture(fixtures.Fixture):
NEUTRON_LINUXBRIDGE_AGENT = "neutron-linuxbridge-agent"
def __init__(self, env_desc, host_desc, test_name,
neutron_cfg_fixture, agent_cfg_fixture,
namespace=None):
super(LinuxBridgeAgentFixture, self).__init__()
self.env_desc = env_desc
self.host_desc = host_desc
self.test_name = test_name
self.neutron_cfg_fixture = neutron_cfg_fixture
self.neutron_config = self.neutron_cfg_fixture.config
self.agent_cfg_fixture = agent_cfg_fixture
self.agent_config = agent_cfg_fixture.config
self.namespace = namespace
def _setUp(self):
config_filenames = [self.neutron_cfg_fixture.filename,
self.agent_cfg_fixture.filename]
self.process_fixture = self.useFixture(
ProcessFixture(
test_name=self.test_name,
process_name=self.NEUTRON_LINUXBRIDGE_AGENT,
exec_name=self.NEUTRON_LINUXBRIDGE_AGENT,
config_filenames=config_filenames,
namespace=self.namespace
)
)
class L3AgentFixture(fixtures.Fixture): class L3AgentFixture(fixtures.Fixture):
NEUTRON_L3_AGENT = "neutron-l3-agent" NEUTRON_L3_AGENT = "neutron-l3-agent"
def __init__(self, env_desc, host_desc, def __init__(self, env_desc, host_desc, test_name,
test_name, neutron_cfg_fixture, l3_agent_cfg_fixture): neutron_cfg_fixture, l3_agent_cfg_fixture,
namespace=None):
super(L3AgentFixture, self).__init__() super(L3AgentFixture, self).__init__()
self.env_desc = env_desc self.env_desc = env_desc
self.host_desc = host_desc self.host_desc = host_desc
self.test_name = test_name self.test_name = test_name
self.neutron_cfg_fixture = neutron_cfg_fixture self.neutron_cfg_fixture = neutron_cfg_fixture
self.l3_agent_cfg_fixture = l3_agent_cfg_fixture self.l3_agent_cfg_fixture = l3_agent_cfg_fixture
self.namespace = namespace
def _setUp(self): def _setUp(self):
self.plugin_config = self.l3_agent_cfg_fixture.config self.plugin_config = self.l3_agent_cfg_fixture.config
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(
test_name=self.test_name, ProcessFixture(
process_name=self.NEUTRON_L3_AGENT, test_name=self.test_name,
exec_name=spawn.find_executable( process_name=self.NEUTRON_L3_AGENT,
'l3_agent.py', exec_name=spawn.find_executable(
path=os.path.join(base.ROOTDIR, 'common', 'agents')), 'l3_agent.py',
config_filenames=config_filenames)) path=os.path.join(base.ROOTDIR, 'common', 'agents')),
config_filenames=config_filenames,
namespace=self.namespace
)
)
def get_namespace_suffix(self): def get_namespace_suffix(self):
return self.plugin_config.DEFAULT.test_namespace_suffix return self.plugin_config.DEFAULT.test_namespace_suffix

View File

@ -15,6 +15,7 @@
from oslo_utils import uuidutils from oslo_utils import uuidutils
import testscenarios import testscenarios
from neutron.common import constants
from neutron.tests.fullstack import base from neutron.tests.fullstack import base
from neutron.tests.fullstack.resources import environment from neutron.tests.fullstack.resources import environment
from neutron.tests.fullstack.resources import machine from neutron.tests.fullstack.resources import machine
@ -23,20 +24,7 @@ from neutron.tests.fullstack.resources import machine
load_tests = testscenarios.load_tests_apply_scenarios load_tests = testscenarios.load_tests_apply_scenarios
class TestConnectivitySameNetwork(base.BaseFullStackTestCase): class BaseConnectivitySameNetworkTest(base.BaseFullStackTestCase):
network_scenarios = [
('VXLAN', {'network_type': 'vxlan',
'l2_pop': False}),
('GRE and l2pop', {'network_type': 'gre',
'l2_pop': True}),
('VLANs', {'network_type': 'vlan',
'l2_pop': False})]
interface_scenarios = [
('Ofctl', {'of_interface': 'ovs-ofctl'}),
('Native', {'of_interface': 'native'})]
scenarios = testscenarios.multiply_scenarios(
network_scenarios, interface_scenarios)
def setUp(self): def setUp(self):
host_descriptions = [ host_descriptions = [
@ -45,15 +33,16 @@ 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(3)] of_interface=self.of_interface,
l2_agent_type=self.l2_agent_type) for _ in range(3)]
env = environment.Environment( env = environment.Environment(
environment.EnvironmentDescription( environment.EnvironmentDescription(
network_type=self.network_type, network_type=self.network_type,
l2_pop=self.l2_pop), l2_pop=self.l2_pop),
host_descriptions) host_descriptions)
super(TestConnectivitySameNetwork, self).setUp(env) super(BaseConnectivitySameNetworkTest, self).setUp(env)
def test_connectivity(self): def _test_connectivity(self):
tenant_uuid = uuidutils.generate_uuid() tenant_uuid = uuidutils.generate_uuid()
network = self.safe_client.create_network(tenant_uuid) network = self.safe_client.create_network(tenant_uuid)
@ -75,3 +64,40 @@ class TestConnectivitySameNetwork(base.BaseFullStackTestCase):
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[0].block_until_ping(vms[2].ip)
vms[1].block_until_ping(vms[2].ip) vms[1].block_until_ping(vms[2].ip)
class TestOvsConnectivitySameNetwork(BaseConnectivitySameNetworkTest):
l2_agent_type = constants.AGENT_TYPE_OVS
network_scenarios = [
('VXLAN', {'network_type': 'vxlan',
'l2_pop': False}),
('GRE and l2pop', {'network_type': 'gre',
'l2_pop': True}),
('VLANs', {'network_type': 'vlan',
'l2_pop': False})]
interface_scenarios = [
('Ofctl', {'of_interface': 'ovs-ofctl'}),
('Native', {'of_interface': 'native'})]
scenarios = testscenarios.multiply_scenarios(
network_scenarios, interface_scenarios)
def test_connectivity(self):
self._test_connectivity()
class TestLinuxBridgeConnectivitySameNetwork(BaseConnectivitySameNetworkTest):
l2_agent_type = constants.AGENT_TYPE_LINUXBRIDGE
scenarios = [
('VXLAN', {'network_type': 'vxlan',
'l2_pop': False}),
('VLANs', {'network_type': 'vlan',
'l2_pop': False}),
('VXLAN and l2pop', {'network_type': 'vxlan',
'l2_pop': True})
]
of_interface = None
def test_connectivity(self):
self._test_connectivity()

View File

@ -245,6 +245,15 @@ function _install_post_devstack {
} }
function _configure_iptables_rules {
# For linuxbridge agent fullstack tests we need to add special rules to
# iptables for connection of agents to rabbitmq:
CHAIN_NAME="openstack-INPUT"
sudo iptables -n --list $CHAIN_NAME 1> /dev/null 2>&1 || CHAIN_NAME="INPUT"
sudo iptables -I $CHAIN_NAME -s 240.0.0.0/8 -p tcp -m tcp -d 240.0.0.0/8 --dport 5672 -j ACCEPT
}
function configure_host_for_func_testing { function configure_host_for_func_testing {
echo_summary "Configuring host for functional testing" echo_summary "Configuring host for functional testing"
@ -270,3 +279,7 @@ if [[ "$IS_GATE" != "True" ]]; then
configure_host_for_func_testing configure_host_for_func_testing
fi fi
fi fi
if [[ "$VENV" =~ "dsvm-fullstack" ]]; then
_configure_iptables_rules
fi