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:: 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/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_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) 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..321ce6b8bc4 100644 --- a/neutron/tests/fullstack/test_securitygroup.py +++ b/neutron/tests/fullstack/test_securitygroup.py @@ -659,42 +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_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) - - 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']) 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) 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() - ) - ) 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): 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``