Merge "Fix DVR tests that recreate fip and live migrate vm"

This commit is contained in:
Zuul 2024-05-01 00:23:15 +00:00 committed by Gerrit Code Review
commit b01efcdb05
5 changed files with 83 additions and 83 deletions

View File

@ -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

View File

@ -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()

View File

@ -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',

View File

@ -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
<vlanid> 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:

View File

@ -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,