summaryrefslogtreecommitdiffstats
path: root/scripts/automation/trex_control_plane/stl
diff options
context:
space:
mode:
authorimarom <imarom@cisco.com>2016-11-13 17:17:36 +0200
committerimarom <imarom@cisco.com>2016-11-13 17:17:36 +0200
commit6e1919c3aebabc0977a8ab40b5c60cbd0e7114d0 (patch)
tree502aa78527ee4de1723e4a32c291dfcdb71cea23 /scripts/automation/trex_control_plane/stl
parentab28fccc187c6134eeb0400ce0b113a77e498bb2 (diff)
RX features - pre-resolve stage
Signed-off-by: imarom <imarom@cisco.com>
Diffstat (limited to 'scripts/automation/trex_control_plane/stl')
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py132
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py156
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py8
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py5
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py33
5 files changed, 234 insertions, 100 deletions
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
index d4d09cd7..2ee5225c 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
@@ -12,7 +12,7 @@ from .trex_stl_types import *
from .trex_stl_async_client import CTRexAsyncClient
from .utils import parsing_opts, text_tables, common
-from .utils.common import list_intersect, list_difference, is_sub_list, PassiveTimer, is_valid_ipv4
+from .utils.common import list_intersect, list_difference, is_sub_list, PassiveTimer, is_valid_ipv4, is_valid_mac
from .utils.text_opts import *
from functools import wraps
@@ -326,24 +326,22 @@ class EventsHandler(object):
elif (event_type == 8):
port_id = int(data['port_id'])
-
- if data['attr'] == self.client.ports[port_id].attr:
- return # false alarm
-
- old_info = self.client.ports[port_id].get_formatted_info(sync = False)
- self.__async_event_port_attr_changed(port_id, data['attr'])
-
- new_info = self.client.ports[port_id].get_formatted_info(sync = False)
+
+ diff = self.__async_event_port_attr_changed(port_id, data['attr'])
+ if not diff:
+ return
+
+
ev = "port {0} attributes changed".format(port_id)
- for key, old_val in old_info.items():
- new_val = new_info.get(key, 'N/A')
- if old_val != new_val:
- ev += '\n {key}: {old} -> {new}'.format(
+ for key, (old_val, new_val) in diff.items():
+ ev += '\n {key}: {old} -> {new}'.format(
key = key,
old = old_val.lower() if type(old_val) is str else old_val,
new = new_val.lower() if type(new_val) is str else new_val)
+
show_event = True
-
+
+
# server stopped
elif (event_type == 100):
@@ -399,7 +397,7 @@ class EventsHandler(object):
def __async_event_port_attr_changed (self, port_id, attr):
if port_id in self.client.ports:
- self.client.ports[port_id].async_event_port_attr_changed(attr)
+ return self.client.ports[port_id].async_event_port_attr_changed(attr)
# add event to log
def __add_event_log (self, origin, ev_type, msg, show = False):
@@ -812,7 +810,7 @@ class STLClient(object):
rc = RC()
for port_id, port_attr_dict in zip(port_id_list, attr_dict):
- rc.add(self.ports[port_id].set_attr(port_attr_dict))
+ rc.add(self.ports[port_id].set_attr(**port_attr_dict))
return rc
@@ -1772,10 +1770,19 @@ class STLClient(object):
raise STLError(rc)
def test (self):
+ #rc = self.ports[0].resolve()
+ #if not rc:
+ # raise STLError(rc)
+ #return
- self.reset(ports = [0, 1])
+ self.reset(ports = [0])
- self.set_port_attr(ports = [0, 1], ipv4 = ['5.5.5.5', '6.6.6.6'])
+ attr = self.ports[0].get_ts_attr()
+ src_ipv4 = attr['src_ipv4']
+ src_mac = attr['src_mac']
+ dest = attr['dest']
+ print(src_ipv4, src_mac, dest)
+ #self.set_port_attr(ports = [0, 1], ipv4 = ['5.5.5.5', '6.6.6.6'])
return
self.set_rx_queue(ports = [0], size = 1000, rxf = 'all')
@@ -2727,7 +2734,7 @@ class STLClient(object):
flow_ctrl = None,
rxf = None,
ipv4 = None,
- default_gateway = None,
+ dest = None,
):
"""
Set port attributes
@@ -2740,8 +2747,8 @@ class STLClient(object):
rxf - 'hw' for hardware rules matching packets only or 'all' all packets
ipv4 - configure IPv4 address for port(s). for multiple ports should be a list
of IPv4 addresses in the same length of the ports array
- default_gateway - configure default gateway for port(s). for multiple ports should be a list
- in the same length of the ports array
+ dest - configure destination address for port(s) in either IPv4 or MAC format.
+ for multiple ports should be a list in the same length of the ports array
:raises:
+ :exe:'STLError'
@@ -2759,54 +2766,39 @@ class STLClient(object):
# common attributes for all ports
cmn_attr_dict = {}
- if promiscuous is not None:
- cmn_attr_dict['promiscuous'] = {'enabled': promiscuous}
-
- if link_up is not None:
- cmn_attr_dict['link_status'] = {'up': link_up}
-
- if led_on is not None:
- cmn_attr_dict['led_status'] = {'on': led_on}
-
- if flow_ctrl is not None:
- cmn_attr_dict['flow_ctrl_mode'] = {'mode': flow_ctrl}
-
- if rxf is not None:
- cmn_attr_dict['rx_filter_mode'] = {'mode': rxf}
+ cmn_attr_dict['promiscuous'] = promiscuous
+ cmn_attr_dict['link_status'] = link_up
+ cmn_attr_dict['led_status'] = led_on
+ cmn_attr_dict['flow_ctrl_mode'] = flow_ctrl
+ cmn_attr_dict['rx_filter_mode'] = rxf
+
# each port starts with a set of the common attributes
attr_dict = [dict(cmn_attr_dict) for _ in ports]
+
+ # default value for IPv4 / dest is none for all ports
+ if ipv4 is None:
+ ipv4 = [None] * len(ports)
+ if dest is None:
+ dest = [None] * len(ports)
- # IPv4
- if ipv4 is not None:
- ipv4_list = listify(ipv4)
-
- if len(ipv4_list) != len(ports):
- raise STLError("'ipv4' must be a list in the same length of ports - 'ports': {0}, 'ip': {1}".format(ports, ipv4_list))
+ ipv4 = listify(ipv4)
+ if len(ipv4) != len(ports):
+ raise STLError("'ipv4' must be a list in the same length of ports - 'ports': {0}, 'ip': {1}".format(ports, ipv4))
- for ipv4, port_attr in zip(ipv4_list, attr_dict):
- if not is_valid_ipv4(ipv4):
- raise STLError("invalid IPv4 address provided: '{0}'".format(ipv4))
- port_attr['ipv4'] = {'addr': ipv4}
-
+ dest = listify(dest)
+ if len(dest) != len(ports):
+ raise STLError("'dest' must be a list in the same length of ports - 'ports': {0}, 'dest': {1}".format(ports, dest))
- # default gateway
- if default_gateway is not None:
- dg_list = listfy(default_gateway)
+ # update each port attribute with ipv4
+ for addr, port_attr in zip(ipv4, attr_dict):
+ port_attr['ipv4'] = addr
+
+ # update each port attribute with dest
+ for addr, port_attr in zip(dest, attr_dict):
+ port_attr['dest'] = addr
- if len(dg_list) != len(ports):
- raise STLError("'default_gateway' must be a list in the same length of ports - 'ports': {0}, 'default_gateway': {1}".format(ports, dg_list))
-
- for dg, port_attr in zip(dg_list, attr_dict):
- if not is_valid_ipv4(dg):
- raise STLError("invalid IPv4 address provided: '{0}'".format(ipv4))
- port_attr['default_gateway'] = {'addr': dg}
-
-
- # no attributes to set
- if not any(attr_dict):
- return
-
+
self.logger.pre_cmd("Applying attributes on port(s) {0}:".format(ports))
rc = self.__set_port_attr(ports, attr_dict)
self.logger.post_cmd(rc)
@@ -2815,7 +2807,6 @@ class STLClient(object):
raise STLError(rc)
-
@__api_check(True)
def set_rx_sniffer (self, ports = None, base_filename = 'rx_capture', limit = 1000, rxf = None):
"""
@@ -3515,6 +3506,8 @@ class STLClient(object):
parsing_opts.FLOW_CTRL,
parsing_opts.SUPPORTED,
parsing_opts.RX_FILTER_MODE,
+ parsing_opts.IPV4,
+ parsing_opts.DEST
)
opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True)
@@ -3527,12 +3520,12 @@ class STLClient(object):
opts.flow_ctrl = parsing_opts.FLOW_CTRL_DICT.get(opts.flow_ctrl)
# if no attributes - fall back to printing the status
- if not list(filter(lambda x:x is not None, [opts.prom, opts.link, opts.led, opts.flow_ctrl, opts.supp, opts.rx_filter_mode])):
+ if not list(filter(lambda x:x is not None, [opts.prom, opts.link, opts.led, opts.flow_ctrl, opts.supp, opts.rx_filter_mode, opts.ipv4, opts.dest])):
self.show_stats_line("--ps --port {0}".format(' '.join(str(port) for port in opts.ports)))
return
if opts.supp:
- info = self.ports[0].get_info() # assume for now all ports are same
+ info = self.ports[0].get_formatted_info() # assume for now all ports are same
print('')
print('Supported attributes for current NICs:')
print(' Promiscuous: yes')
@@ -3541,7 +3534,14 @@ class STLClient(object):
print(' Flow control: %s' % info['fc_supported'])
print('')
else:
- return self.set_port_attr(opts.ports, opts.prom, opts.link, opts.led, opts.flow_ctrl, opts.rx_filter_mode)
+ return self.set_port_attr(opts.ports,
+ opts.prom,
+ opts.link,
+ opts.led,
+ opts.flow_ctrl,
+ opts.rx_filter_mode,
+ opts.ipv4,
+ opts.dest)
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py
index 418ee5a6..3fd00391 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py
@@ -10,6 +10,7 @@ from .utils.constants import FLOW_CTRL_DICT_REVERSED
import base64
import copy
from datetime import datetime, timedelta
+import threading
StreamOnPort = namedtuple('StreamOnPort', ['compiled_stream', 'metadata'])
@@ -50,7 +51,9 @@ class Port(object):
def __init__ (self, port_id, user, comm_link, session_id, info):
self.port_id = port_id
+
self.state = self.STATE_IDLE
+
self.handler = None
self.comm_link = comm_link
self.transmit = comm_link.transmit
@@ -63,7 +66,7 @@ class Port(object):
self.profile = None
self.session_id = session_id
self.status = {}
- self.attr = {}
+ self.__attr = {}
self.port_stats = trex_stl_stats.CPortStats(self)
@@ -74,6 +77,8 @@ class Port(object):
self.owner = ''
self.last_factor_type = None
+ self.attr_lock = threading.Lock()
+
# decorator to verify port is up
def up(func):
def func_wrapper(*args, **kwargs):
@@ -88,7 +93,7 @@ class Port(object):
# owned
def owned(func):
- def func_wrapper(*args):
+ def func_wrapper(*args, **kwargs):
port = args[0]
if not port.is_up():
@@ -97,7 +102,7 @@ class Port(object):
if not port.is_acquired():
return port.err("{0} - port is not owned".format(func.__name__))
- return func(*args)
+ return func(*args, **kwargs)
return func_wrapper
@@ -129,10 +134,10 @@ class Port(object):
return RC_OK(data)
def get_speed_bps (self):
- return (self.attr['speed'] * 1000 * 1000 * 1000)
+ return (self.__attr['speed'] * 1000 * 1000 * 1000)
def get_formatted_speed (self):
- return "%g Gb/s" % (self.attr['speed'] / 1000)
+ return "%g Gb/s" % (self.__attr['speed'] / 1000)
def is_acquired(self):
return (self.handler != None)
@@ -253,7 +258,8 @@ class Port(object):
self.status = rc.data()
- self.attr = rc.data()['attr']
+ # replace the attributes in a thread safe manner
+ self.set_ts_attr(rc.data()['attr'])
return self.ok()
@@ -645,19 +651,44 @@ class Port(object):
@owned
- def set_attr (self, attr_dict):
+ def set_attr (self, **kwargs):
+
+ json_attr = {}
+
+ if kwargs.get('promiscuous') is not None:
+ json_attr['promiscuous'] = {'enabled': kwargs.get('promiscuous')}
+
+ if kwargs.get('link_up') is not None:
+ json_attr['link_status'] = {'up': kwargs.get('link_up')}
+
+ if kwargs.get('led_on') is not None:
+ json_attr['led_status'] = {'on': kwargs.get('led_on')}
+
+ if kwargs.get('flow_ctrl_mode') is not None:
+ json_attr['flow_ctrl_mode'] = {'on': kwargs.get('flow_ctrl_mode')}
+
+ if kwargs.get('rx_filter_mode') is not None:
+ json_attr['rx_filter_mode'] = {'mode': kwargs.get('rx_filter_mode')}
+
+ if kwargs.get('ipv4') is not None:
+ json_attr['ipv4'] = {'addr': kwargs.get('ipv4')}
+
+ if kwargs.get('dest') is not None:
+ json_attr['dest'] = {'addr': kwargs.get('dest')}
+
params = {"handler": self.handler,
"port_id": self.port_id,
- "attr": attr_dict}
+ "attr": json_attr}
rc = self.transmit("set_port_attr", params)
if rc.bad():
return self.err(rc.err())
- return self.ok()
-
+ # update the dictionary from the server explicitly
+ return self.sync()
+
@writeable
def push_remote (self, pcap_filename, ipg_usec, speedup, count, duration, is_dual, slave_handler):
@@ -730,7 +761,8 @@ class Port(object):
if sync:
self.sync()
- attr = self.attr
+ # get a copy of the current attribute set (safe against manipulation)
+ attr = self.get_ts_attr()
info = dict(self.info)
@@ -781,15 +813,35 @@ class Port(object):
if 'rx_filter_mode' in attr:
- info['rx_filter_mode'] = 'Hardware Match' if attr['rx_filter_mode'] == 'hw' else 'Fetch All'
+ info['rx_filter_mode'] = 'hardware match' if attr['rx_filter_mode'] == 'hw' else 'fetch all'
else:
info['rx_filter_mode'] = 'N/A'
+ # src MAC and IPv4
+ info['src_mac'] = attr.get('src_mac', 'N/A')
+
+ info['src_ipv4'] = attr.get('src_ipv4', 'N/A')
+ if info['src_ipv4'] == 'none':
+ info['src_ipv4'] = 'Not Configured'
+
+ # dest
+ dest = attr.get('dest', {})
+ info['dest'] = dest.get('addr', 'N/A')
+
+ if dest['type'] == 'mac':
+ info['arp'] = '-'
+ else:
+ info['arp'] = dest.get('arp', 'N/A')
+
+
+ if info['dest'] == 'none':
+ info['dest'] = 'Not Configured'
+
+
+ if info['arp'] == 'none':
+ info['arp'] = 'unresolved'
+
- info['mac_addr'] = attr.get('mac_addr', 'N/A')
- info['ipv4'] = attr.get('ipv4', 'N/A')
- info['default_gateway'] = attr.get('default_gateway', 'N/A')
- info['next_hop_mac'] = attr.get('next_hop_mac', 'N/A')
# RX info
rx_info = self.status['rx_info']
@@ -816,6 +868,17 @@ class Port(object):
def get_port_state_name(self):
return self.STATES_MAP.get(self.state, "Unknown")
+ def get_src_ipv4 (self):
+ src_ipv4 = self.__attr['src_ipv4']
+ if src_ipv4 == 'none':
+ src_ipv4 = None
+
+ return src_ipv4
+
+ def get_src_mac (self):
+ return self.__attr['src_mac']
+
+
################# stats handler ######################
def generate_port_stats(self):
return self.port_stats.generate_stats()
@@ -824,14 +887,14 @@ class Port(object):
info = self.get_formatted_info()
- return {"driver": info['driver'],
- "description": info.get('description', 'N/A')[:18],
- "MAC addr": info['mac_addr'],
- "Next hop MAC": info['next_hop_mac'],
- "IPv4": info['ipv4'],
- "Default gateway": info['default_gateway'],
- "PCI Address": info['pci_addr'],
- "NUMA Node": info['numa'],
+ return {"driver": info['driver'],
+ "description": info.get('description', 'N/A')[:18],
+ "src MAC": info['src_mac'],
+ "src IPv4": info['src_ipv4'],
+ "Destination": info['dest'],
+ "ARP Resolution": info['arp'],
+ "PCI Address": info['pci_addr'],
+ "NUMA Node": info['numa'],
"--": "",
"---": "",
"----": "",
@@ -881,8 +944,20 @@ class Port(object):
return {"streams" : OrderedDict(sorted(data.items())) }
-
-
+
+ ######## attributes are a complex type (dict) that might be manipulated through the async thread #############
+
+ # get in a thread safe manner a duplication of attributes
+ def get_ts_attr (self):
+ with self.attr_lock:
+ return dict(self.__attr)
+
+ # set in a thread safe manner a new dict of attributes
+ def set_ts_attr (self, new_attr):
+ with self.attr_lock:
+ self.__attr = new_attr
+
+
################# events handler ######################
def async_event_port_job_done (self):
# until thread is locked - order is important
@@ -890,8 +965,33 @@ class Port(object):
self.state = self.STATE_STREAMS
self.last_factor_type = None
- def async_event_port_attr_changed (self, attr):
- self.attr = attr
+ def async_event_port_attr_changed (self, new_attr):
+
+ # get a thread safe duplicate
+ cur_attr = self.get_ts_attr()
+
+ # check if anything changed
+ if new_attr == cur_attr:
+ return None
+
+ # generate before
+ before = self.get_formatted_info(sync = False)
+
+ # update
+ self.set_ts_attr(new_attr)
+
+ # generate after
+ after = self.get_formatted_info(sync = False)
+
+ # return diff
+ diff = {}
+ for key, new_value in after.items():
+ old_value = before.get(key, 'N/A')
+ if new_value != old_value:
+ diff[key] = (old_value, new_value)
+
+ return diff
+
# rest of the events are used for TUI / read only sessions
def async_event_port_stopped (self):
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
index 55620689..f0293016 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
@@ -670,11 +670,11 @@ class CTRexInfoGenerator(object):
("promiscuous", []),
("flow ctrl", []),
("--", []),
- ("MAC addr", []),
- ("Next hop MAC", []),
+ ("src IPv4", []),
+ ("src MAC", []),
("---", []),
- ("IPv4", []),
- ("Default gateway", []),
+ ("Destination", []),
+ ("ARP Resolution", []),
("----", []),
("PCI Address", []),
("NUMA Node", []),
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py
index f0da9a08..d4ac973d 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py
@@ -4,6 +4,7 @@ import string
import random
import time
import socket
+import re
try:
import pwd
@@ -93,3 +94,7 @@ def is_valid_ipv4 (addr):
return True
except socket.error:
return False
+
+def is_valid_mac (mac):
+ return bool(re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", mac.lower()))
+
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
index b93a797d..80260d4a 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
@@ -1,6 +1,6 @@
import argparse
from collections import namedtuple, OrderedDict
-from .common import list_intersect, list_difference
+from .common import list_intersect, list_difference, is_valid_ipv4, is_valid_mac
from .text_opts import format_text
from ..trex_stl_types import *
from .constants import ON_OFF_DICT, UP_DOWN_DICT, FLOW_CTRL_DICT
@@ -45,12 +45,14 @@ FLOW_CTRL = 28
SUPPORTED = 29
RX_FILTER_MODE = 30
-
OUTPUT_FILENAME = 31
ALL_FILES = 32
LIMIT = 33
PORT_RESTART = 34
+IPV4 = 35
+DEST = 36
+
GLOBAL_STATS = 50
PORT_STATS = 51
PORT_STATUS = 52
@@ -224,8 +226,20 @@ def is_valid_file(filename):
return filename
+def check_ipv4_addr (ipv4_str):
+ if not is_valid_ipv4(ipv4_str):
+ raise argparse.ArgumentTypeError("invalid IPv4 address: '{0}'".format(ipv4_str))
+
+ return ipv4_str
+
+def check_dest_addr (addr):
+ if not (is_valid_ipv4(addr) or is_valid_mac(addr)):
+ raise argparse.ArgumentTypeError("not a valid IPv4 or MAC address: '{0}'".format(addr))
+
+ return addr
+
def decode_tunables (tunable_str):
tunables = {}
@@ -316,6 +330,21 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
'choices': ['hw', 'all']}),
+ IPV4: ArgumentPack(['--ipv4'],
+ {'help': 'IPv4 address(s) for the port(s)',
+ 'dest': 'ipv4',
+ 'nargs': '+',
+ 'default': None,
+ 'type': check_ipv4_addr}),
+
+ DEST: ArgumentPack(['--dest'],
+ {'help': 'Destination address(s) for the port(s) in either IPv4 or MAC format',
+ 'dest': 'dest',
+ 'nargs': '+',
+ 'default': None,
+ 'type': check_dest_addr}),
+
+
OUTPUT_FILENAME: ArgumentPack(['-o', '--output'],
{'help': 'Output PCAP filename',
'dest': 'output_filename',