Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ jobs:
run: cmd/install -l
- name: Run tests for conn module
shell: bash {0}
run: bash testing/unit/run_test_module.sh conn captures ethtool output
run: bash testing/unit/run_test_module.sh conn captures ethtool ifconfig output
- name: Run tests for dns module
shell: bash {0}
run: bash testing/unit/run_test_module.sh dns captures reports output
Expand Down
10 changes: 9 additions & 1 deletion framework/python/src/net_orc/ip_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,22 @@ def get_iface_mac_address(iface):
return addr_info.address
return None

def get_iface_port_stats(self, iface):
def get_iface_ethtool_port_stats(self, iface):
"""Extract information about packets connection"""
response = util.run_command(f'ethtool -S {iface}')
if len(response[1]) == 0:
return response[0]
else:
return None

def get_iface_ifconfig_port_stats(self, iface):
"""Extract information about packets connection"""
response = util.run_command(f'ifconfig -a {iface}')
if len(response[1]) == 0:
return response[0]
else:
return None

def get_ip_address(self, iface):
addrs = psutil.net_if_addrs()
if iface in addrs:
Expand Down
45 changes: 24 additions & 21 deletions framework/python/src/net_orc/network_orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def start_network(self):
self._session.set_status(TestrunStatus.VALIDATING)
self.validator.start()
self.validator.stop()
except Exception as e:
except Exception as e: # pylint: disable=W0703
LOGGER.error(f'Validation failed {e}')

self._session.set_status('Waiting for Device')
Expand Down Expand Up @@ -257,14 +257,21 @@ def _get_conn_stats(self):
def _get_port_stats(self, pre_monitor=True):
""" Extract information about the port statistics
and store it to a file for the conn test module to access"""
suffix = 'pre_monitor' if pre_monitor else 'post_monitor'
dev_int = self._session.get_device_interface()
port_stats = self._ip_ctrl.get_iface_port_stats(dev_int)
if port_stats is not None:
suffix = 'pre_monitor' if pre_monitor else 'post_monitor'
eth_out_file = os.path.join(NET_DIR, f'ethtool_port_stats_{suffix}.txt')
with open(eth_out_file, 'w', encoding='utf-8') as f:
f.write(port_stats)
else:
ethtool_port_stats = self._ip_ctrl.get_iface_ethtool_port_stats(dev_int)
ifconfig_port_stats = self._ip_ctrl.get_iface_ifconfig_port_stats(dev_int)
if ethtool_port_stats is not None:
ethtool_out_file = os.path.join(NET_DIR,
f'ethtool_port_stats_{suffix}.txt')
with open(ethtool_out_file, 'w', encoding='utf-8') as f:
f.write(ethtool_port_stats)
if ifconfig_port_stats is not None:
ifconfig_out_file = os.path.join(NET_DIR,
f'ifconfig_port_stats_{suffix}.txt')
with open(ifconfig_out_file, 'w', encoding='utf-8') as f:
f.write(ifconfig_port_stats)
if ethtool_port_stats is None and ifconfig_port_stats is None:
LOGGER.error('Failed to generate port stats')

def monitor_in_progress(self):
Expand Down Expand Up @@ -307,10 +314,8 @@ def _start_device_monitor(self, device):
time.sleep(1)

# Check Testrun hasn't been cancelled
if self._session.get_status() in (
TestrunStatus.STOPPING,
TestrunStatus.CANCELLED
):
if self._session.get_status() in (TestrunStatus.STOPPING,
TestrunStatus.CANCELLED):
sniffer.stop()
return

Expand Down Expand Up @@ -438,8 +443,8 @@ def load_network_modules(self):

for module_dir in os.listdir(net_modules_dir):

if (self._get_network_module(module_dir) is None and
module_dir != 'template'):
if (self._get_network_module(module_dir) is None
and module_dir != 'template'):
loaded_module = self._load_network_module(module_dir)
loaded_modules += loaded_module.dir_name + ' '

