NFVBENCH-152 Add service_mode method for debugging purpose
[nfvbench.git] / nfvbench / traffic_client.py
index dbb8206..7591062 100755 (executable)
@@ -23,7 +23,9 @@ from attrdict import AttrDict
 import bitmath
 from netaddr import IPNetwork
 # pylint: disable=import-error
-from trex_stl_lib.api import STLError
+from trex.stl.api import Ether
+from trex.stl.api import STLError
+from trex.stl.api import UDP
 # pylint: enable=import-error
 
 from log import LOG
@@ -44,12 +46,13 @@ class TrafficClientException(Exception):
 class TrafficRunner(object):
     """Serialize various steps required to run traffic."""
 
-    def __init__(self, client, duration_sec, interval_sec=0):
+    def __init__(self, client, duration_sec, interval_sec=0, service_mode=False):
         """Create a traffic runner."""
         self.client = client
         self.start_time = None
         self.duration_sec = duration_sec
         self.interval_sec = interval_sec
+        self.service_mode = service_mode
 
     def run(self):
         """Clear stats and instruct the traffic generator to start generating traffic."""
@@ -57,6 +60,10 @@ class TrafficRunner(object):
             return None
         LOG.info('Running traffic generator')
         self.client.gen.clear_stats()
+        # Debug use only : new '--service-mode' option available for the NFVBench command line.
+        # A read-only mode TRex console would be able to capture the generated traffic.
+        self.client.gen.set_service_mode(enabled=self.service_mode)
+        LOG.info('Service mode is %sabled', 'en' if self.service_mode else 'dis')
         self.client.gen.start_traffic()
         self.start_time = time.time()
         return self.poll_stats()
@@ -241,6 +248,11 @@ class Device(object):
         self.vnis = vnis
         LOG.info("Port %d: VNIs %s", self.port, self.vnis)
 
+    def set_gw_ip(self, gateway_ip):
+        self.gw_ip_block = IpBlock(gateway_ip,
+                                   self.generator_config.gateway_ip_addrs_step,
+                                   self.chain_count)
+
     def get_gw_ip(self, chain_index):
         """Retrieve the IP address assigned for the gateway of a given chain."""
         return self.gw_ip_block.get_ip(chain_index)
@@ -334,17 +346,22 @@ class GeneratorConfig(object):
         else:
             self.cores = gen_config.get('cores', 1)
         self.mbuf_factor = config.mbuf_factor
+        self.mbuf_64 = config.mbuf_64
+        self.hdrh = not config.disable_hdrh
         if gen_config.intf_speed:
             # interface speed is overriden from config
             self.intf_speed = bitmath.parse_string(gen_config.intf_speed.replace('ps', '')).bits
         else:
             # interface speed is discovered/provided by the traffic generator
             self.intf_speed = 0
+        self.name = gen_config.name
+        self.zmq_pub_port = gen_config.get('zmq_pub_port', 4500)
+        self.zmq_rpc_port = gen_config.get('zmq_rpc_port', 4501)
+        self.limit_memory = gen_config.get('limit_memory', 1024)
         self.software_mode = gen_config.get('software_mode', False)
         self.interfaces = gen_config.interfaces
         if self.interfaces[0].port != 0 or self.interfaces[1].port != 1:
             raise TrafficClientException('Invalid port order/id in generator_profile.interfaces')
-
         self.service_chain = config.service_chain
         self.service_chain_count = config.service_chain_count
         self.flow_count = config.flow_count
@@ -386,10 +403,11 @@ class GeneratorConfig(object):
         port_index: the port for which dest macs must be set
         dest_macs: a list of dest MACs indexed by chain id
         """
-        if len(dest_macs) != self.config.service_chain_count:
+        if len(dest_macs) < self.config.service_chain_count:
             raise TrafficClientException('Dest MAC list %s must have %d entries' %
                                          (dest_macs, self.config.service_chain_count))
-        self.devices[port_index].set_dest_macs(dest_macs)
+        # only pass the first scc dest MACs
+        self.devices[port_index].set_dest_macs(dest_macs[:self.config.service_chain_count])
         LOG.info('Port %d: dst MAC %s', port_index, [str(mac) for mac in dest_macs])
 
     def set_vtep_dest_macs(self, port_index, dest_macs):
@@ -473,7 +491,8 @@ class TrafficClient(object):
         self.notifier = notifier
         self.interval_collector = None
         self.iteration_collector = None
-        self.runner = TrafficRunner(self, self.config.duration_sec, self.config.interval_sec)
+        self.runner = TrafficRunner(self, self.config.duration_sec, self.config.interval_sec,
+                                    self.config.service_mode)
         self.config.frame_sizes = self._get_frame_sizes()
         self.run_config = {
             'l2frame_size': None,
@@ -493,8 +512,8 @@ class TrafficClient(object):
     def _get_generator(self):
         tool = self.tool.lower()
         if tool == 'trex':
-            from traffic_gen import trex
-            return trex.TRex(self)
+            from traffic_gen import trex_gen
+            return trex_gen.TRex(self)
         if tool == 'dummy':
             from traffic_gen import dummy
             return dummy.DummyTG(self)
@@ -571,8 +590,8 @@ class TrafficClient(object):
         LOG.info('Starting traffic generator to ensure end-to-end connectivity')
         # send 2pps on each chain and each direction
         rate_pps = {'rate_pps': str(self.config.service_chain_count * 2)}
-        self.gen.create_traffic('64', [rate_pps, rate_pps], bidirectional=True, latency=False)
-
+        self.gen.create_traffic('64', [rate_pps, rate_pps], bidirectional=True, latency=False,
+                                e2e=True)
         # ensures enough traffic is coming back
         retry_count = (self.config.check_traffic_time_sec +
                        self.config.generic_poll_sec - 1) / self.config.generic_poll_sec
@@ -605,11 +624,10 @@ class TrafficClient(object):
             self.gen.stop_traffic()
             self.gen.fetch_capture_packets()
             self.gen.stop_capture()
-
             for packet in self.gen.packet_list:
                 mac_id = get_mac_id(packet)
                 src_mac = ':'.join(["%02x" % ord(x) for x in mac_id])
-                if src_mac in mac_map:
+                if src_mac in mac_map and self.is_udp(packet):
                     port, chain = mac_map[src_mac]
                     LOG.info('Received packet from mac: %s (chain=%d, port=%d)',
                              src_mac, chain, port)
@@ -618,9 +636,18 @@ class TrafficClient(object):
                 if not mac_map:
                     LOG.info('End-to-end connectivity established')
                     return
-
+            if self.config.l3_router and not self.config.no_arp:
+                # In case of L3 traffic mode, routers are not able to route traffic
+                # until VM interfaces are up and ARP requests are done
+                LOG.info('Waiting for loopback service completely started...')
+                LOG.info('Sending ARP request to assure end-to-end connectivity established')
+                self.ensure_arp_successful()
         raise TrafficClientException('End-to-end connectivity cannot be ensured')
 
+    def is_udp(self, packet):
+        pkt = Ether(packet['binary'])
+        return UDP in pkt
+
     def ensure_arp_successful(self):
         """Resolve all IP using ARP and throw an exception in case of failure."""
         dest_macs = self.gen.resolve_arp()