jskunda c2eb890259 Replace dpkt library with scapy
Since dpkt is not provided in RDO packages we need to replace it
with other library. In this patch dpkt library is replaced by scapy.

Change-Id: Ifd3348bf183e35f40a8f13e97c1936faf20ce705
2023-09-19 15:15:05 +02:00

138 lines
5.0 KiB
Python

# Copyright 2019 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 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
CONF = config.CONF
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 = CONF.whitebox_options.capture_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('sudo mktemp').rstrip()
cmd = 'sudo timeout {} tcpdump -s0 -Uni {} {} -w {}'.format(
self.timeout, interface, self.filter_str,
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
def cleanup(self):
self.stop()
if self.capture_files:
if utils.host_responds_to_ping(self.client.host):
self.client.exec_command(
'sudo rm -f ' + ' '.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 = 'sudo tcpdump -r {} | wc -l'
for cap_file in self.capture_files:
if 0 < int(self.client.exec_command(
print_pcap_file_cmd.format(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(
'sudo mktemp').rstrip()
n_retries = 5
for i in range(n_retries):
try:
self.client.exec_command(
'sudo tcpslice -w {} {}'.format(
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('sudo cat ' + merged_cap_file)
self.addCleanup(ssh_channel.close)
return ssh_channel.makefile()