Expand Down Expand Up @@ -700,8 +705,7 @@ def network_adapters_checker(self, mqtt_client: mqtt.MQTT, topic: str):
def is_device_connected(self):
"""Check if device connected"""
return self._ip_ctrl.check_interface_status(
self._session.get_device_interface()
)
self._session.get_device_interface())

def internet_conn_checker(self, mqtt_client: mqtt.MQTT, topic: str):
"""Checks internet connection and sends a status to frontend"""
Expand All @@ -711,11 +715,9 @@ def internet_conn_checker(self, mqtt_client: mqtt.MQTT, topic: str):

# Only check if Testrun is running
if self.get_session().get_status() not in [
TestrunStatus.WAITING_FOR_DEVICE,
TestrunStatus.MONITORING,
TestrunStatus.IN_PROGRESS,
TestrunStatus.STARTING
]:
TestrunStatus.WAITING_FOR_DEVICE, TestrunStatus.MONITORING,
TestrunStatus.IN_PROGRESS, TestrunStatus.STARTING
]:
message['connection'] = None

# Only run if single intf mode not used
Expand All @@ -734,6 +736,7 @@ def internet_conn_checker(self, mqtt_client: mqtt.MQTT, topic: str):
# Broadcast via MQTT client
mqtt_client.send_message(topic, message)


class NetworkConfig:
"""Define all the properties of the network configuration"""

Expand Down
71 changes: 59 additions & 12 deletions modules/test/conn/python/src/port_stats_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
"""Module that contains various methods for validating the Port statistics """

import os
import re

ETHTOOL_CONN_STATS_FILE = 'runtime/network/ethtool_conn_stats.txt'
ETHTOOL_PORT_STATS_PRE_FILE = (
'runtime/network/ethtool_port_stats_pre_monitor.txt')
ETHTOOL_PORT_STATS_POST_FILE = (
'runtime/network/ethtool_port_stats_post_monitor.txt')
IFCONFIG_PORT_STATS_PRE_FILE = (
'runtime/network/ifconfig_port_stats_pre_monitor.txt')
IFCONFIG_PORT_STATS_POST_FILE = (
'runtime/network/ifconfig_port_stats_post_monitor.txt')

LOG_NAME = 'port_stats_util'
LOGGER = None
Expand All @@ -32,10 +37,14 @@ def __init__(self,
logger,
ethtool_conn_stats_file=ETHTOOL_CONN_STATS_FILE,
ethtool_port_stats_pre_file=ETHTOOL_PORT_STATS_PRE_FILE,
ethtool_port_stats_post_file=ETHTOOL_PORT_STATS_POST_FILE):
ethtool_port_stats_post_file=ETHTOOL_PORT_STATS_POST_FILE,
ifconfig_port_stats_pre_file=IFCONFIG_PORT_STATS_PRE_FILE,
ifconfig_port_stats_post_file=IFCONFIG_PORT_STATS_POST_FILE):
self.ethtool_conn_stats_file = ethtool_conn_stats_file
self.ethtool_port_stats_pre_file = ethtool_port_stats_pre_file
self.ethtool_port_stats_post_file = ethtool_port_stats_post_file
self.ifconfig_port_stats_pre_file = ifconfig_port_stats_pre_file
self.ifconfig_port_stats_post_file = ifconfig_port_stats_post_file
global LOGGER
LOGGER = logger
self.conn_stats = self._read_stats_file(self.ethtool_conn_stats_file)
Expand All @@ -48,16 +57,13 @@ def is_autonegotiate(self):
auto_negotiation = 'on' in auto_negotiation_status
return auto_negotiation

def connection_port_link_test(self):
def ethtool_port_link_test(self):
stats_pre = self._read_stats_file(self.ethtool_port_stats_pre_file)
stats_post = self._read_stats_file(self.ethtool_port_stats_post_file)
result = None
description = ''
details = ''
if stats_pre is None or stats_pre is None:
result = 'Error'
description = 'Port stats not available'
else:
if stats_pre is not None and stats_pre is not None:
tx_errors_pre = self._get_stat_option(stats=stats_pre,
option='tx_errors:')
tx_errors_post = self._get_stat_option(stats=stats_post,
Expand All @@ -68,11 +74,33 @@ def connection_port_link_test(self):
option='rx_errors:')

