From 2254acef062241b43d7138c981551183fc4a8930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awek=20Kap=C5=82o=C5=84ski?= Date: Mon, 23 Nov 2015 23:14:12 +0000 Subject: [PATCH] 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 Change-Id: I6230d8d09f77bd20674bf6c3be69fbc1627d66f8 Closes-bug: #1518675 --- TESTING.rst | 9 +- .../linuxbridge/agent/common/constants.py | 2 + .../agent/linuxbridge_neutron_agent.py | 12 +- neutron/tests/common/net_helpers.py | 112 ++++++++++-- neutron/tests/fullstack/resources/config.py | 78 +++++++-- .../tests/fullstack/resources/environment.py | 161 ++++++++++++++++-- neutron/tests/fullstack/resources/machine.py | 13 +- neutron/tests/fullstack/resources/process.py | 70 ++++++-- neutron/tests/fullstack/test_connectivity.py | 60 +++++-- tools/configure_for_func_testing.sh | 13 ++ 10 files changed, 450 insertions(+), 80 deletions(-) diff --git a/TESTING.rst b/TESTING.rst index e4c49d62cce..25f5d53e596 100644 --- a/TESTING.rst +++ b/TESTING.rst @@ -225,9 +225,12 @@ neutron/tests/fullstack/test_connectivity.py. Full stack testing can simulate multi node testing by starting an agent 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 -is connected to its own pair of br-int/br-ex, and those bridges are then -interconnected. +OVS/LinuxBridge/DHCP/L3 agents, all configured with the same "host" value. +Each OVS agent is connected to its own pair of br-int/br-ex, and those bridges +are then interconnected. +For LinuxBridge agent each agent is started in its own namespace, called +"host-". Such namespaces are connected with OVS "central" +bridge to eachother. .. image:: images/fullstack_multinode_simulation.png diff --git a/neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py b/neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py index 45c791868a8..e08ee911741 100644 --- a/neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py +++ b/neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py @@ -21,3 +21,5 @@ VXLAN_MCAST = 'multicast_flooding' VXLAN_UCAST = 'unicast_flooding' EXTENSION_DRIVER_TYPE = 'linuxbridge' + +RESOURCE_ID_LENGTH = 11 diff --git a/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py b/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py index 9c7a1d8597a..60085c56c1f 100644 --- a/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py @@ -128,11 +128,13 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase): return None 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: LOG.warning(_LW("Invalid Network ID, will lead to incorrect " "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 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) return subinterface_name - def get_tap_device_name(self, interface_id): + @staticmethod + def get_tap_device_name(interface_id): if not interface_id: LOG.warning(_LW("Invalid Interface ID, will lead to incorrect " "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 def get_vxlan_device_name(self, segmentation_id): diff --git a/neutron/tests/common/net_helpers.py b/neutron/tests/common/net_helpers.py index 3ccd8aa7e1f..00e3007336d 100644 --- a/neutron/tests/common/net_helpers.py +++ b/neutron/tests/common/net_helpers.py @@ -39,6 +39,8 @@ from neutron.agent.linux import ip_lib from neutron.agent.linux import utils from neutron.common import constants as n_const 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.common import base as common_base from neutron.tests import tools @@ -47,11 +49,18 @@ UNDEFINED = object() NS_PREFIX = 'test-' BR_PREFIX = 'test-br' -PORT_PREFIX = 'test-port' +PORT_PREFIX = 'port' VETH0_PREFIX = 'test-veth0' VETH1_PREFIX = 'test-veth1' 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( r'^.*\s+\d+\s+.*:(?P\d+)\s+[0-9:].*') @@ -473,7 +482,7 @@ class VethFixture(fixtures.Fixture): """Create a veth. :ivar ports: created veth ports - :type ports: IPDevice 2-uplet + :type ports: tuple of 2 IPDevice """ def _setUp(self): @@ -508,6 +517,32 @@ class VethFixture(fixtures.Fixture): 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) class PortFixture(fixtures.Fixture): """Create a port. @@ -541,7 +576,7 @@ class PortFixture(fixtures.Fixture): if isinstance(bridge, ovs_lib.OVSBridge): return OVSPortFixture(bridge, namespace, mac, port_id) if isinstance(bridge, bridge_lib.BridgeDevice): - return LinuxBridgePortFixture(bridge, namespace) + return LinuxBridgePortFixture(bridge, namespace, mac, port_id) if isinstance(bridge, VethBridge): return VethPortFixture(bridge, namespace) tools.fail('Unexpected bridge type: %s' % type(bridge)) @@ -578,7 +613,13 @@ class OVSPortFixture(PortFixture): interface_config.register_opts(interface.OPTS) 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( None, self.port_id, @@ -598,22 +639,44 @@ class LinuxBridgeFixture(fixtures.Fixture): :ivar namespace: created bridge namespace :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__() self.prefix = prefix + self.prefix_is_full_name = prefix_is_full_name self.namespace = namespace def _setUp(self): if self.namespace is UNDEFINED: self.namespace = self.useFixture(NamespaceFixture()).name - self.bridge = common_base.create_resource( - self.prefix, - bridge_lib.BridgeDevice.addbr, - namespace=self.namespace) - self.addCleanup(self.bridge.delbr) + self.bridge = self._create_bridge() + self.addCleanup(self.safe_delete) 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): @@ -625,12 +688,29 @@ class LinuxBridgePortFixture(PortFixture): :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): return LinuxBridgeFixture() def _setUp(self): 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 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) 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): diff --git a/neutron/tests/fullstack/resources/config.py b/neutron/tests/fullstack/resources/config.py index 94d95e032ab..472f173f560 100644 --- a/neutron/tests/fullstack/resources/config.py +++ b/neutron/tests/fullstack/resources/config.py @@ -88,7 +88,7 @@ class NeutronConfigFixture(ConfigFixture): 'oslo_messaging_rabbit': { 'rabbit_userid': rabbitmq_environment.user, 'rabbit_password': rabbitmq_environment.password, - 'rabbit_hosts': '127.0.0.1', + 'rabbit_hosts': rabbitmq_environment.host, 'rabbit_virtual_host': rabbitmq_environment.vhost, } }) @@ -114,14 +114,10 @@ class ML2ConfigFixture(ConfigFixture): super(ML2ConfigFixture, self).__init__( 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({ 'ml2': { 'tenant_network_types': tenant_network_types, - 'mechanism_drivers': mechanism_drivers, + 'mechanism_drivers': self.mechanism_drivers, }, 'ml2_type_vlan': { 'network_vlan_ranges': 'physnet1:1000:2999', @@ -138,6 +134,16 @@ class ML2ConfigFixture(ConfigFixture): self.config['ml2']['extension_drivers'] =\ 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): @@ -205,12 +211,59 @@ class OVSConfigFixture(ConfigFixture): 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): - 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__( 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({ 'DEFAULT': { 'l3_agent_manager': ('neutron.agent.l3_agent.' @@ -219,9 +272,14 @@ class L3ConfigFixture(ConfigFixture): 'OVSInterfaceDriver'), 'ovs_integration_bridge': integration_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'), } }) diff --git a/neutron/tests/fullstack/resources/environment.py b/neutron/tests/fullstack/resources/environment.py index 1105e8c1730..51a9980aad0 100644 --- a/neutron/tests/fullstack/resources/environment.py +++ b/neutron/tests/fullstack/resources/environment.py @@ -19,8 +19,12 @@ import netaddr from neutronclient.common import exceptions as nc_exc from oslo_config import cfg +from neutron.agent.linux import ip_lib from neutron.agent.linux import utils +from neutron.common import constants 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.fullstack.resources import config from neutron.tests.fullstack.resources import process @@ -35,6 +39,7 @@ class EnvironmentDescription(object): self.network_type = network_type self.l2_pop = l2_pop self.qos = qos + self.network_range = None @property def tunneling_enabled(self): @@ -47,7 +52,9 @@ class HostDescription(object): What agents should the host spawn? What mode should each agent operate 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.of_interface = of_interface @@ -75,12 +82,29 @@ class Host(fixtures.Fixture): self.test_name = test_name self.neutron_config = neutron_config # 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_external_bridge = central_external_bridge + self.host_namespace = None self.agents = {} + # we need to cache already created "per network" bridges if linuxbridge + # agent is used on host: + self.network_bridges = {} 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( self.env_desc, self.host_desc, self.neutron_config.temp_dir, self.local_ip) @@ -103,21 +127,62 @@ class Host(fixtures.Fixture): self.test_name, self.neutron_config, agent_cfg_fixture)) if self.host_desc.l3_agent: - l3_agent_cfg_fixture = self.useFixture( + self.l3_agent_cfg_fixture = self.useFixture( config.L3ConfigFixture( self.env_desc, self.host_desc, self.neutron_config.temp_dir, self.ovs_agent.agent_cfg_fixture.get_br_int_name())) br_ex = self.useFixture( 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.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.test_name, - self.neutron_config, - l3_agent_cfg_fixture)) + self.neutron_config.temp_dir)) + + 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): veth_1, veth_2 = self.useFixture( @@ -140,6 +205,30 @@ class Host(fixtures.Fixture): net_helpers.create_patch_ports( 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 def get_random_ip(low, high): parent_range = netaddr.IPRange(low, high) @@ -165,6 +254,14 @@ class Host(fixtures.Fixture): def ovs_agent(self, 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): """Represents a deployment topology. @@ -215,12 +312,21 @@ class Environment(fixtures.Fixture): def _setUp(self): 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( - process.RabbitmqEnvironmentFixture()) + process.RabbitmqEnvironmentFixture(host=rabbitmq_ip_address) + ) plugin_cfg_fixture = self.useFixture( config.ML2ConfigFixture( - self.env_desc, None, self.temp_dir, + self.env_desc, self.hosts_desc, self.temp_dir, self.env_desc.network_type)) neutron_cfg_fixture = self.useFixture( config.NeutronConfigFixture( @@ -231,11 +337,34 @@ class Environment(fixtures.Fixture): self.env_desc, None, 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.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)) diff --git a/neutron/tests/fullstack/resources/machine.py b/neutron/tests/fullstack/resources/machine.py index 170678a888b..91e4b2bd8cb 100644 --- a/neutron/tests/fullstack/resources/machine.py +++ b/neutron/tests/fullstack/resources/machine.py @@ -20,11 +20,11 @@ from neutron.tests.common import net_helpers class FakeFullstackMachine(machine_fixtures.FakeMachineBase): + def __init__(self, host, network_id, tenant_id, safe_client, neutron_port=None): super(FakeFullstackMachine, self).__init__() - self.bridge = host.ovs_agent.br_int - self.host_binding = host.hostname + self.host = host self.tenant_id = tenant_id self.network_id = network_id self.safe_client = safe_client @@ -33,18 +33,19 @@ class FakeFullstackMachine(machine_fixtures.FakeMachineBase): def _setUp(self): super(FakeFullstackMachine, self)._setUp() + self.bridge = self.host.get_bridge(self.network_id) + if not self.neutron_port: self.neutron_port = self.safe_client.create_port( network_id=self.network_id, tenant_id=self.tenant_id, - hostname=self.host_binding) - self.neutron_port_id = self.neutron_port['id'] + hostname=self.host.hostname) mac_address = self.neutron_port['mac_address'] self.port = self.useFixture( net_helpers.PortFixture.get( 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'] subnet_id = self.neutron_port['fixed_ips'][0]['subnet_id'] @@ -69,6 +70,6 @@ class FakeFullstackMachine(machine_fixtures.FakeMachineBase): def block_until_boot(self): 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'), sleep=3) diff --git a/neutron/tests/fullstack/resources/process.py b/neutron/tests/fullstack/resources/process.py index b8066b5b3bc..9f14d39669c 100644 --- a/neutron/tests/fullstack/resources/process.py +++ b/neutron/tests/fullstack/resources/process.py @@ -36,7 +36,7 @@ DEFAULT_LOG_DIR = '/tmp/dsvm-fullstack-logs/' class ProcessFixture(fixtures.Fixture): 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__() self.test_name = test_name self.process_name = process_name @@ -44,6 +44,7 @@ class ProcessFixture(fixtures.Fixture): self.config_filenames = config_filenames self.process = None self.kill_signal = kill_signal + self.namespace = namespace def _setUp(self): self.start() @@ -62,7 +63,10 @@ class ProcessFixture(fixtures.Fixture): '--log-file', log_file] for filename in self.config_filenames: 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) def stop(self): @@ -71,6 +75,10 @@ class ProcessFixture(fixtures.Fixture): class RabbitmqEnvironmentFixture(fixtures.Fixture): + def __init__(self, host="127.0.0.1"): + super(RabbitmqEnvironmentFixture, self).__init__() + self.host = host + def _setUp(self): self.user = base.get_rand_name(prefix='user') self.password = base.get_rand_name(prefix='pass') @@ -97,6 +105,7 @@ class NeutronServerFixture(fixtures.Fixture): def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, plugin_cfg_fixture): + super(NeutronServerFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name @@ -136,6 +145,7 @@ class OVSAgentFixture(fixtures.Fixture): def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, agent_cfg_fixture): + super(OVSAgentFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name @@ -161,31 +171,69 @@ class OVSAgentFixture(fixtures.Fixture): 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): NEUTRON_L3_AGENT = "neutron-l3-agent" - def __init__(self, env_desc, host_desc, - test_name, neutron_cfg_fixture, l3_agent_cfg_fixture): + def __init__(self, env_desc, host_desc, test_name, + neutron_cfg_fixture, l3_agent_cfg_fixture, + namespace=None): super(L3AgentFixture, 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.l3_agent_cfg_fixture = l3_agent_cfg_fixture + self.namespace = namespace def _setUp(self): self.plugin_config = self.l3_agent_cfg_fixture.config config_filenames = [self.neutron_cfg_fixture.filename, self.l3_agent_cfg_fixture.filename] - self.process_fixture = self.useFixture(ProcessFixture( - test_name=self.test_name, - process_name=self.NEUTRON_L3_AGENT, - exec_name=spawn.find_executable( - 'l3_agent.py', - path=os.path.join(base.ROOTDIR, 'common', 'agents')), - config_filenames=config_filenames)) + self.process_fixture = self.useFixture( + ProcessFixture( + test_name=self.test_name, + process_name=self.NEUTRON_L3_AGENT, + exec_name=spawn.find_executable( + 'l3_agent.py', + path=os.path.join(base.ROOTDIR, 'common', 'agents')), + config_filenames=config_filenames, + namespace=self.namespace + ) + ) def get_namespace_suffix(self): return self.plugin_config.DEFAULT.test_namespace_suffix diff --git a/neutron/tests/fullstack/test_connectivity.py b/neutron/tests/fullstack/test_connectivity.py index 0db5a3c8cd2..491b2e600f9 100644 --- a/neutron/tests/fullstack/test_connectivity.py +++ b/neutron/tests/fullstack/test_connectivity.py @@ -15,6 +15,7 @@ from oslo_utils import uuidutils import testscenarios +from neutron.common import constants from neutron.tests.fullstack import base from neutron.tests.fullstack.resources import environment 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 -class TestConnectivitySameNetwork(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) +class BaseConnectivitySameNetworkTest(base.BaseFullStackTestCase): def setUp(self): host_descriptions = [ @@ -45,15 +33,16 @@ class TestConnectivitySameNetwork(base.BaseFullStackTestCase): # agent types present on machines. environment.HostDescription( 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( environment.EnvironmentDescription( network_type=self.network_type, l2_pop=self.l2_pop), 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() 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[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() diff --git a/tools/configure_for_func_testing.sh b/tools/configure_for_func_testing.sh index d434af4d096..3bedf0902eb 100755 --- a/tools/configure_for_func_testing.sh +++ b/tools/configure_for_func_testing.sh @@ -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 { echo_summary "Configuring host for functional testing" @@ -270,3 +279,7 @@ if [[ "$IS_GATE" != "True" ]]; then configure_host_for_func_testing fi fi + +if [[ "$VENV" =~ "dsvm-fullstack" ]]; then + _configure_iptables_rules +fi