summaryrefslogtreecommitdiffstats
path: root/scripts/automation/trex_control_plane
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/automation/trex_control_plane')
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py59
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py189
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py8
3 files changed, 233 insertions, 23 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 2ee5225c..3a37524c 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
@@ -804,6 +804,17 @@ class STLClient(object):
return rc
+ def __resolve (self, port_id_list = None, retries = 0):
+ port_id_list = self.__ports(port_id_list)
+
+ rc = RC()
+
+ for port_id in port_id_list:
+ rc.add(self.ports[port_id].resolve(retries))
+
+ return rc
+
+
def __set_port_attr (self, port_id_list = None, attr_dict = None):
port_id_list = self.__ports(port_id_list)
@@ -1770,6 +1781,9 @@ class STLClient(object):
raise STLError(rc)
def test (self):
+ self.resolve()
+ return
+
#rc = self.ports[0].resolve()
#if not rc:
# raise STLError(rc)
@@ -1827,7 +1841,8 @@ class STLClient(object):
+ :exc:`STLError`
"""
- self.test()
+ self.resolve()
+ return
self.logger.pre_cmd("Pinging the server on '{0}' port '{1}': ".format(self.connection_info['server'],
self.connection_info['sync_port']))
@@ -2151,7 +2166,7 @@ class STLClient(object):
validate_type('core_mask', core_mask, (int, list))
# verify link status
- ports_link_down = [port_id for port_id in ports if self.ports[port_id].attr.get('link',{}).get('up') == False]
+ ports_link_down = [port_id for port_id in ports if not self.ports[port_id].is_up()]
if not force and ports_link_down:
raise STLError("Port(s) %s - link DOWN - check the connection or specify 'force'" % ports_link_down)
@@ -2808,6 +2823,36 @@ class STLClient(object):
@__api_check(True)
+ def resolve (self, ports = None, retries = 0):
+ """
+ Resolves ports (ARP resolution)
+
+ :parameters:
+ ports - for which ports to apply a unique sniffer (each port gets a unique file)
+ retires - how many times to retry on each port (intervals of 100 milliseconds)
+ :raises:
+ + :exe:'STLError'
+
+ """
+ # by default - resolve all the ports that are configured with IPv4 dest
+ if ports is None:
+ ports = [port_id for port_id in self.get_acquired_ports() if self.ports[port_id].get_dest()['type'] == 'ipv4']
+
+ ports = self._validate_port_list(ports)
+
+
+ self.logger.pre_cmd("Resolving destination on port(s) {0}:".format(ports))
+ with self.logger.supress():
+ rc = self.__resolve(ports, retries)
+ self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+
+
+
+ @__api_check(True)
def set_rx_sniffer (self, ports = None, base_filename = 'rx_capture', limit = 1000, rxf = None):
"""
Sets RX sniffer for port(s) written to a PCAP file
@@ -2871,7 +2916,7 @@ class STLClient(object):
The queue is cyclic and will hold last 'size' packets
:parameters:
- ports - for which ports to apply a unique sniffer (each port gets a unique file)
+ ports - for which ports to apply a queue
size - size of the queue
rxf - which RX filter to use on those ports: 'hw' or 'all'
:raises:
@@ -2905,6 +2950,8 @@ class STLClient(object):
"""
Removes RX queue from port(s)
+ :parameters:
+ ports - for which ports to remove the RX queue
:raises:
+ :exe:'STLError'
@@ -2927,7 +2974,10 @@ class STLClient(object):
Returns any packets queued on the RX side by the server
return value is a dictonary per port
+ :parameters:
+ ports - for which ports to fetch
"""
+
ports = ports if ports is not None else self.get_acquired_ports()
ports = self._validate_port_list(ports)
@@ -3667,3 +3717,6 @@ class STLClient(object):
else:
return "{0} {1}>".format(prefix, self.get_acquired_ports())
+
+
+
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 3fd00391..e19eebe1 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
@@ -2,15 +2,18 @@
from collections import namedtuple, OrderedDict
from .trex_stl_packet_builder_scapy import STLPktBuilder
-from .trex_stl_streams import STLStream
+from .trex_stl_streams import STLStream, STLTXSingleBurst
from .trex_stl_types import *
from . import trex_stl_stats
from .utils.constants import FLOW_CTRL_DICT_REVERSED
+from scapy.layers.l2 import Ether, ARP
+
import base64
import copy
from datetime import datetime, timedelta
import threading
+import time
StreamOnPort = namedtuple('StreamOnPort', ['compiled_stream', 'metadata'])
@@ -143,7 +146,7 @@ class Port(object):
return (self.handler != None)
def is_up (self):
- return (self.state != self.STATE_DOWN)
+ return self.__attr['link']['up']
def is_active(self):
return (self.state == self.STATE_TX ) or (self.state == self.STATE_PAUSE) or (self.state == self.STATE_PCAP_TX)
@@ -171,7 +174,6 @@ class Port(object):
# take the port
- @up
def acquire(self, force = False, sync_streams = True):
params = {"port_id": self.port_id,
"user": self.user,
@@ -191,7 +193,6 @@ class Port(object):
# sync all the streams with the server
- @up
def sync_streams (self):
params = {"port_id": self.port_id}
@@ -207,7 +208,6 @@ class Port(object):
return self.ok()
# release the port
- @up
def release(self):
params = {"port_id": self.port_id,
"handler": self.handler}
@@ -225,7 +225,6 @@ class Port(object):
- @up
def sync(self):
params = {"port_id": self.port_id}
@@ -511,6 +510,21 @@ class Port(object):
return self.ok()
@owned
+ def set_arp_resolution (self, ipv4, mac):
+
+ params = {"handler": self.handler,
+ "port_id": self.port_id,
+ "ipv4": ipv4,
+ "mac": mac}
+
+ rc = self.transmit("set_arp_resolution", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ return self.ok()
+
+
+ @owned
def remove_rx_sniffer (self):
params = {"handler": self.handler,
"port_id": self.port_id,
@@ -658,11 +672,11 @@ class Port(object):
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('link_status') is not None:
+ json_attr['link_status'] = {'up': kwargs.get('link_status')}
- if kwargs.get('led_on') is not None:
- json_attr['led_status'] = {'on': kwargs.get('led_on')}
+ if kwargs.get('led_status') is not None:
+ json_attr['led_status'] = {'on': kwargs.get('led_status')}
if kwargs.get('flow_ctrl_mode') is not None:
json_attr['flow_ctrl_mode'] = {'on': kwargs.get('flow_ctrl_mode')}
@@ -713,7 +727,35 @@ class Port(object):
def get_profile (self):
return self.profile
-
+ # invalidates the current ARP
+ def invalidate_arp (self):
+ dest = self.__attr['dest']
+
+ if dest['type'] != 'mac':
+ return self.set_attr(dest = dest['addr'])
+ else:
+ return self.ok()
+
+
+ @writeable
+ def add_arp_request (self):
+ ipv4 = self.__attr['src_ipv4']
+ dest = self.__attr['dest']
+ mac = self.__attr['src_mac']
+
+ if ipv4 == 'none':
+ return self.err('port must have a configured IPv4')
+
+ if dest['type'] == 'mac':
+ return self.err('port must have an IPv4 destination')
+
+
+ base_pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(psrc = ipv4, pdst = dest['addr'], hwsrc = mac)
+ s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
+
+ return self.add_streams([s1])
+
+
def print_profile (self, mult, duration):
if not self.get_profile():
return
@@ -874,10 +916,17 @@ class Port(object):
src_ipv4 = None
return src_ipv4
+
+ def get_dest (self):
+ return self.__attr['dest']
def get_src_mac (self):
return self.__attr['src_mac']
-
+
+
+ def resolve (self, retries):
+ return ARPResolver(self).resolve(retries)
+
################# stats handler ######################
def generate_port_stats(self):
@@ -944,7 +993,7 @@ 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
@@ -1017,3 +1066,117 @@ class Port(object):
def async_event_released (self):
self.owner = ''
+
+# a class to handle port ARP resolution
+class ARPResolver(object):
+ def __init__ (self, port):
+ self.port = port
+
+ # some sanity checks before resolving
+ def sanity (self):
+ if self.port.get_dest()['type'] == 'mac':
+ return self.port.err('resolve - port does not have an IPv4 as destination')
+
+ if self.port.get_src_ipv4() is None:
+ return self.port.err('resolve - port does not have an IPv4 address configured')
+
+ return self.port.ok()
+
+ # main resolve function
+ def resolve (self, retries):
+ rc = self.sanity()
+ if not rc:
+ return rc
+
+ # invalidate the current ARP resolution (if exists)
+ rc = self.port.invalidate_arp()
+ if not rc:
+ return rc
+
+ rc = self.port.remove_all_streams()
+ if not rc:
+ return rc
+
+ rc = self.port.set_rx_queue(size = 100)
+ if not rc:
+ return rc
+
+ rc = self.port.add_arp_request()
+ if not rc:
+ return rc
+
+
+ # retry for 'retries'
+ index = 0
+ while True:
+ response = self.resolve_iteration()
+ if response:
+ break
+
+ if index >= retries:
+ return self.port.err('failed to receive ARP response ({0} retries)'.format(retries))
+
+ index += 1
+ time.sleep(0.1)
+
+
+ # set ARP resolution result
+ rc = self.port.set_arp_resolution(response['ipv4'], response['mac'])
+ if not rc:
+ return rc
+
+ return self.port.ok()
+
+
+ def resolve_iteration (self):
+
+ mult = {'op': 'abs', 'type' : 'percentage', 'value': 100}
+ rc = self.port.start(mul = mult, force = False, duration = -1, mask = 0xffffffff)
+ if not rc:
+ return rc
+
+ # block until traffic finishes
+ while self.port.is_active():
+ time.sleep(0.01)
+
+ return self.wait_for_rx_response()
+
+
+ def wait_for_rx_response (self):
+
+ # we try to fetch response for 5 times
+ polling = 5
+
+ while polling > 0:
+ rx_pkts = self.port.get_rx_queue_pkts()
+ response = self.find_arp_response(rx_pkts)
+
+ if response:
+ return response
+
+ if polling == 0:
+ return None
+
+ polling -= 1
+ time.sleep(0.1)
+
+
+ # search in 'pkts' for an ARP response that matches the dest
+ def find_arp_response (self, pkts):
+
+ for pkt in pkts:
+ scapy_pkt = Ether(pkt)
+ if not 'ARP' in scapy_pkt:
+ continue
+
+ arp = scapy_pkt['ARP']
+ dest = self.port.get_dest()
+
+ # check this is the right ARP (ARP reply with the address)
+ if (arp.op != 2) or (arp.psrc != dest['addr']):
+ continue
+
+ return {'ipv4': arp.psrc, 'mac': arp.hwsrc}
+
+ return None
+
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 f0293016..2efb5a84 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
@@ -1109,13 +1109,7 @@ class CPortStats(CTRexStats):
port_state = format_text(port_state, 'bold')
if self._port_obj:
- if 'link' in self._port_obj.attr:
- if self._port_obj.attr.get('link', {}).get('up') == False:
- link_state = format_text('DOWN', 'red', 'bold')
- else:
- link_state = 'UP'
- else:
- link_state = 'N/A'
+ link_state = 'UP' if self._port_obj.is_up() else format_text('DOWN', 'red', 'bold')
else:
link_state = ''