# Check that the above have been resolved correctly
if (tx_errors_pre is None or tx_errors_post is None or
rx_errors_pre is None or rx_errors_post is None):
result = 'Error'
description = 'Port stats not available'
else:
if (tx_errors_pre is not None and tx_errors_post is not None
and rx_errors_pre is not None and rx_errors_post is not None):
tx_errors = int(tx_errors_post) - int(tx_errors_pre)
rx_errors = int(rx_errors_post) - int(rx_errors_pre)
if tx_errors > 0 or rx_errors > 0:
result = False
description = 'Port errors detected'
details = f'TX errors: {tx_errors}, RX errors: {rx_errors}'
else:
result = True
description = 'No port errors detected'
return result, description, details

def ifconfig_port_link_test(self):
stats_pre = self._read_stats_file(self.ifconfig_port_stats_pre_file)
stats_post = self._read_stats_file(self.ifconfig_port_stats_post_file)
result = None
description = ''
details = ''
if stats_pre is not None and stats_pre is not None:
rx_errors_pre, tx_errors_pre = self.extract_rx_tx_error_counts(stats_pre)
rx_errors_post, tx_errors_post = self.extract_rx_tx_error_counts(
stats_post)

# Check that the above have been resolved correctly
if (tx_errors_pre is not None and tx_errors_post is not None
and rx_errors_pre is not None and rx_errors_post is not None):
tx_errors = int(tx_errors_post) - int(tx_errors_pre)
rx_errors = int(rx_errors_post) - int(rx_errors_pre)
if tx_errors > 0 or rx_errors > 0:
Expand All @@ -84,6 +112,17 @@ def connection_port_link_test(self):
description = 'No port errors detected'
return result, description, details

def connection_port_link_test(self):
port_results = self.ethtool_port_link_test()
if port_results[0] is None:
port_results = self.ifconfig_port_link_test()
if port_results[0] is None:
result = 'Error'
description = 'Port stats not available'
details = ''
port_results = result, description, details
return port_results

def connection_port_duplex_test(self):
auto_negotiation = self.is_autonegotiate()
# Calculate final results
Expand Down Expand Up @@ -132,11 +171,19 @@ def connection_port_speed_test(self):
details = f'Speed negotiated: {speed}'
return result, description, details

def extract_rx_tx_error_counts(self, ifconfig):
rx_match = re.search(r'^\s*RX errors (\d+)', ifconfig, re.MULTILINE)
tx_match = re.search(r'^\s*TX errors (\d+)', ifconfig, re.MULTILINE)

if rx_match and tx_match:
return int(rx_match.group(1)), int(tx_match.group(1))
else:
return None, None

def _get_stat_option(self, stats, option):
"""Extract the requested parameter from the ethtool result"""
value = None
for line in stats.split('\n'):
#LOGGER.info(f'Checking option: {line}')
if line.startswith(f'{option}'):
value = line.split(':')[1].strip()
break
Expand Down
36 changes: 36 additions & 0 deletions testing/unit/conn/conn_module_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,21 @@
TEST_FILES_DIR, 'ethtool',
'ethtool_port_stats_post_monitor_noncompliant.txt')

IFCONFIG_PORT_STATS_PRE_FILE = os.path.join(
TEST_FILES_DIR, 'ifconfig', 'ifconfig_port_stats_pre_monitor.txt')
IFCONFIG_PORT_STATS_POST_FILE = os.path.join(
TEST_FILES_DIR, 'ifconfig', 'ifconfig_port_stats_post_monitor.txt')
IFCONFIG_PORT_STATS_POST_NONCOMPLIANT_FILE = os.path.join(
TEST_FILES_DIR, 'ifconfig',
'ifconfig_port_stats_post_noncompliant_monitor.txt')

# Define the capture files to be used for the test
STARTUP_CAPTURE_FILE = os.path.join(CAPTURES_DIR, 'startup.pcap')
MONITOR_CAPTURE_FILE = os.path.join(CAPTURES_DIR, 'monitor.pcap')

