diff --git a/whitebox_neutron_tempest_plugin/common/constants.py b/whitebox_neutron_tempest_plugin/common/constants.py index 0beff5a..136e2e6 100644 --- a/whitebox_neutron_tempest_plugin/common/constants.py +++ b/whitebox_neutron_tempest_plugin/common/constants.py @@ -15,6 +15,7 @@ GLOBAL_IP = '1.1.1.1' METADATA_SERVICE_IP = '169.254.169.254' +NODE_TUNNEL_INTERFACE = 'genev_sys_6081' NCAT_PORT = 65000 NCAT_TIMEOUT = 30 IP_HEADER_LENGTH = 20 diff --git a/whitebox_neutron_tempest_plugin/common/tcpdump_capture.py b/whitebox_neutron_tempest_plugin/common/tcpdump_capture.py index e1d282f..3bad21d 100644 --- a/whitebox_neutron_tempest_plugin/common/tcpdump_capture.py +++ b/whitebox_neutron_tempest_plugin/common/tcpdump_capture.py @@ -1,4 +1,4 @@ -# Copyright 2019 Red Hat, Inc. +# Copyright 2024 Red Hat, Inc. # All Rights Reserved. # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -12,6 +12,7 @@ # 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 re import time import fixtures @@ -49,6 +50,9 @@ class TcpdumpCapture(fixtures.Fixture): # the toolbox runs in a container. # host file system is mounted there to /host self.path_prefix = '/host' + # when running tcpdump via the toolbox it is necessary + # to escape symbols like () in tcpdump filters + self.filter_str = re.escape(filter_str) self.cmd_prefix = cmd_prefix.format(self.timeout) def _setUp(self): @@ -80,6 +84,10 @@ class TcpdumpCapture(fixtures.Fixture): for process in (self.processes or []): process.close() self.processes = None + if 'toolbox' in self.client.exec_command( + 'which toolbox || true'): + self.client.exec_command( + "sudo podman stop toolbox-$(whoami) || true") def cleanup(self): self.stop() diff --git a/whitebox_neutron_tempest_plugin/config.py b/whitebox_neutron_tempest_plugin/config.py index 1a5c1ea..be2b3b0 100644 --- a/whitebox_neutron_tempest_plugin/config.py +++ b/whitebox_neutron_tempest_plugin/config.py @@ -123,7 +123,7 @@ WhiteboxNeutronPluginOptions = [ default='br-int', help="OpenvSwitch bridge dedicated for OVN's use."), cfg.StrOpt('ext_bridge', - default='{"default": "br-ex", "alt": "ospbr"}', + default='{"default": "ospbr", "alt": "br-ex"}', help="Bridge dedicated for external network. Dict with values " "for default node and alternative node (if exist)."), cfg.StrOpt('node_ext_interface', diff --git a/whitebox_neutron_tempest_plugin/tests/scenario/base.py b/whitebox_neutron_tempest_plugin/tests/scenario/base.py index 19b4286..1eadee0 100644 --- a/whitebox_neutron_tempest_plugin/tests/scenario/base.py +++ b/whitebox_neutron_tempest_plugin/tests/scenario/base.py @@ -40,6 +40,7 @@ from tempest.lib.common import fixed_network from tempest.lib.common.utils import data_utils from tempest.lib.common.utils import test_utils +from whitebox_neutron_tempest_plugin.common import constants as local_constants from whitebox_neutron_tempest_plugin.common import tcpdump_capture as capture from whitebox_neutron_tempest_plugin.common import utils as local_utils @@ -119,8 +120,8 @@ class BaseTempestWhiteboxTestCase(base.BaseTempestTestCase): def get_external_bridge(self, client): commands = [ - "sudo ovs-vsctl list-br", - "sudo ip -o link show type bridge | cut -d ' ' -f 2 | tr -d ':'"] + "sudo ip -o link show type bridge | cut -d ' ' -f 2 | tr -d ':'", + "sudo ovs-vsctl list-br"] for cmd in commands: result = client.exec_command(cmd).strip().split() if self.ext_bridge['default'] in result: @@ -476,6 +477,10 @@ class BaseTempestWhiteboxTestCase(base.BaseTempestTestCase): if not kwargs.get('security_groups'): kwargs['security_groups'] = [{ 'name': self.security_groups[-1]['name']}] + # (rsafrono) delete scheduler_hints from kwargs if it's set to empty + # dict. This prevents 'scheduler_hints was unexpected' error. + if not kwargs.get('scheduler_hints'): + kwargs.pop("scheduler_hints", None) if exclude_hosts: exclude_hosts_ignored = False if kwargs.get('host') and (kwargs['host'] in exclude_hosts): @@ -1002,7 +1007,7 @@ class TrafficFlowTest(BaseTempestWhiteboxTestCase): node['capture'] = capture.TcpdumpCapture( node['client'], node_interface, filters) self.useFixture(node['capture']) - time.sleep(2) + time.sleep(5) def _stop_captures(self): for node in self.nodes: @@ -1012,12 +1017,7 @@ class TrafficFlowTest(BaseTempestWhiteboxTestCase): self, dst_ip, expected_routing_nodes, expected_macs, ssh_client): """Check that traffic routed as expected within a tenant network Both directions are supported. - Traffic is captured on - CONF.whitebox_neutron_plugin_options.node_tunnel_interface. - Use values: - genev_sys_6081 for OVN - vxlanxx for ML2/OVS with VXLAN tunnels - for ML2/OVS with VLAN tunnels + Traffic is captured on OVN-specific genev_sys_6081 interface :param dst_ip(str): Destination IP address that we check route to :param expected_routing_nodes(list): Hostnames of expected gateways, @@ -1029,7 +1029,7 @@ class TrafficFlowTest(BaseTempestWhiteboxTestCase): (the one that we send traffic from) """ - interface = CONF.whitebox_neutron_plugin_options.node_tunnel_interface + interface = local_constants.NODE_TUNNEL_INTERFACE # create filters if type(expected_macs) is tuple: diff --git a/whitebox_neutron_tempest_plugin/tests/scenario/test_dvr_ovn.py b/whitebox_neutron_tempest_plugin/tests/scenario/test_dvr_ovn.py index 8523641..3959343 100644 --- a/whitebox_neutron_tempest_plugin/tests/scenario/test_dvr_ovn.py +++ b/whitebox_neutron_tempest_plugin/tests/scenario/test_dvr_ovn.py @@ -87,9 +87,9 @@ class OvnDvrBase(base.TrafficFlowTest, base.BaseTempestTestCaseOvn): device_owner=constants.DEVICE_OWNER_ROUTER_GW)['ports'][0] self.chassis_list = self.get_router_gateway_chassis_list( self.router_port['id']) - chassis_name = self.get_router_gateway_chassis_by_id( + self.chassis_name = self.get_router_gateway_chassis_by_id( self.chassis_list[0]) - LOG.debug("router chassis name = {}".format(chassis_name)) + LOG.debug("router chassis name = {}".format(self.chassis_name)) # Since we are going to spawn VMs with 'host' option which # is available only for admin user, we create security group @@ -120,7 +120,7 @@ class OvnDvrBase(base.TrafficFlowTest, base.BaseTempestTestCaseOvn): # We create VMs on compute hosts that are not on the same host # as router gateway port, i.e. the test is capable to work even # on environments that schedule ovn routers on compute nodes - self.exclude_hosts = [chassis_name] + self.exclude_hosts = [self.chassis_name] network_details = self.os_admin.network_client.show_network( self.network['id']) @@ -179,8 +179,6 @@ class OvnDvrTest(OvnDvrBase): ssh_client=self.server_ssh_client) @decorators.idempotent_id('682167ba-6250-4f3c-8fdf-1c768825cb8c') - @testtools.skipIf(WB_CONF.openstack_type == 'podified', - 'Not yet adapted for podified environment') def test_validate_floatingip_compute_ingress_delete_fip_restart_instance( self): """Check that traffic to a VM with a floating ip enters through @@ -221,6 +219,7 @@ class OvnDvrTest(OvnDvrBase): expected_routing_nodes=[self.compute], expected_mac=self.fip_port_mac, ssh_client=self.proxy_host_client) + # Delete fip LOG.debug('Deleting floating ip') self.os_admin.network_client.delete_floatingip( @@ -240,9 +239,9 @@ class OvnDvrTest(OvnDvrBase): # Reboot the server and make sure that routing is still via compute. LOG.debug('Rebooting vm') - self.os_primary.servers_client.reboot_server( + self.os_admin.servers_client.reboot_server( self.server['server']['id'], type='SOFT') - waiters.wait_for_server_status(self.os_primary.servers_client, + waiters.wait_for_server_status(self.os_admin.servers_client, self.server['server']['id'], neutron_constants.SERVER_STATUS_ACTIVE) self.check_north_south_icmp_flow( @@ -299,11 +298,9 @@ class OvnDvrTest(OvnDvrBase): @testtools.skipUnless(CONF.compute.min_compute_nodes > 1, 'Less than 2 compute nodes, skipping multinode ' 'tests.') - @testtools.skipIf(WB_CONF.openstack_type == 'podified', - 'Not yet adapted for podified environment') @decorators.attr(type='slow') @utils.services('compute', 'network') - def test_validate_dvr_connectivity_live_migration(self): + def test_validate_dvr_connectivity_live_migration_basic(self): """Check that after VM migration to a new compute node, traffic is correctly routed through that new node. @@ -342,10 +339,10 @@ class OvnDvrTest(OvnDvrBase): 7. Repeat steps 3 and 4. """ - router = self.create_router_by_client() - self._setup(router=router) + self._setup() server2 = self._create_server( - scheduler_hints={'different_host': self.server['server']['id']}) + exclude_hosts=self.exclude_hosts) + server2_ip = server2['port']['fixed_ips'][0]['ip_address'] server2_mac = server2['port']['mac_address'] server2_fip_ip = server2['fip']['floating_ip_address'] @@ -355,10 +352,8 @@ class OvnDvrTest(OvnDvrBase): CONF.validation.image_ssh_user, pkey=self.keypair['private_key'], proxy_client=self.server_ssh_client) - server2_host_full = self.get_host_for_server( - server2['server']['server']['id']) - server2_host = server2_host_full.split('.')[0] - servers_host_suffix = '.'.join(server2_host_full.split('.')[1:]) + server2_host = self.get_host_for_server( + server2['server']['id']) # verify N/S connection with self.server self.check_north_south_icmp_flow( @@ -384,7 +379,7 @@ class OvnDvrTest(OvnDvrBase): self.check_east_west_icmp_flow( dst_ip=server2_ip, expected_routing_nodes=expected_routing_nodes, - expected_macs=(self.port['mac_address'], server2_mac), + expected_macs=(self.server['port']['mac_address'], server2_mac), ssh_client=self.server_ssh_client) block_migration = (CONF.compute_feature_enabled. @@ -392,24 +387,26 @@ class OvnDvrTest(OvnDvrBase): # migrate self.server self.os_admin.servers_client.live_migrate_server( self.server['server']['id'], host=None, - block_migration=block_migration, disk_over_commit=False) - self.wait_for_server_active(self.server['server']) + block_migration=block_migration) + self.wait_for_server_active( + self.server['server'], client=self.os_admin.servers_client) new_host = self.get_host_for_server( - self.server['server']['id']).split('.')[0] + self.server['server']['id']) self.assertNotEqual(self.compute, new_host, 'Server1 did not migrate') # migrate server2 compute_names = [ node['name'] for node in self.nodes - if node['type'] == 'compute' and node['name'] not in ( + if node['is_compute'] and node['name'] not in ( new_host, server2_host)] - host = '.'.join((random.choice(compute_names), - servers_host_suffix)) + host = random.choice(compute_names) self.os_admin.servers_client.live_migrate_server( - server2['server']['server']['id'], host=host, - block_migration=block_migration, disk_over_commit=False) - self.wait_for_server_active(server2['server']['server']) + server2['server']['id'], host=host, + block_migration=block_migration) + self.wait_for_server_active( + server2['server'], client=self.os_admin.servers_client) + new_server2_host = self.get_host_for_server( - server2['server']['server']['id']).split('.')[0] + server2['server']['id']) self.assertNotEqual(server2_host, new_server2_host, 'Server2 did not migrate') @@ -437,7 +434,7 @@ class OvnDvrTest(OvnDvrBase): self.check_east_west_icmp_flow( dst_ip=server2_ip, expected_routing_nodes=expected_routing_nodes, - expected_macs=(self.port['mac_address'], server2_mac), + expected_macs=(self.server['port']['mac_address'], server2_mac), ssh_client=self.server_ssh_client) @decorators.idempotent_id('609997ab-bffc-40e5-a858-635099df4db9') @@ -446,8 +443,6 @@ class OvnDvrTest(OvnDvrBase): @testtools.skipUnless(CONF.compute.min_compute_nodes > 1, 'Less than 2 compute nodes, skipping multinode ' 'tests.') - @testtools.skipIf(WB_CONF.openstack_type == 'podified', - 'Not yet adapted for podified environment') @decorators.attr(type='slow') @utils.services('compute', 'network') def test_validate_dvr_connectivity_live_migration_different_networks(self): @@ -462,7 +457,6 @@ class OvnDvrTest(OvnDvrBase): secgroup_id=self.security_groups[-1]['id']) self.create_pingable_secgroup_rule( secgroup_id=self.security_groups[-1]['id']) - self.keypair = self.create_keypair() router = self.create_router_by_client() @@ -478,13 +472,13 @@ class OvnDvrTest(OvnDvrBase): subnets.append(self.create_subnet(network=networks[i])) self.create_router_interface(router['id'], subnets[i]['id']) scheduler_hints = ( - {'different_host': servers[0]['server']['server']['id']} + {'different_host': servers[0]['server']['id']} if i > 0 else {}) servers.append(self._create_server( network=networks[i], scheduler_hints=scheduler_hints)) servers_host.append(self.get_host_for_server( - servers[i]['server']['server']['id']).split('.')[0]) + servers[i]['server']['id'])) servers_fip_mac.append(self.get_fip_port_details( servers[i]['fip'])['mac_address']) servers_ssh_client.append(ssh.Client( @@ -525,25 +519,21 @@ class OvnDvrTest(OvnDvrBase): block_migration_for_live_migration) # migrate self.server new_servers_host = [] - servers_host_suffix = '.'.join( - self.get_host_for_server( - servers[0]['server']['server']['id']).split('.')[1:]) for i, server in enumerate(servers): if i < 1: host = None else: compute_names = [ node['name'] for node in self.nodes - if node['type'] == 'compute' and node['name'] not in ( + if node['is_compute'] and node['name'] not in ( new_servers_host[0], servers_host[1])] - host = '.'.join((random.choice(compute_names), - servers_host_suffix)) + host = random.choice(compute_names) self.os_admin.servers_client.live_migrate_server( - server['server']['server']['id'], host=host, - block_migration=block_migration, disk_over_commit=False) - self.wait_for_server_active(server['server']['server']) + server['server']['id'], host=host, + block_migration=block_migration) + self.wait_for_server_active(server['server']) new_servers_host.append(self.get_host_for_server( - server['server']['server']['id']).split('.')[0]) + server['server']['id'])) self.assertNotEqual(servers_host[i], new_servers_host[i], 'Server%d did not migrate' % i) @@ -569,8 +559,6 @@ class OvnDvrTest(OvnDvrBase): ssh_client=servers_ssh_client[0]) @decorators.idempotent_id('0423e5b5-ac6a-4d4a-ad98-b0465e3ad71d') - @testtools.skipIf(WB_CONF.openstack_type == 'podified', - 'Not yet adapted for podified environment') def test_dvr_create_delete_fip_restart_instance(self): """Check that traffic from a VM with a FIP passes through compute node where VM is running and traffic from VM without a FIP passes @@ -612,34 +600,33 @@ class OvnDvrTest(OvnDvrBase): 9. Restart the server and verify that routing has not changed """ - router = self.create_router_by_client() - self._setup(router=router) - # self.server will be used as a proxy server for accessing test_server. - test_server = self._create_server(create_floating_ip=False) + self._setup() + test_server = self._create_server(exclude_hosts=self.exclude_hosts, + create_floating_ip=False) test_server_ip = test_server['port']['fixed_ips'][0]['ip_address'] - test_server_client = ssh.Client(test_server_ip, - CONF.validation.image_ssh_user, - pkey=self.keypair['private_key'], - proxy_client=self.server_ssh_client) - router_port = self.os_admin.network_client.list_ports( - device_id=router['id'], - device_owner=constants.DEVICE_OWNER_ROUTER_GW)['ports'][0] - router_gateway_chassis = self.get_router_gateway_chassis( - router_port['id']) + test_server_client = ssh.Client( + test_server_ip, + CONF.validation.image_ssh_user, + pkey=self.keypair['private_key'], + proxy_client=self.server_ssh_client) + self.check_north_south_icmp_flow( dst_ip=self.gateway_external_ip, - expected_routing_nodes=[router_gateway_chassis], - expected_mac=router_port['mac_address'], + expected_routing_nodes=[self.chassis_name], + expected_mac=self.router_port['mac_address'], ssh_client=test_server_client, ignore_outbound=self.ignore_outbound) # Now add a fip to the test server and make sure that routing now # via compute. LOG.debug('Adding floating ip to source vm') - fip = self.create_floatingip(port=test_server['port']) + + fip = self.os_admin.network_client.create_floatingip( + port_id=test_server['port']['id'], + floating_network_id=CONF.network.public_network_id)['floatingip'] fip_port_mac = self.get_fip_port_details(fip)['mac_address'] test_server_compute = self.get_host_for_server( - test_server['server']['server']['id']).split('.')[0] + test_server['server']['id']) self.check_north_south_icmp_flow( dst_ip=self.gateway_external_ip, expected_routing_nodes=[test_server_compute], @@ -648,18 +635,22 @@ class OvnDvrTest(OvnDvrBase): # Delete fip and make sure that traffic goes via router chassis. LOG.debug('Deleting floating ip from source vm') - self.delete_floatingip(fip) + self.os_admin.network_client.delete_floatingip(fip['id']) + self.check_north_south_icmp_flow( dst_ip=self.gateway_external_ip, - expected_routing_nodes=[router_gateway_chassis], - expected_mac=router_port['mac_address'], + expected_routing_nodes=[self.chassis_name], + expected_mac=self.router_port['mac_address'], ssh_client=test_server_client, ignore_outbound=self.ignore_outbound) # Add a new fip to the test server and make sure that routing is # via compute again. LOG.debug('Adding new floating ip to source vm') - fip = self.create_floatingip(port=test_server['port']) + + fip = self.os_admin.network_client.create_floatingip( + port_id=test_server['port']['id'], + floating_network_id=CONF.network.public_network_id)['floatingip'] fip_port_mac = self.get_fip_port_details(fip)['mac_address'] self.check_north_south_icmp_flow( dst_ip=self.gateway_external_ip, @@ -669,10 +660,10 @@ class OvnDvrTest(OvnDvrBase): # Reboot the server and make sure that routing is still via compute. LOG.debug('Rebooting vm') - self.os_primary.servers_client.reboot_server( - test_server['server']['server']['id'], type='SOFT') - waiters.wait_for_server_status(self.os_primary.servers_client, - test_server['server']['server']['id'], + self.os_admin.servers_client.reboot_server( + test_server['server']['id'], type='SOFT') + waiters.wait_for_server_status(self.os_admin.servers_client, + test_server['server']['id'], neutron_constants.SERVER_STATUS_ACTIVE) self.check_north_south_icmp_flow( dst_ip=self.gateway_external_ip,