From 5dcad1d7c724814d921deaaaf5d469913115f0b9 Mon Sep 17 00:00:00 2001 From: Alexey Stupnikov Date: Tue, 17 Oct 2023 16:34:27 +0200 Subject: [PATCH 1/7] Don't set port capabilities for OVS HW offloading Nova will automatically translate VF capabilities to Neutron port binding profiles after patch [1] will be merged. Existing recommendations in "admin/config-ovs-offload.html" should be updated: there is no need to define capabilities in port binding profiles for new ports anymore. [1] https://review.opendev.org/c/openstack/nova/+/899229 Related-bug: #2020813 Depends-on: https://review.opendev.org/c/openstack/nova/+/899229 Change-Id: I63b0641f6b7ef0e1190f421a90619bb2971d0d44 (cherry picked from commit 3fc8d32383feb325f090af5866cad75e0b564e36) (cherry picked from commit 7daf61a0af7327eb46a29805cc93773aa8cd16fd) (cherry picked from commit ba8335375a28aa51a91a90f3ce855996915bfba5) --- doc/source/admin/config-ovs-offload.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/admin/config-ovs-offload.rst b/doc/source/admin/config-ovs-offload.rst index c3709693741..7f5c5b3afd2 100644 --- a/doc/source/admin/config-ovs-offload.rst +++ b/doc/source/admin/config-ovs-offload.rst @@ -355,7 +355,7 @@ Validate Open vSwitch hardware offloading .. code-block:: bash - # openstack port create --network private --vnic-type=direct --binding-profile '{"capabilities": ["switchdev"]}' direct_port1 + # openstack port create --network private --vnic-type=direct direct_port1 #. Create an instance using the direct port on 'First Compute Node' @@ -369,7 +369,7 @@ Validate Open vSwitch hardware offloading .. code-block:: bash - # openstack port create --network private --vnic-type=direct --binding-profile '{"capabilities": ["switchdev"]}' direct_port2 + # openstack port create --network private --vnic-type=direct direct_port2 # openstack server create --flavor m1.small --image mellanox_fedora --nic port-id=direct_port2 vm2 .. note:: From 2de488ab11c4d66087ce86d9f3dac23c1fa3164d Mon Sep 17 00:00:00 2001 From: Bence Romsics Date: Tue, 9 Jan 2024 14:18:45 +0100 Subject: [PATCH 2/7] Set trunk parent port as access port in ovs to avoid loop A non-vlan-transparent trunk parent port (tpt) should only forward untagged frames. Earlier it was configured to forward anything (trunk mode in ovs). This patch changes the trunk mode to access mode and sets the trunk parent's tag explicitly to 0. Change-Id: I4bcfe53fe87d7c9218dd0db9d7224bb323709a21 Closes-Bug: #2048785 (cherry picked from commit 27601f8eead444283e4d1c258298ac5afaff377f) --- .../internals/openvswitch_agent.rst | 34 +++++++++---------- .../openvswitch/agent/trunk_manager.py | 12 +++---- .../openvswitch/agent/test_trunk_manager.py | 8 +++++ ...unk-parent-vlan-mode-9280ff2d45403bde.yaml | 8 +++++ 4 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 releasenotes/notes/bug-2048785-trunk-parent-vlan-mode-9280ff2d45403bde.yaml diff --git a/doc/source/contributor/internals/openvswitch_agent.rst b/doc/source/contributor/internals/openvswitch_agent.rst index 5fb35684156..e0d47d85885 100644 --- a/doc/source/contributor/internals/openvswitch_agent.rst +++ b/doc/source/contributor/internals/openvswitch_agent.rst @@ -494,7 +494,7 @@ The IDs used for bridge and port names are truncated. | tbr-trunk-id | | | | tpt-parent-id spt-subport-id | - | (tag 100) | + | (tag 0) (tag 100) | +-----+-----------------+--------+ | | | | @@ -514,27 +514,27 @@ spi-subport-id: int bridge side of the patch port that implements a subport. Trunk creation ++++++++++++++ -A VM is spawned passing to Nova the port-id of a parent port associated with -a trunk. Neutron will pass to Nova the bridge where to plug the vif as part of the vif details. -The os-vif driver creates the trunk bridge tbr-trunk-id if it does not exist in plug(). -It will create the tap interface tap1 and plug it into tbr-trunk-id setting the parent port ID in the external-ids. -The OVS agent will be monitoring the creation of ports on the trunk bridges. When it detects -that a new port has been created on the trunk bridge, it will do the following: +A VM is spawned passing to Nova the port-id of a parent port associated +with a trunk. Neutron will pass to Nova the bridge where to plug the +vif as part of the vif details. The os-vif driver creates the trunk +bridge tbr-trunk-id if it does not exist in plug(). It will create the +tap interface tap1 and plug it into tbr-trunk-id setting the parent port +ID in the external-ids. The trunk driver will wire the parent port via +a patch port to connect the trunk bridge to the integration bridge: :: - ovs-vsctl add-port tbr-trunk-id tpt-parent-id -- set Interface tpt-parent-id type=patch options:peer=tpi-parent-id - ovs-vsctl add-port br-int tpi-parent-id tag=3 -- set Interface tpi-parent-id type=patch options:peer=tpt-parent-id + ovs-vsctl add-port tbr-trunk-id tpt-parent-id -- set Interface tpt-parent-id type=patch options:peer=tpi-parent-id -- set Port tpt-parent-id vlan_mode=access tag=0 + ovs-vsctl add-port br-int tpi-parent-id -- set Interface tpi-parent-id type=patch options:peer=tpt-parent-id -A patch port is created to connect the trunk bridge to the integration bridge. -tpt-parent-id, the trunk bridge side of the patch is not associated to any -tag. It will carry untagged traffic. -tpi-parent-id, the br-int side the patch port is tagged with VLAN 3. We assume that the -trunk is on network1 that on this host is associated with VLAN 3. -The OVS agent will set the trunk ID in the external-ids of tpt-parent-id and tpi-parent-id. -If the parent port is associated with one or more subports the agent will process them as -described in the next paragraph. +tpt-parent-id, the trunk bridge side of the patch will carry untagged +traffic (vlan_mode=access tag=0). The OVS agent will be monitoring the +creation of ports on the integration bridge. tpi-parent-id, the br-int +side the patch port is tagged with VLAN 3 by ovs-agent. We assume that +the trunk is on network1 that on this host is associated with VLAN 3. +If the parent port is associated with one or more subports the agent +will process them as described in the next paragraph. Subport creation ++++++++++++++++ diff --git a/neutron/services/trunk/drivers/openvswitch/agent/trunk_manager.py b/neutron/services/trunk/drivers/openvswitch/agent/trunk_manager.py index 3dfd4580129..cbd9e19d0c1 100644 --- a/neutron/services/trunk/drivers/openvswitch/agent/trunk_manager.py +++ b/neutron/services/trunk/drivers/openvswitch/agent/trunk_manager.py @@ -91,7 +91,7 @@ def __init__(self, trunk_id, port_id, port_mac=None): self.DEV_PREFIX, port_id) self._transaction = None - def plug(self, br_int): + def plug(self, br_int, tag=0): """Plug patch ports between trunk bridge and given bridge. The method plugs one patch port on the given bridge side using @@ -124,6 +124,9 @@ def plug(self, br_int): self.patch_port_trunk_name)) txn.add(ovsdb.db_set('Interface', self.patch_port_trunk_name, *patch_trunk_attrs)) + txn.add(ovsdb.db_set('Port', self.patch_port_trunk_name, + ('vlan_mode', 'access'), + ('tag', tag))) def unplug(self, bridge): """Unplug the trunk from bridge. @@ -168,12 +171,7 @@ def plug(self, br_int): :param br_int: an integration bridge where peer endpoint of patch port will be created. """ - ovsdb = self.bridge.ovsdb - with ovsdb.transaction() as txn: - super(SubPort, self).plug(br_int) - txn.add(ovsdb.db_set( - "Port", self.patch_port_trunk_name, - ("tag", self.segmentation_id))) + super(SubPort, self).plug(br_int, tag=self.segmentation_id) def unplug(self, bridge): """Unplug the sub port from the bridge. diff --git a/neutron/tests/functional/services/trunk/drivers/openvswitch/agent/test_trunk_manager.py b/neutron/tests/functional/services/trunk/drivers/openvswitch/agent/test_trunk_manager.py index 01fd692c8a0..560da6a69d2 100644 --- a/neutron/tests/functional/services/trunk/drivers/openvswitch/agent/test_trunk_manager.py +++ b/neutron/tests/functional/services/trunk/drivers/openvswitch/agent/test_trunk_manager.py @@ -55,6 +55,14 @@ def test_plug(self): self.trunk.bridge.get_port_name_list()) self.assertIn(self.trunk.patch_port_int_name, self.br_int.get_port_name_list()) + self.assertEqual( + 'access', + self.trunk.bridge.db_get_val( + 'Port', self.trunk.patch_port_trunk_name, 'vlan_mode')) + self.assertEqual( + 0, + self.trunk.bridge.db_get_val( + 'Port', self.trunk.patch_port_trunk_name, 'tag')) def test_plug_failure_doesnt_create_ports(self): with mock.patch.object( diff --git a/releasenotes/notes/bug-2048785-trunk-parent-vlan-mode-9280ff2d45403bde.yaml b/releasenotes/notes/bug-2048785-trunk-parent-vlan-mode-9280ff2d45403bde.yaml new file mode 100644 index 00000000000..725acc3203f --- /dev/null +++ b/releasenotes/notes/bug-2048785-trunk-parent-vlan-mode-9280ff2d45403bde.yaml @@ -0,0 +1,8 @@ +--- +issues: + - | + The fix of `bug 2048785 `_ + only fixes newly created trunk parent ports. If the fix of already existing + trunks is needed, then either delete and re-create the affected trunks + or set tpt ports' vlan_mode and tag manually: + ``ovs-vsctl set Port tpt-... vlan_mode=access tag=0`` From 69bda7333fa2410e5ed13ceaf20a47947da82ddd Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Tue, 5 Dec 2023 11:13:44 +0100 Subject: [PATCH 3/7] [Fullstack] Remove unnecessary tests This patch removes some of the fullstack test cases which aren't really needed because they are either testing some use cases covered already by scenario tests or some other common tests. Removed tests: * TestOvsConnectivitySameNetwork.test_connectivity - basic connectivity test covered by many other test cases, * TestDhcpAgentNoHA.test_dhcp_assignment - basic test checking if network was assigned to the DHCP agent - it's tested by many other tests, * TestLegacyL3Agent.test_namespace_exists - test which only checks that qrouter namespace was created by the L3 agent, not needed really, * TestLegacyL3Agent.test_east_west_traffic - covered already by many scenario test cases, * TestLegacyL3Agent.test_north_south_traffic - covered already by many scenario test cases, * TestBwLimitQoS.test_bw_limit_qos_policy_rule_lifecycle - covered already by neutron-tempest-plugin scenario test, * TestQoSWithL2Population - trivial test which isn't needed really, * SecurityGroupRulesTest.test_security_group_rule_quota - already covered by the neutron-tempest-plugin admin api test cases, * TestSubnet.test_create_subnet_ipv4 - already tested in many scenario test cases, * TestSubnet.test_create_subnet_ipv6_slaac - already tested in tempest scenario test case, * TestTrunkPlugin.test_trunk_lifecycle - already covered by the scenario test from the neutron-tempest-plugin Additionally this patch removes monkeypatching of the init_handler method from the neutron-ovs-agent. It was needed only due to the trunk ports test and is not needed anymore. Conflicts: neutron/tests/fullstack/test_trunk.py Change-Id: Ifa438d30599ad7e627c85c772ffae9ae9226f7ea (cherry picked from commit c1a0ceb86c64e6c312ad680b7051ccb931eb5fe3) --- neutron/tests/fullstack/agents/ovs_agent.py | 17 - neutron/tests/fullstack/test_connectivity.py | 16 - neutron/tests/fullstack/test_dhcp_agent.py | 9 - neutron/tests/fullstack/test_l3_agent.py | 99 ------ neutron/tests/fullstack/test_qos.py | 63 ---- neutron/tests/fullstack/test_securitygroup.py | 15 - neutron/tests/fullstack/test_subnet.py | 21 -- neutron/tests/fullstack/test_trunk.py | 313 ------------------ 8 files changed, 553 deletions(-) delete mode 100644 neutron/tests/fullstack/test_trunk.py diff --git a/neutron/tests/fullstack/agents/ovs_agent.py b/neutron/tests/fullstack/agents/ovs_agent.py index 6f73ca78c8e..565dea111d8 100755 --- a/neutron/tests/fullstack/agents/ovs_agent.py +++ b/neutron/tests/fullstack/agents/ovs_agent.py @@ -22,25 +22,9 @@ from neutron.agent.common import polling from neutron.agent.l2.extensions import qos as qos_extension from neutron.common import config -from neutron.services.trunk.drivers.openvswitch.agent \ - import driver as trunk_driver from neutron.tests.common.agents import ovs_agent -def monkeypatch_init_handler(): - original_handler = trunk_driver.init_handler - - def new_init_handler(resource, event, trigger, payload=None): - # NOTE(slaweq): make this setup conditional based on server-side - # capabilities for fullstack tests we can assume that server-side - # and agent-side conf are in sync - if "trunk" not in cfg.CONF.service_plugins: - return - original_handler(resource, event, trigger, payload) - - trunk_driver.init_handler = new_init_handler - - def monkeypatch_qos(): mock.patch.object(ovs_lib.OVSBridge, 'clear_bandwidth_qos').start() if "qos" in cfg.CONF.service_plugins: @@ -63,7 +47,6 @@ def main(): # ovs-vswitchd processes for each test will be isolated in separate # namespace config.register_common_config_options() - monkeypatch_init_handler() monkeypatch_qos() monkeypatch_event_filtering() ovs_agent.main() diff --git a/neutron/tests/fullstack/test_connectivity.py b/neutron/tests/fullstack/test_connectivity.py index 6802024ff5f..8db7cc9ab54 100644 --- a/neutron/tests/fullstack/test_connectivity.py +++ b/neutron/tests/fullstack/test_connectivity.py @@ -83,22 +83,6 @@ def _test_connectivity(self): vms.ping_all() -class TestOvsConnectivitySameNetwork(BaseConnectivitySameNetworkTest): - - l2_agent_type = constants.AGENT_TYPE_OVS - scenarios = [ - ('VXLAN', {'network_type': 'vxlan', - 'l2_pop': False}), - ('GRE-l2pop-arp_responder', {'network_type': 'gre', - 'l2_pop': True, - 'arp_responder': True}), - ('VLANs', {'network_type': 'vlan', - 'l2_pop': False})] - - def test_connectivity(self): - self._test_connectivity() - - class TestOvsConnectivitySameNetworkOnOvsBridgeControllerStop( BaseConnectivitySameNetworkTest): diff --git a/neutron/tests/fullstack/test_dhcp_agent.py b/neutron/tests/fullstack/test_dhcp_agent.py index fa8bb6385c9..04702459756 100644 --- a/neutron/tests/fullstack/test_dhcp_agent.py +++ b/neutron/tests/fullstack/test_dhcp_agent.py @@ -91,15 +91,6 @@ class TestDhcpAgentNoHA(BaseDhcpAgentTest): number_of_hosts = 1 agent_down_time = 60 - def test_dhcp_assignment(self): - # First check if network was scheduled to one DHCP agent - dhcp_agents = self.client.list_dhcp_agent_hosting_networks( - self.network['id']) - self.assertEqual(1, len(dhcp_agents['agents'])) - - # And check if IP and gateway config is fine on FakeMachine - self.vm.block_until_dhcp_config_done() - def test_mtu_update(self): # The test case needs access to devices in nested namespaces. ip_lib # doesn't support it, and it's probably unsafe to touch the library for diff --git a/neutron/tests/fullstack/test_l3_agent.py b/neutron/tests/fullstack/test_l3_agent.py index e477e0ddce3..9e42e5b9d97 100644 --- a/neutron/tests/fullstack/test_l3_agent.py +++ b/neutron/tests/fullstack/test_l3_agent.py @@ -16,7 +16,6 @@ import os import time -import netaddr from neutron_lib import constants from neutronclient.common import exceptions from oslo_utils import uuidutils @@ -323,18 +322,6 @@ def setUp(self): host_descriptions) super(TestLegacyL3Agent, self).setUp(env) - def test_namespace_exists(self): - tenant_id = uuidutils.generate_uuid() - - router = self.safe_client.create_router(tenant_id) - network = self.safe_client.create_network(tenant_id) - subnet = self.safe_client.create_subnet( - tenant_id, network['id'], '20.0.0.0/24', gateway_ip='20.0.0.1') - self.safe_client.add_router_interface(router['id'], subnet['id']) - - namespace = self._get_namespace(router['id']) - self.assert_namespace_exists(namespace) - def test_mtu_update(self): tenant_id = uuidutils.generate_uuid() @@ -361,92 +348,6 @@ def test_mtu_update(self): network = self.safe_client.update_network(network['id'], mtu=mtu) common_utils.wait_until_true(lambda: ri_dev.link.mtu == mtu) - def test_east_west_traffic(self): - tenant_id = uuidutils.generate_uuid() - router = self.safe_client.create_router(tenant_id) - - vm1 = self._create_net_subnet_and_vm( - tenant_id, ['20.0.0.0/24', '2001:db8:aaaa::/64'], - self.environment.hosts[0], router) - vm2 = self._create_net_subnet_and_vm( - tenant_id, ['21.0.0.0/24', '2001:db8:bbbb::/64'], - self.environment.hosts[1], router) - - vm1.block_until_ping(vm2.ip) - # Verify ping6 from vm2 to vm1 IPv6 Address - vm2.block_until_ping(vm1.ipv6) - - def test_north_south_traffic(self): - # This function creates an external network which is connected to - # central_bridge and spawns an external_vm on it. - # The external_vm is configured with the gateway_ip (both v4 & v6 - # addresses) of external subnet. Later, it creates a tenant router, - # a tenant network and two tenant subnets (v4 and v6). The tenant - # router is associated with tenant network and external network to - # provide north-south connectivity to the VMs. - # We validate the following in this testcase. - # 1. SNAT support: using ping from tenant VM to external_vm - # 2. Floating IP support: using ping from external_vm to VM floating ip - # 3. IPv6 ext connectivity: using ping6 from tenant vm to external_vm. - tenant_id = uuidutils.generate_uuid() - ext_net, ext_sub = self._create_external_network_and_subnet(tenant_id) - external_vm = self._create_external_vm(ext_net, ext_sub) - # Create an IPv6 subnet in the external network - v6network = self.useFixture( - ip_network.ExclusiveIPNetwork( - "2001:db8:1234::1", "2001:db8:1234::10", "64")).network - # NOTE(ykarel): gateway_ip is explicitly added as iputils package - # requires fix for https://github.com/iputils/iputils/issues/371 - # is not available in CentOS 9-Stream - ext_v6sub = self.safe_client.create_subnet( - tenant_id, ext_net['id'], v6network, gateway_ip='2001:db8:1234::1') - - router = self.safe_client.create_router(tenant_id, - external_network=ext_net['id']) - - # Configure the gateway_ip of external v6subnet on the external_vm. - external_vm.ipv6_cidr = common_utils.ip_to_cidr( - ext_v6sub['gateway_ip'], 64) - - # Configure an IPv6 downstream route to the v6Address of router gw port - for fixed_ip in router['external_gateway_info']['external_fixed_ips']: - if netaddr.IPNetwork(fixed_ip['ip_address']).version == 6: - external_vm.set_default_gateway(fixed_ip['ip_address']) - - vm = self._create_net_subnet_and_vm( - tenant_id, ['20.0.0.0/24', '2001:db8:aaaa::/64'], - self.environment.hosts[1], router) - - # ping external vm to test snat - vm.block_until_ping(external_vm.ip) - - fip = self.safe_client.create_floatingip( - tenant_id, ext_net['id'], vm.ip, vm.neutron_port['id']) - - # ping floating ip from external vm - external_vm.block_until_ping(fip['floating_ip_address']) - - # Verify VM is able to reach the router interface. - vm.block_until_ping(vm.gateway_ipv6) - # Verify north-south connectivity using ping6 to external_vm. - vm.block_until_ping(external_vm.ipv6) - - # Now let's remove and create again phys bridge and check connectivity - # once again - br_phys = self.environment.hosts[0].br_phys - br_phys.destroy() - br_phys.create() - self.environment.hosts[0].connect_to_central_network_via_vlans( - br_phys) - - # ping floating ip from external vm - external_vm.block_until_ping(fip['floating_ip_address']) - - # Verify VM is able to reach the router interface. - vm.block_until_ping(vm.gateway_ipv6) - # Verify north-south connectivity using ping6 to external_vm. - vm.block_until_ping(external_vm.ipv6) - def test_gateway_ip_changed(self): self._test_gateway_ip_changed() diff --git a/neutron/tests/fullstack/test_qos.py b/neutron/tests/fullstack/test_qos.py index 42c57b9f483..75a741c79da 100644 --- a/neutron/tests/fullstack/test_qos.py +++ b/neutron/tests/fullstack/test_qos.py @@ -29,7 +29,6 @@ from neutron.tests.unit import testlib_api from neutron.agent.common import ovs_lib -from neutron.services.qos.drivers.openvswitch import driver as ovs_drv load_tests = testlib_api.module_load_tests @@ -201,47 +200,6 @@ def _restart_agent_and_check_rules_applied(self, policy_id, vm, for direction in list(all_directions): self._wait_for_bw_rule_applied(vm, None, None, direction) - def test_bw_limit_qos_policy_rule_lifecycle(self): - new_limit = BANDWIDTH_LIMIT + 100 - - # Create port with qos policy attached - vm, qos_policy = self._prepare_vm_with_qos_policy( - [functools.partial( - self._add_bw_limit_rule, - BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.direction)]) - bw_rule = qos_policy['rules'][0] - - self._wait_for_bw_rule_applied( - vm, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.direction) - qos_policy_id = qos_policy['id'] - - self.client.delete_bandwidth_limit_rule(bw_rule['id'], qos_policy_id) - self._wait_for_bw_rule_removed(vm, self.direction) - - # Create new rule with no given burst value, in such case ovs and lb - # agent should apply burst value as - # bandwidth_limit * qos_consts.DEFAULT_BURST_RATE - new_expected_burst = self._get_expected_burst_value(new_limit, - self.direction) - new_rule = self.safe_client.create_bandwidth_limit_rule( - self.tenant_id, qos_policy_id, new_limit, direction=self.direction) - self._wait_for_bw_rule_applied( - vm, new_limit, new_expected_burst, self.direction) - - # Update qos policy rule id - self.client.update_bandwidth_limit_rule( - new_rule['id'], qos_policy_id, - body={'bandwidth_limit_rule': {'max_kbps': BANDWIDTH_LIMIT, - 'max_burst_kbps': BANDWIDTH_BURST}}) - self._wait_for_bw_rule_applied( - vm, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.direction) - - # Remove qos policy from port - self.client.update_port( - vm.neutron_port['id'], - body={'port': {'qos_policy_id': None}}) - self._wait_for_bw_rule_removed(vm, self.direction) - def test_bw_limit_direction_change(self): # Create port with qos policy attached, with rule self.direction vm, qos_policy = self._prepare_vm_with_qos_policy( @@ -584,27 +542,6 @@ class TestPacketRateLimitQoSOvs(_TestPacketRateLimitQoS, ] -class TestQoSWithL2Population(base.BaseFullStackTestCase): - scenarios = [ - (constants.AGENT_TYPE_OVS, - {'mech_drivers': 'openvswitch', - 'supported_rules': ovs_drv.SUPPORTED_RULES}), - ] - - def setUp(self): - host_desc = [] # No need to register agents for this test case - env_desc = environment.EnvironmentDescription( - qos=True, l2_pop=True, mech_drivers=self.mech_drivers) - env = environment.Environment(env_desc, host_desc) - super(TestQoSWithL2Population, self).setUp(env) - - def test_supported_qos_rule_types(self): - res = self.client.list_qos_rule_types() - rule_types = {t['type'] for t in res['rule_types']} - expected_rules = set(self.supported_rules) - self.assertEqual(expected_rules, rule_types) - - class TestQoSPolicyIsDefault(base.BaseFullStackTestCase): NAME = 'fs_policy' diff --git a/neutron/tests/fullstack/test_securitygroup.py b/neutron/tests/fullstack/test_securitygroup.py index 12a60646a78..fb9787be351 100644 --- a/neutron/tests/fullstack/test_securitygroup.py +++ b/neutron/tests/fullstack/test_securitygroup.py @@ -669,21 +669,6 @@ def setUp(self): host_descriptions) super(SecurityGroupRulesTest, self).setUp(env) - def test_security_group_rule_quota(self): - project_id = uuidutils.generate_uuid() - quota = self.client.show_quota_details(project_id) - sg_rules_used = quota['quota']['security_group_rule']['used'] - self.assertEqual(0, sg_rules_used) - - self.safe_client.create_security_group(project_id) - quota = self.client.show_quota_details(project_id) - sg_rules_used = quota['quota']['security_group_rule']['used'] - self.safe_client.update_quota(project_id, 'security_group_rule', - sg_rules_used) - - self.assertRaises(nc_exc.OverQuotaClient, - self.safe_client.create_security_group, project_id) - def test_normalized_cidr_in_rule(self): project_id = uuidutils.generate_uuid() sg = self.safe_client.create_security_group(project_id) diff --git a/neutron/tests/fullstack/test_subnet.py b/neutron/tests/fullstack/test_subnet.py index e617dc555e4..b14b95296af 100644 --- a/neutron/tests/fullstack/test_subnet.py +++ b/neutron/tests/fullstack/test_subnet.py @@ -61,27 +61,6 @@ def _create_subnet(self, project_id, network_id, cidr=None, def _show_subnet(self, subnet_id): return self.client.show_subnet(subnet_id) - def test_create_subnet_ipv4(self): - cidr = self.useFixture( - ip_network.ExclusiveIPNetwork( - '240.0.0.0', '240.255.255.255', '24')).network - subnet = self._create_subnet(self._project_id, self._network['id'], - cidr) - subnet = self._show_subnet(subnet['id']) - self.assertEqual(subnet['subnet']['gateway_ip'], - str(netaddr.IPNetwork(cidr).network + 1)) - - def test_create_subnet_ipv6_slaac(self): - cidr = self.useFixture( - ip_network.ExclusiveIPNetwork( - '2001:db8::', '2001:db8::ffff', '64')).network - subnet = self._create_subnet(self._project_id, self._network['id'], - cidr, ipv6_address_mode='slaac', - ipv6_ra_mode='slaac') - subnet = self._show_subnet(subnet['id']) - self.assertEqual(subnet['subnet']['gateway_ip'], - str(netaddr.IPNetwork(cidr).network)) - def test_create_subnet_ipv6_prefix_delegation(self): subnet = self._create_subnet(self._project_id, self._network['id'], None, ipv6_address_mode='slaac', diff --git a/neutron/tests/fullstack/test_trunk.py b/neutron/tests/fullstack/test_trunk.py deleted file mode 100644 index ace42a36520..00000000000 --- a/neutron/tests/fullstack/test_trunk.py +++ /dev/null @@ -1,313 +0,0 @@ -# Copyright 2016 Red Hat, Inc. -# -# 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 functools - -import netaddr -from neutron_lib import constants -from oslo_utils import uuidutils - -from neutron.common import utils -from neutron.services.trunk.drivers.openvswitch.agent import ovsdb_handler -from neutron.services.trunk.drivers.openvswitch.agent import trunk_manager -from neutron.services.trunk.drivers.openvswitch import utils as trunk_ovs_utils -from neutron.tests.fullstack import base -from neutron.tests.fullstack.resources import environment -from neutron.tests.fullstack.resources import machine - - -def trunk_bridge_does_not_exist(trunk_id): - """Return true if trunk bridge for given ID does not exists.""" - bridge = trunk_manager.TrunkBridge(trunk_id) - return not bridge.exists() - - -def make_ip_network(port, network): - """Make an IPNetwork object from port and network. - - Function returns IPNetwork object containing fixed IP address from port - dictionary with prefixlen from network object. - - :param port: Port dictionary returned by Neutron API - :param network: IPNetwork object in which the port's IP will be assigned. - """ - ip_address = netaddr.IPAddress( - port['fixed_ips'][0]['ip_address']) - return netaddr.IPNetwork( - (ip_address.value, network.prefixlen)) - - -class TrunkTestException(Exception): - pass - - -class Network(object): - """A helper class to keep persistent info about assigned addresses.""" - def __init__(self, prefix, network_cidr, tag=None): - self.prefix = prefix - self.network = netaddr.IPNetwork(network_cidr) - self.neutron_network = None - self.neutron_subnet = None - self.tag = tag - # Currently, only vlan is supported. Pass via __init__ once more are - # supported. - self.segmentation_type = 'vlan' - - @property - def cidr(self): - return str(self.network.cidr) - - @property - def gateway(self): - """Return lowest possible IP in the given subnet.""" - return str(netaddr.IPAddress(self.network.first + 1)) - - @property - def id(self): - return self.neutron_network['id'] - - @property - def name(self): - return "%s-network" % self.prefix - - @property - def subnet_name(self): - return "%s-subnet" % self.prefix - - -class TestTrunkPlugin(base.BaseFullStackTestCase): - def setUp(self): - host_desc = [environment.HostDescription( - l3_agent=False, - l2_agent_type=constants.AGENT_TYPE_OVS)] - env_desc = environment.EnvironmentDescription(service_plugins='trunk') - env = environment.Environment(env_desc, host_desc) - super(TestTrunkPlugin, self).setUp(env) - - self.tenant_id = uuidutils.generate_uuid() - self.trunk_network = Network('trunk', '10.0.0.0/24') - self.vlan1_network = Network('vlan1', '192.168.0.0/24', tag=10) - self.vlan2_network = Network('vlan2', '192.168.1.0/24', tag=20) - - self.host = self.environment.hosts[0] - - for network in ( - self.trunk_network, self.vlan1_network, self.vlan2_network): - self.create_network_and_subnet(network) - - def create_network_and_subnet(self, network): - """Create network and subnet resources in Neutron based on network - object. - - The resource names will be -network and -subnet, where - prefix is taken from network object. - - :param network: Network object from this module. - """ - network.neutron_network = self.safe_client.create_network( - self.tenant_id, network.name) - network.neutron_subnet = self.safe_client.create_subnet( - self.tenant_id, - network.id, - cidr=network.cidr, - gateway_ip=network.gateway, - name=network.subnet_name, - enable_dhcp=False) - - def create_vlan_aware_vm(self, trunk_network, vlan_networks): - """Create a fake machine with one untagged port and subports - according vlan_networks parameter. - - :param trunk_network: Instance of Network where trunk port should be - created. - :param vlan_networks: List of Network instances where subports should - be created. - """ - trunk_parent_port = self.safe_client.create_port( - self.tenant_id, trunk_network.id) - - vlan_subports = [ - self.safe_client.create_port(self.tenant_id, vlan_network.id, - mac_address=trunk_parent_port['mac_address']) - for vlan_network in vlan_networks] - - trunk = self.safe_client.create_trunk( - self.tenant_id, - name='mytrunk', - port_id=trunk_parent_port['id'], - sub_ports=[ - {'port_id': vlan_subport['id'], - 'segmentation_type': 'vlan', - 'segmentation_id': vlan_network.tag} - for vlan_subport, vlan_network in zip(vlan_subports, - vlan_networks) - ], - ) - - vm = self.useFixture( - machine.FakeFullstackTrunkMachine( - trunk, - self.host, - trunk_network.id, - self.tenant_id, - self.safe_client, - neutron_port=trunk_parent_port, - bridge_name=trunk_ovs_utils.gen_trunk_br_name(trunk['id']))) - - for port, vlan_network in zip(vlan_subports, vlan_networks): - ip_network = make_ip_network(port, vlan_network.network) - vm.add_vlan_interface( - port['mac_address'], ip_network, vlan_network.tag) - vm.block_until_boot() - - return vm - - def create_vm_in_network(self, network): - """Create a fake machine in given network.""" - return self.useFixture( - machine.FakeFullstackMachine( - self.host, - network.id, - self.tenant_id, - self.safe_client - ) - ) - - def add_subport_to_vm(self, vm, subport_network): - """Add subport from subport_network to given vm. - - :param vm: FakeFullstackMachine instance to with subport should be - added. - :param subport_network: Network object representing network containing - port for subport. - """ - subport = self.safe_client.create_port( - self.tenant_id, subport_network.id, - mac_address=vm.neutron_port['mac_address']) - subport_spec = { - 'port_id': subport['id'], - 'segmentation_type': subport_network.segmentation_type, - 'segmentation_id': subport_network.tag - } - - self.safe_client.trunk_add_subports( - self.tenant_id, vm.trunk['id'], [subport_spec]) - ip_network = make_ip_network(subport, subport_network.network) - vm.add_vlan_interface( - subport['mac_address'], ip_network, subport_network.tag) - - # NOTE(slaweq): As is described in bug - # https://bugs.launchpad.net/neutron/+bug/1687709 when more than one - # different ovs-agent with enabled trunk driver is running at a time it - # might lead to race conditions between them. - # Because of that ovs_agent used for fullstack tests is monkeypatched and - # loads trunk driver only if trunk service plugin is enabled. - # That makes restriction that only a single set of tests with trunk-enabled - # services will run at the same time. - def test_trunk_lifecycle(self): - """Test life-cycle of a fake VM with trunk port. - - This test uses 4 fake machines: - - vlan_aware_vm (A) that is at the beginning connected to a trunk - network and a vlan1 network. - - trunk_network_vm (B) that is connected to the trunk network. - - vlan1_network_vm (C) that is connected to the vlan1 network. - - vlan2_network_vm (D) that is connected to a vlan2 network. - - Scenario steps: - - all the vms from above are created - - A can talk with B (over the trunk network) - - A can talk with C (over the vlan1 network) - - A can not talk with D (no leg on the vlan2 network) - - - subport from the vlan2 network is added to A - - A can now talk with D (over the vlan2 network) - - - subport from the vlan1 network is removed from A - - A can talk with B (over the trunk network) - - A can not talk with C (no leg on the vlan1 network) - - A can talk with D (over the vlan2 network) - - - A is deleted which leads to removal of trunk bridge - - no leftovers like patch ports to the trunk bridge should remain on - an integration bridge - """ - - vlan_aware_vm = self.create_vlan_aware_vm( - self.trunk_network, - [self.vlan1_network] - ) - trunk_id = vlan_aware_vm.trunk['id'] - - # Create helper vms with different networks - trunk_network_vm = self.create_vm_in_network(self.trunk_network) - vlan1_network_vm = self.create_vm_in_network(self.vlan1_network) - vlan2_network_vm = self.create_vm_in_network(self.vlan2_network) - - for vm in trunk_network_vm, vlan1_network_vm, vlan2_network_vm: - vm.block_until_boot() - - # Test connectivity to trunk and subport - vlan_aware_vm.block_until_ping(trunk_network_vm.ip) - vlan_aware_vm.block_until_ping(vlan1_network_vm.ip) - - # Subport for vlan2 hasn't been added yet - vlan_aware_vm.block_until_no_ping(vlan2_network_vm.ip) - - # Add another subport and test - self.add_subport_to_vm(vlan_aware_vm, self.vlan2_network) - vlan_aware_vm.block_until_ping(vlan2_network_vm.ip) - - # Remove the first subport - self.safe_client.trunk_remove_subports( - self.tenant_id, - trunk_id, - [vlan_aware_vm.trunk['sub_ports'][0]]) - - # vlan1_network_vm now shouldn't be able to talk to vlan_aware_vm - vlan_aware_vm.block_until_no_ping(vlan1_network_vm.ip) - - # but trunk and vlan2 should be able to ping - vlan_aware_vm.block_until_ping(trunk_network_vm.ip) - vlan_aware_vm.block_until_ping(vlan2_network_vm.ip) - - # Delete vm and check that patch ports and trunk bridge are gone - vlan_aware_vm.destroy() - bridge_doesnt_exist_predicate = functools.partial( - trunk_bridge_does_not_exist, trunk_id) - utils.wait_until_true( - bridge_doesnt_exist_predicate, - exception=TrunkTestException( - 'Trunk bridge with ID %s has not been removed' % - trunk_id) - ) - - integration_bridge = self.host.get_bridge(None) - no_patch_ports_predicate = functools.partial( - lambda bridge: not ovsdb_handler.bridge_has_service_port(bridge), - integration_bridge, - ) - try: - utils.wait_until_true(no_patch_ports_predicate) - except utils.WaitTimeout: - # Create exception object after timeout to provide up-to-date list - # of interfaces - raise TrunkTestException( - "Integration bridge %s still has following ports while some of" - " them are patch ports for trunk that were supposed to be " - "removed: %s" % ( - integration_bridge.br_name, - integration_bridge.get_iface_name_list() - ) - ) From af83df07f45fc383851118d973e1ad5914b79737 Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Tue, 5 Dec 2023 13:06:48 +0100 Subject: [PATCH 4/7] [Fullstack] Remove test_port_shut_down module This fullstack module contains only one test test_port_shut_down and it is now replaced by the API test in neutron_tempest_plugin (see depends on patch). Depends-On: https://review.opendev.org/c/openstack/neutron-tempest-plugin/+/902638 Change-Id: I79bdc4a48ddbdb7d5872ec9422e97445bf7fa553 (cherry picked from commit fea0e7d0aef6406826d97da494c2ad9a09bf920f) --- .../tests/fullstack/test_port_shut_down.py | 103 ------------------ 1 file changed, 103 deletions(-) delete mode 100644 neutron/tests/fullstack/test_port_shut_down.py diff --git a/neutron/tests/fullstack/test_port_shut_down.py b/neutron/tests/fullstack/test_port_shut_down.py deleted file mode 100644 index ff70649c385..00000000000 --- a/neutron/tests/fullstack/test_port_shut_down.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright 2017 - Nokia -# -# 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 functools - -from neutron.common import utils - -from neutron.tests.fullstack import base -from neutron.tests.fullstack.resources import environment -from neutron.tests.unit import testlib_api -from neutron_lib import constants -from oslo_utils import uuidutils - -load_tests = testlib_api.module_load_tests - - -class PortShutDownTest(base.BaseFullStackTestCase): - # This is a test to confirm the port status - # on shutting down the port administratively. - # The port status should no longer be ACTIVE - # and go to DOWN - - use_dhcp = True - l2_pop = False - arp_responder = False - num_hosts = 1 - - scenarios = [ - (constants.AGENT_TYPE_OVS, - {'l2_agent_type': constants.AGENT_TYPE_OVS}) - ] - - def setUp(self): - host_descriptions = [ - environment.HostDescription( - l2_agent_type=self.l2_agent_type, - dhcp_agent=self.use_dhcp, - ) - for _ in range(self.num_hosts)] - env = environment.Environment( - environment.EnvironmentDescription( - l2_pop=self.l2_pop, - arp_responder=self.arp_responder), - host_descriptions) - super(PortShutDownTest, self).setUp(env) - - def _create_external_network_and_subnet(self, tenant_id): - # This test is not exclusive for the external networks. - # It is only used here to implicitly create a dhcp port - # on the network creation. - network = self.safe_client.create_network( - tenant_id, name='test-public', external=True, network_type='local') - self.safe_client.create_subnet(tenant_id, network['id'], - '240.0.0.0/8', gateway_ip='240.0.0.2') - return network - - def _get_network_dhcp_ports(self, network_id): - return self.client.list_ports(network_id=network_id, - device_owner=constants.DEVICE_OWNER_DHCP)['ports'] - - def _is_port_active(self, port_id): - port = self.client.show_port(port_id)['port'] - return port['status'] == constants.PORT_STATUS_ACTIVE - - def _is_port_down(self, port_id): - port = self.client.show_port(port_id)['port'] - return port['status'] == constants.PORT_STATUS_DOWN - - def test_port_shut_down(self): - tenant_id = uuidutils.generate_uuid() - # Create an external network - network = self._create_external_network_and_subnet(tenant_id) - - # Check if the DHCP port is created - port_created = functools.partial(self._get_network_dhcp_ports, - network['id']) - utils.wait_until_true(port_created) - - # Get the DHCP port - port = self._get_network_dhcp_ports(network['id'])[0] - - # Wait till the changes are reflected to DB - port_status_active_predicate = functools.partial( - self._is_port_active, port['id']) - utils.wait_until_true(port_status_active_predicate) - - # Shut down the port - self.safe_client.update_port(port['id'], admin_state_up=False) - - port_status_down_predicate = functools.partial( - self._is_port_down, port['id']) - utils.wait_until_true(port_status_down_predicate) From 5250b62a8593ac790de486f16fa626264669f684 Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Wed, 6 Dec 2023 11:13:18 +0100 Subject: [PATCH 5/7] [Fullstack] Remove SecurityGroupRulesTest.test_normalized_cidr_in_rule test This fullstack test is now replaced by the API test in neutron_tempest_plugin (see depends on patch). Depends-On: https://review.opendev.org/c/openstack/neutron-tempest-plugin/+/902753 Change-Id: I2d01b6bf65ebb4dd5bf50e3aca2b3c69a2b70d23 (cherry picked from commit ef15700055467e8459b7309a168aeb4eb392acea) --- neutron/tests/fullstack/test_securitygroup.py | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/neutron/tests/fullstack/test_securitygroup.py b/neutron/tests/fullstack/test_securitygroup.py index fb9787be351..321ce6b8bc4 100644 --- a/neutron/tests/fullstack/test_securitygroup.py +++ b/neutron/tests/fullstack/test_securitygroup.py @@ -659,27 +659,3 @@ def _test_overlapping_mac_addresses(self): vm1, vm2, net_helpers.NetcatTester.TCP, port + 1) self.verify_no_connectivity_between_vms( vm2, vm1, net_helpers.NetcatTester.TCP, port + 1) - - -class SecurityGroupRulesTest(base.BaseFullStackTestCase): - - def setUp(self): - host_descriptions = [environment.HostDescription()] - env = environment.Environment(environment.EnvironmentDescription(), - host_descriptions) - super(SecurityGroupRulesTest, self).setUp(env) - - def test_normalized_cidr_in_rule(self): - project_id = uuidutils.generate_uuid() - sg = self.safe_client.create_security_group(project_id) - - rule = self.safe_client.create_security_group_rule( - project_id, sg['id'], direction='ingress', - remote_ip_prefix='10.0.0.34/24') - self.assertEqual('10.0.0.0/24', rule['normalized_cidr']) - self.assertEqual('10.0.0.34/24', rule['remote_ip_prefix']) - - rule = self.safe_client.create_security_group_rule( - project_id, sg['id'], direction='ingress') - self.assertIsNone(rule['normalized_cidr']) - self.assertIsNone(rule['remote_ip_prefix']) From c47c600b6ab4ec2f524f20658bc3513977eb4b88 Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Wed, 6 Dec 2023 11:46:41 +0100 Subject: [PATCH 6/7] [Fullstack] Consolidate segmentation_id update tests into single test There were 3 tests which were testing update of the segmentation_id for the network with: * no ports * unbound or binding failed ports, * bound ports. Due to the nature of the fullstack tests, each of tests required to spawn neutron server, neutron ovs agent, etc. Now all of those tests are consolidated into one test end execution time of test cases from that class went down from about 125 seconds to around 45 seconds. Change-Id: I4c0444700bff734cf1947f0e0a3e44ea0e11a155 (cherry picked from commit 8935b7a133de5dc8e6d92834e74bc3be89fb96af) --- .../tests/fullstack/test_segmentation_id.py | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/neutron/tests/fullstack/test_segmentation_id.py b/neutron/tests/fullstack/test_segmentation_id.py index 61759474b41..76629fb05c6 100644 --- a/neutron/tests/fullstack/test_segmentation_id.py +++ b/neutron/tests/fullstack/test_segmentation_id.py @@ -74,33 +74,32 @@ class TestSegmentationId(BaseSegmentationIdTest): ] num_hosts = 1 - def test_change_segmentation_id_no_ports_in_network(self): - network = self._create_network() - # Now change segmentation_id to some other value - self._update_segmentation_id(network) - - def test_change_segmentation_id_with_unbound_ports_in_network(self): + def test_change_segmentation_id(self): network = self._create_network() + # Now change segmentation_id to some other value when there are no + # ports created in network + network = self._update_segmentation_id(network) self.safe_client.create_subnet( self.project_id, network['id'], '20.0.0.0/24') + # Create some unbound and binding_failed ports # Unbound port self.safe_client.create_port(self.project_id, network['id']) # Port failed to bind self.safe_client.create_port(self.project_id, network['id'], "non-existing-host") - self._update_segmentation_id(network) - - def test_change_segmentation_id_with_bound_ports_in_network(self): - network = self._create_network() + # Test update segmentation_id to some othe value with unbound and + # binding_failed ports created in the network + network = self._update_segmentation_id(network) - self.safe_client.create_subnet( - self.project_id, network['id'], '20.0.0.0/24') + # Create bound port self.safe_client.create_port(self.project_id, network['id'], self.environment.hosts[0].hostname) + # Test update segmentation_id to some othe value when bound ports are + # created in the network self._update_segmentation_id(network) From 1f5ce18a14bc3b3d3884c3dc50daf1c8062605b6 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Fri, 19 Jan 2024 11:41:15 +0000 Subject: [PATCH 7/7] [OVN][FT] Retry in case of timeout when executing "ovsdb-client". The shell command "ovsdb-client", in the functional tests, is prone to timeouts. This patch adds a tenacity decorator and sets the command timeout to 3 seconds, that should be more than enough to retrieve one single register. Closes-Bug: #1955008 Change-Id: I38626835ca809cc3f2894e5f81fab55cf3f40071 (cherry picked from commit 64fddf4f2d18b134b5cc8348049a3c4f10f69a28) --- .../drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py index e8d6665c2e4..5630a8c50eb 100644 --- a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py @@ -13,6 +13,7 @@ # under the License. import datetime import functools +import subprocess from unittest import mock import fixtures as og_fixtures @@ -26,6 +27,7 @@ from oslo_utils import uuidutils from ovsdbapp.backend.ovs_idl import event from ovsdbapp.backend.ovs_idl import idlutils +import tenacity from neutron.common.ovn import constants as ovn_const from neutron.common import utils as n_utils @@ -144,6 +146,10 @@ def _create_fip(self, port, fip_address): 'port_id': port['id']}}) return r1_f2 + @tenacity.retry( + retry=tenacity.retry_if_exception_type(subprocess.TimeoutExpired), + wait=tenacity.wait_exponential(multiplier=0.02, max=1), + reraise=True) def _check_mac_binding_exists(self, macb_id): cmd = ['ovsdb-client', 'transact', self.mech_driver.sb_ovn.connection_string] @@ -156,8 +162,7 @@ def _check_mac_binding_exists(self, macb_id): cmd += ['["OVN_Southbound", {"op": "select", "table": "MAC_Binding", ' '"where": [["_uuid", "==", ["uuid", "%s"]]]}]' % macb_id] - out, _ = processutils.execute(*cmd, - log_errors=False) + out, _ = processutils.execute(*cmd, log_errors=False, timeout=3) return str(macb_id) in out def test_floatingip_mac_bindings(self):