Roman Safronov 43b59d0e0b Fix DVR tests that recreate fip and live migrate vm
This patch adds necessary adjustments to DVR tests that recreate
floating ip and the ones that test OVN DVR after VM live migration
in order to allow them to run properly on a podified
environment.
Fixed tests list:
test_validate_floatingip_compute_ingress_delete_fip_restart_instance
test_dvr_create_delete_fip_restart_instance
test_validate_dvr_connectivity_live_migration_basic
test_validate_dvr_connectivity_live_migration_different_networks

Change-Id: I5f75784ca879161f486d778b4a39c4d0410aad44
2024-04-22 16:37:57 +03:00

163 lines
6.2 KiB
Python

# Copyright 2024 Red Hat, Inc.
# All Rights Reserved.
# 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 re
import time
import fixtures
from oslo_log import log
from scapy.all import ICMP
from scapy.all import rdpcap
from tempest import config
from tempest.lib import exceptions
from whitebox_neutron_tempest_plugin.common import utils
WB_CONF = config.CONF.whitebox_neutron_plugin_options
LOG = log.getLogger(__name__)
class TcpdumpCapture(fixtures.Fixture):
capture_files = None
processes = None
def __init__(self, client, interfaces, filter_str=''):
self.client = client
self.interfaces = [ifc.strip() for ifc in interfaces.split(',')]
self.filter_str = filter_str
self.timeout = WB_CONF.capture_timeout
result = self.client.exec_command(
'which toolbox || echo missing')
if 'missing' in result:
cmd_prefix = "sudo timeout {}"
self.path_prefix = ''
else:
# (rsafrono) on coreos ocp nodes tcpdump is executed via
# toolbox. the toolbox can ask for update, therefore we need
# the 'yes no' to skip updating since it can take some time
cmd_prefix = "yes no | timeout {} toolbox"
# 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):
self.start()
def start(self):
if not self.capture_files:
# mktemp needs to be executed with sudo - otherwise the later
# tcpdump command (run with sudo) fails because the created temp
# file cannot be written
# This happens in RHEL9 because fs.protected_regular is enabled
self.capture_files = []
self.processes = []
for interface in self.interfaces:
process = self.client.open_session()
capture_file = self.client.exec_command(
'mktemp -u').rstrip()
cmd = '{} tcpdump -s0 -Uni {} {} -w {}{}'.format(
self.cmd_prefix, interface, self.filter_str,
self.path_prefix, capture_file)
self.capture_files.append(capture_file)
LOG.debug('Executing command: {}'.format(cmd))
process.exec_command(cmd)
self.processes.append(process)
self.addCleanup(self.cleanup)
def stop(self):
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()
if self.capture_files:
if utils.host_responds_to_ping(self.client.host):
self.client.exec_command(
'{} rm -f '.format(self.cmd_prefix) + ' '.join(
self.capture_files))
self.capture_files = None
def is_empty(self):
try:
pcap = rdpcap(self._open_capture_file())
except Exception as e:
LOG.debug('Error reading pcap file: ', str(e))
return True
for record in pcap:
return False
return True
def get_next_hop_mtu(self):
pcap = rdpcap(self._open_capture_file())
for record in pcap:
if 'IP' in record and 'ICMP' in record:
icmp = record[ICMP]
# ICMP type 3 = Destionation Unreachable
if icmp.type == 3:
return repr(icmp.nexthopmtu)
return None
def _open_capture_file(self):
if not self.capture_files:
raise ValueError('No capture files available')
elif len(self.capture_files) == 1:
merged_cap_file = self.capture_files[0]
else:
cap_file_candidates = []
print_pcap_file_cmd = '{} tcpdump -r {} | wc -l'
for cap_file in self.capture_files:
if 0 < int(self.client.exec_command(
print_pcap_file_cmd.format(
self.cmd_prefix, cap_file)).rstrip()):
# cap files that are not empty
cap_file_candidates.append(cap_file)
if not cap_file_candidates:
# they are all empty
merged_cap_file = self.capture_files[0]
elif 1 == len(cap_file_candidates):
merged_cap_file = cap_file_candidates[0]
else:
merged_cap_file = self.client.exec_command(
'mktemp -u').rstrip()
n_retries = 5
for i in range(n_retries):
try:
self.client.exec_command(
'{} tcpslice -w {} {}'.format(
self.cmd_prefix, merged_cap_file,
' '.join(cap_file_candidates)))
except exceptions.SSHExecCommandFailed as exc:
if i == (n_retries - 1):
raise exc
LOG.warn('tcpslice command failed - retrying...')
time.sleep(5)
else:
break
ssh_channel = self.client.open_session()
ssh_channel.exec_command('cat ' + merged_cap_file)
self.addCleanup(ssh_channel.close)
return ssh_channel.makefile()