LOGGER = None


class ConnectionModuleTest(unittest.TestCase):
"""Contains and runs all the unit tests concerning Connection
module behaviors"""
Expand All @@ -70,6 +79,29 @@ def connection_port_link_compliant_test(self):
LOGGER.info(result)
self.assertEqual(result[0], True)

def connection_port_link_ifconfig_compliant_test(self):
LOGGER.info('connection_port_link_ifconfig_compliant_test')
p_stats = PortStatsUtil(
logger=LOGGER,
ethtool_conn_stats_file=ETHTOOL_RESULTS_COMPLIANT_FILE,
ifconfig_port_stats_pre_file=IFCONFIG_PORT_STATS_PRE_FILE,
ifconfig_port_stats_post_file=IFCONFIG_PORT_STATS_POST_FILE)
result = p_stats.connection_port_link_test()
LOGGER.info(result)
self.assertEqual(result[0], True)

def connection_port_link_ifconfig_noncompliant_test(self):
LOGGER.info('connection_port_link_ifconfig_noncompliant_test')
p_stats = PortStatsUtil(
logger=LOGGER,
ethtool_conn_stats_file=ETHTOOL_RESULTS_COMPLIANT_FILE,
ifconfig_port_stats_pre_file=IFCONFIG_PORT_STATS_PRE_FILE,
ifconfig_port_stats_post_file=IFCONFIG_PORT_STATS_POST_NONCOMPLIANT_FILE
)
result = p_stats.connection_port_link_test()
LOGGER.info(result)
self.assertEqual(result[0], False)

# Test the port duplex setting
def connection_port_duplex_compliant_test(self):
LOGGER.info('connection_port_duplex_compliant_test')
Expand Down Expand Up @@ -174,11 +206,15 @@ def communication_network_type_test(self):

# Compliant port stats tests
suite.addTest(ConnectionModuleTest('connection_port_link_compliant_test'))
suite.addTest(
ConnectionModuleTest('connection_port_link_ifconfig_compliant_test'))
suite.addTest(ConnectionModuleTest('connection_port_duplex_compliant_test'))
suite.addTest(ConnectionModuleTest('connection_port_speed_compliant_test'))

# Non-compliant port stats tests
suite.addTest(ConnectionModuleTest('connection_port_link_noncompliant_test'))
suite.addTest(
ConnectionModuleTest('connection_port_link_ifconfig_noncompliant_test'))
suite.addTest(
ConnectionModuleTest('connection_port_duplex_noncompliant_test'))
suite.addTest(ConnectionModuleTest('connection_port_speed_noncompliant_test'))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
enx00e04c6601a4: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.228.1 netmask 255.255.255.0 broadcast 192.168.228.255
inet6 fe80::2e0:4cff:fe66:1a4 prefixlen 64 scopeid 0x20<link>
ether 00:e0:4c:66:01:a4 txqueuelen 1000 (Ethernet)
RX packets 23 bytes 2718 (2.7 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 36 bytes 3831 (3.8 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
enx00e04c6601a4: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.228.1 netmask 255.255.255.0 broadcast 192.168.228.255
inet6 fe80::2e0:4cff:fe66:1a4 prefixlen 64 scopeid 0x20<link>
ether 00:e0:4c:66:01:a4 txqueuelen 1000 (Ethernet)
RX packets 23 bytes 2718 (2.7 KB)
RX errors 5 dropped 0 overruns 0 frame 7
TX packets 36 bytes 3831 (3.8 KB)
TX errors 10 dropped 0 overruns 0 carrier 0 collisions 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
enx00e04c6601a4: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.228.1 netmask 255.255.255.0 broadcast 192.168.228.255
inet6 fe80::2e0:4cff:fe66:1a4 prefixlen 64 scopeid 0x20<link>
ether 00:e0:4c:66:01:a4 txqueuelen 1000 (Ethernet)
RX packets 1 bytes 46 (46.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2 bytes 180 (180.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0