summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docker/Dockerfile2
-rw-r--r--docs/testing/user/userguide/advanced.rst18
-rwxr-xr-xnfvbench/cfg.default.yaml23
-rwxr-xr-xnfvbench/traffic_client.py161
-rw-r--r--nfvbench/traffic_gen/trex_gen.py80
-rw-r--r--test/test_nfvbench.py148
6 files changed, 384 insertions, 48 deletions
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 50d37cf..1cb7c71 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -2,7 +2,7 @@
FROM ubuntu:16.04
ENV TREX_VER "v2.61"
-ENV VM_IMAGE_VER "0.11"
+ENV VM_IMAGE_VER "0.12"
ENV PYTHONIOENCODING "utf8"
# Note: do not clone with --depth 1 as it will cause pbr to fail extracting the nfvbench version
diff --git a/docs/testing/user/userguide/advanced.rst b/docs/testing/user/userguide/advanced.rst
index 5424a05..5307bd0 100644
--- a/docs/testing/user/userguide/advanced.rst
+++ b/docs/testing/user/userguide/advanced.rst
@@ -263,6 +263,24 @@ These can also be written in CIDR notation to represent the subnet.
The corresponding ``step`` is used for ranging the IP addresses from the `ip_addrs``, ``tg_gateway_ip_addrs`` and ``gateway_ip_addrs`` base addresses.
0.0.0.1 is the default step for all IP ranges. In ``ip_addrs``, 'random' can be configured which tells NFVBench to generate random src/dst IP pairs in the traffic stream.
+UDP ports can be controlled with the following NFVbench configuration options:
+
+.. code-block:: bash
+
+ udp_src_port: ['1024', '65000']
+ udp_dst_port: 53
+ udp_port_step: 1
+
+``udp_src_port`` and ``udp_dst_port`` are the UDP port value used by the traffic generators.
+These can be written for unique port or range ports for all flow.
+
+The corresponding ``udp_port_step`` is used for ranging the UDP port.
+1 is the default step for all UDP ranges, 'random' can be configured which tells NFVBench to generate random src/dst UDP pairs in the traffic stream.
+
+NB:
+ Use of UDP range will increase possible values of flows (based on ip src/dst and port src/dst tuple).
+ NFVBench will calculate the least common multiple for this tuple to adapt flows generation to ``flow_count`` parameter.
+
Traffic Configuration via CLI
-----------------------------
diff --git a/nfvbench/cfg.default.yaml b/nfvbench/cfg.default.yaml
index 97f98cd..d154318 100755
--- a/nfvbench/cfg.default.yaml
+++ b/nfvbench/cfg.default.yaml
@@ -163,6 +163,12 @@ traffic_generator:
# `ip_addrs_step`: step for generating IP sequence. Use "random" for random patterns, default is 0.0.0.1.
ip_addrs: ['10.0.0.0/8', '20.0.0.0/8']
ip_addrs_step: 0.0.0.1
+
+ #'ip_src_static': an attribute to precise the state of source IP during the generation of traffic, It indicates whether
+ # the IP source variate or remain constant. Use True for constant IP and False for varying IPs.
+ # default value is True
+ ip_src_static: True
+
# `tg_gateway_ip_addrs` base IP for traffic generator ports in the left and right networks to the VNFs
# chain count consecutive IP addresses spaced by tg_gateway_ip_addrs_step will be used
# `tg_gateway_ip_addrs__step`: step for generating traffic generator gateway sequences. default is 0.0.0.1
@@ -177,10 +183,27 @@ traffic_generator:
# `gateway_ip_addrs_step`: step for generating router gateway sequences. default is 0.0.0.1
gateway_ip_addrs: ['1.1.0.2', '2.2.0.2']
gateway_ip_addrs_step: 0.0.0.1
+
+ # UDP DEFINED VARIABLES
+ # TRex pick default UDP port (53) but the range of UDP source and destination ports are also
+ # defined from configuration file by using the following attributes:
+ #
# `udp_src_port`: the source port for sending UDP traffic, default is picked by TRex (53)
# `udp_dst_port`: the destination port for sending UDP traffic, default is picked by TRex (53)
+ # `udp_src_port` and `udp_dst_port` can be defined by a single port or a range. Example:
+ # udp_src_port: 80
+ # udp_dst_port: ['1024','65000']
+ # `udp_port_step`: the step between two generated ports, default is equal to '1'
+ #
+ # NOTICE:
+ # Following TRex functionalities, incrementation and decrementation of source port and destination
+ # port values occur simultaneously.
+ # So, in order to reach the highest possible number of packets, it's recommended that the range of source ports
+ # minus the range of destination ports should be different of 1
+ # i.e: |range[source_port] - range[destination_port]| = 1
udp_src_port:
udp_dst_port:
+ udp_port_step: '1'
# VxLAN only: optionally specify what VLAN tag to use for the VxLAN overlay
# This is used if the vxlan tunnels are running on a specific VLAN.
diff --git a/nfvbench/traffic_client.py b/nfvbench/traffic_client.py
index 062d414..8ef0403 100755
--- a/nfvbench/traffic_client.py
+++ b/nfvbench/traffic_client.py
@@ -15,6 +15,7 @@
"""Interface to the traffic generator clients including NDR/PDR binary search."""
from datetime import datetime
+from math import gcd
import socket
import struct
import time
@@ -126,16 +127,25 @@ class IpBlock(object):
raise IndexError('Index out of bounds: %d (max=%d)' % (index, self.max_available))
return Device.int_to_ip(self.base_ip_int + index * self.step)
- def reserve_ip_range(self, count):
- """Reserve a range of count consecutive IP addresses spaced by step."""
- if self.next_free + count > self.max_available:
+ def reserve_ip_range(self, count, force_ip_reservation=False):
+ """Reserve a range of count consecutive IP addresses spaced by step.
+ force_ip_reservation parameter allows to continue the calculation of IPs when
+ the 2 sides (ports) have different size and the flow is greater than
+ the size as well.
+ """
+ if self.next_free + count > self.max_available and force_ip_reservation is False:
raise IndexError('No more IP addresses next free=%d max_available=%d requested=%d' %
(self.next_free,
self.max_available,
count))
- first_ip = self.get_ip(self.next_free)
- last_ip = self.get_ip(self.next_free + count - 1)
- self.next_free += count
+ if self.next_free + count > self.max_available and force_ip_reservation is True:
+ first_ip = self.get_ip(self.next_free)
+ last_ip = self.get_ip(self.next_free + self.max_available - 1)
+ self.next_free += self.max_available
+ else:
+ first_ip = self.get_ip(self.next_free)
+ last_ip = self.get_ip(self.next_free + count - 1)
+ self.next_free += count
return (first_ip, last_ip)
def reset_reservation(self):
@@ -143,6 +153,17 @@ class IpBlock(object):
self.next_free = 0
+class UdpPorts(object):
+
+ def __init__(self, src_min, src_max, dst_min, dst_max, step):
+
+ self.src_min = src_min
+ self.src_max = src_max
+ self.dst_min = dst_min
+ self.dst_max = dst_max
+ self.step = step
+
+
class Device(object):
"""Represent a port device and all information associated to it.
@@ -154,7 +175,11 @@ class Device(object):
"""Create a new device for a given port."""
self.generator_config = generator_config
self.chain_count = generator_config.service_chain_count
- self.flow_count = generator_config.flow_count / 2
+ if generator_config.bidirectional:
+ self.flow_count = generator_config.flow_count / 2
+ else:
+ self.flow_count = generator_config.flow_count
+
self.port = port
self.switch_port = generator_config.interfaces[port].get('switch_port', None)
self.vtep_vlan = None
@@ -175,10 +200,58 @@ class Device(object):
self.vnis = None
self.vlans = None
self.ip_addrs = generator_config.ip_addrs[port]
- subnet = IPNetwork(self.ip_addrs)
- self.ip = subnet.ip.format()
+ self.ip_src_static = generator_config.ip_src_static
self.ip_addrs_step = generator_config.ip_addrs_step
- self.ip_block = IpBlock(self.ip, self.ip_addrs_step, self.flow_count)
+ if self.ip_addrs_step == 'random':
+ # Set step to 1 to calculate the IP range size (see check_ip_size below)
+ step = '0.0.0.1'
+ else:
+ step = self.ip_addrs_step
+ self.ip_size = self.check_ipsize(IPNetwork(self.ip_addrs).size, Device.ip_to_int(step))
+ self.ip = str(IPNetwork(self.ip_addrs).network)
+ ip_addrs_left = generator_config.ip_addrs[0]
+ ip_addrs_right = generator_config.ip_addrs[1]
+ self.ip_addrs_size = {
+ 'left': self.check_ipsize(IPNetwork(ip_addrs_left).size, Device.ip_to_int(step)),
+ 'right': self.check_ipsize(IPNetwork(ip_addrs_right).size, Device.ip_to_int(step))}
+ udp_src_port = generator_config.gen_config.udp_src_port
+ if udp_src_port is None:
+ udp_src_port = 53
+ udp_dst_port = generator_config.gen_config.udp_dst_port
+ if udp_dst_port is None:
+ udp_dst_port = 53
+ src_max, src_min = self.define_udp_range(udp_src_port, 'udp_src_port')
+ dst_max, dst_min = self.define_udp_range(udp_dst_port, 'udp_dst_port')
+ udp_src_range = int(src_max) - int(src_min) + 1
+ udp_dst_range = int(dst_max) - int(dst_min) + 1
+ lcm_port = self.lcm(udp_src_range, udp_dst_range)
+ if self.ip_src_static is True:
+ lcm_ip = self.lcm(1, min(self.ip_addrs_size['left'], self.ip_addrs_size['right']))
+ else:
+ lcm_ip = self.lcm(self.ip_addrs_size['left'], self.ip_addrs_size['right'])
+ flow_max = self.lcm(lcm_port, lcm_ip)
+ if self.flow_count > flow_max:
+ raise TrafficClientException('Trying to set unachievable traffic (%d > %d)' %
+ (self.flow_count, flow_max))
+
+ # manage udp range regarding flow count value
+ # UDP dst range is greater than FC => range will be limited to min + FC
+ if self.flow_count <= udp_dst_range:
+ dst_max = int(dst_min) + self.flow_count - 1
+ # UDP src range is greater than FC => range will be limited to min + FC
+ if self.flow_count <= udp_src_range:
+ src_max = int(src_min) + self.flow_count - 1
+ # Define IP block limit regarding flow count
+ if self.flow_count <= self.ip_size:
+ self.ip_block = IpBlock(self.ip, step, self.flow_count)
+ else:
+ self.ip_block = IpBlock(self.ip, step, self.ip_size)
+
+ if generator_config.gen_config.udp_port_step == 'random':
+ step = 1
+ else:
+ step = generator_config.gen_config.udp_port_step
+ self.udp_ports = UdpPorts(src_min, src_max, dst_min, dst_max, step)
self.gw_ip_block = IpBlock(generator_config.gateway_ips[port],
generator_config.gateway_ip_addrs_step,
self.chain_count)
@@ -186,8 +259,40 @@ class Device(object):
self.tg_gw_ip_block = IpBlock(self.tg_gateway_ip_addrs,
generator_config.tg_gateway_ip_addrs_step,
self.chain_count)
- self.udp_src_port = generator_config.udp_src_port
- self.udp_dst_port = generator_config.udp_dst_port
+
+ @staticmethod
+ def define_udp_range(udp_port, property_name):
+ if isinstance(udp_port, int):
+ min = udp_port
+ max = min
+ elif isinstance(udp_port, tuple):
+ min = udp_port[0]
+ max = udp_port[1]
+ else:
+ raise TrafficClientException('Invalid %s property value (53 or [\'53\',\'1024\'])'
+ % property_name)
+ return max, min
+
+ @staticmethod
+ def lcm(a, b):
+ """Calculate the maximum possible value for both IP and ports,
+ eventually for maximum possible flux."""
+ if a != 0 and b != 0:
+ lcm_value = a * b // gcd(a, b)
+ return lcm_value
+ raise TypeError(" IP size or port range can't be zero !")
+
+ @staticmethod
+ def check_ipsize(ip_size, step):
+ """Check and set the available IPs, considering the step."""
+ try:
+ if ip_size % step == 0:
+ value = int(ip_size / step)
+ else:
+ value = int((ip_size / step)) + 1
+ return value
+ except ZeroDivisionError:
+ raise ZeroDivisionError("step can't be zero !")
def set_mac(self, mac):
"""Set the local MAC for this port device."""
@@ -288,14 +393,27 @@ class Device(object):
# example 11 flows and 3 chains => 3, 4, 4
flows_per_chain = int((self.flow_count + self.chain_count - 1) / self.chain_count)
cur_chain_flow_count = int(self.flow_count - flows_per_chain * (self.chain_count - 1))
+ force_ip_reservation = False
+ # use case example of this parameter:
+ # - static IP addresses (source & destination), netmask = /30
+ # - 4 varying UDP source ports | 1 UDP destination port
+ # - Flow count = 8
+ # --> parameter 'reserve_ip_range' should have flag 'force_ip_reservation'
+ # in order to assign the maximum available IP on each iteration
+ if self.ip_size < cur_chain_flow_count \
+ or self.ip_addrs_size['left'] != self.ip_addrs_size['right']:
+ force_ip_reservation = True
+
peer = self.get_peer_device()
self.ip_block.reset_reservation()
peer.ip_block.reset_reservation()
dest_macs = self.get_dest_macs()
for chain_idx in range(self.chain_count):
- src_ip_first, src_ip_last = self.ip_block.reserve_ip_range(cur_chain_flow_count)
- dst_ip_first, dst_ip_last = peer.ip_block.reserve_ip_range(cur_chain_flow_count)
+ src_ip_first, src_ip_last = self.ip_block.reserve_ip_range\
+ (cur_chain_flow_count, force_ip_reservation)
+ dst_ip_first, dst_ip_last = peer.ip_block.reserve_ip_range\
+ (cur_chain_flow_count, force_ip_reservation)
configs.append({
'count': cur_chain_flow_count,
@@ -308,8 +426,14 @@ class Device(object):
'ip_dst_addr_max': dst_ip_last,
'ip_dst_count': cur_chain_flow_count,
'ip_addrs_step': self.ip_addrs_step,
- 'udp_src_port': self.udp_src_port,
- 'udp_dst_port': self.udp_dst_port,
+ 'ip_src_static': self.ip_src_static,
+ 'udp_src_port': self.udp_ports.src_min,
+ 'udp_src_port_max': self.udp_ports.src_max,
+ 'udp_src_count': int(self.udp_ports.src_max) - int(self.udp_ports.src_min) + 1,
+ 'udp_dst_port': self.udp_ports.dst_min,
+ 'udp_dst_port_max': self.udp_ports.dst_max,
+ 'udp_dst_count': int(self.udp_ports.dst_max) - int(self.udp_ports.dst_min) + 1,
+ 'udp_port_step': self.udp_ports.step,
'mac_discovery_gw': self.get_gw_ip(chain_idx),
'ip_src_tg_gw': self.tg_gw_ip_block.get_ip(chain_idx),
'ip_dst_tg_gw': peer.tg_gw_ip_block.get_ip(chain_idx),
@@ -387,7 +511,7 @@ class GeneratorConfig(object):
self.service_chain_count = config.service_chain_count
self.flow_count = config.flow_count
self.host_name = gen_config.host_name
-
+ self.bidirectional = config.traffic.bidirectional
self.tg_gateway_ip_addrs = gen_config.tg_gateway_ip_addrs
self.ip_addrs = gen_config.ip_addrs
self.ip_addrs_step = gen_config.ip_addrs_step or self.DEFAULT_SRC_DST_IP_STEP
@@ -395,8 +519,7 @@ class GeneratorConfig(object):
gen_config.tg_gateway_ip_addrs_step or self.DEFAULT_IP_STEP
self.gateway_ip_addrs_step = gen_config.gateway_ip_addrs_step or self.DEFAULT_IP_STEP
self.gateway_ips = gen_config.gateway_ip_addrs
- self.udp_src_port = gen_config.udp_src_port
- self.udp_dst_port = gen_config.udp_dst_port
+ self.ip_src_static = gen_config.ip_src_static
self.vteps = gen_config.get('vteps')
self.devices = [Device(port, self) for port in [0, 1]]
# This should normally always be [0, 1]
diff --git a/nfvbench/traffic_gen/trex_gen.py b/nfvbench/traffic_gen/trex_gen.py
index af70cde..189c3e5 100644
--- a/nfvbench/traffic_gen/trex_gen.py
+++ b/nfvbench/traffic_gen/trex_gen.py
@@ -387,19 +387,28 @@ class TRex(AbstractTrafficGenerator):
udp_args = {}
if stream_cfg['udp_src_port']:
udp_args['sport'] = int(stream_cfg['udp_src_port'])
+ udp_args['sport_step'] = int(stream_cfg['udp_port_step'])
+ udp_args['sport_max'] = int(stream_cfg['udp_src_port_max'])
if stream_cfg['udp_dst_port']:
udp_args['dport'] = int(stream_cfg['udp_dst_port'])
- pkt_base /= IP() / UDP(**udp_args)
+ udp_args['dport_step'] = int(stream_cfg['udp_port_step'])
+ udp_args['dport_max'] = int(stream_cfg['udp_dst_port_max'])
+ pkt_base /= IP(src=stream_cfg['ip_src_addr'], dst=stream_cfg['ip_dst_addr']) / \
+ UDP(dport=udp_args['dport'], sport=udp_args['sport'])
+ if stream_cfg['ip_src_static'] is True:
+ src_max_ip_value = stream_cfg['ip_src_addr']
+ else:
+ src_max_ip_value = stream_cfg['ip_src_addr_max']
if stream_cfg['ip_addrs_step'] == 'random':
- src_fv = STLVmFlowVarRepeatableRandom(
+ src_fv_ip = STLVmFlowVarRepeatableRandom(
name="ip_src",
min_value=stream_cfg['ip_src_addr'],
- max_value=stream_cfg['ip_src_addr_max'],
+ max_value=src_max_ip_value,
size=4,
seed=random.randint(0, 32767),
limit=stream_cfg['ip_src_count'])
- dst_fv = STLVmFlowVarRepeatableRandom(
+ dst_fv_ip = STLVmFlowVarRepeatableRandom(
name="ip_dst",
min_value=stream_cfg['ip_dst_addr'],
max_value=stream_cfg['ip_dst_addr_max'],
@@ -407,14 +416,14 @@ class TRex(AbstractTrafficGenerator):
seed=random.randint(0, 32767),
limit=stream_cfg['ip_dst_count'])
else:
- src_fv = STLVmFlowVar(
+ src_fv_ip = STLVmFlowVar(
name="ip_src",
min_value=stream_cfg['ip_src_addr'],
- max_value=stream_cfg['ip_src_addr'],
+ max_value=src_max_ip_value,
size=4,
op="inc",
step=stream_cfg['ip_addrs_step'])
- dst_fv = STLVmFlowVar(
+ dst_fv_ip = STLVmFlowVar(
name="ip_dst",
min_value=stream_cfg['ip_dst_addr'],
max_value=stream_cfg['ip_dst_addr_max'],
@@ -422,18 +431,49 @@ class TRex(AbstractTrafficGenerator):
op="inc",
step=stream_cfg['ip_addrs_step'])
- vm_param.extend([
- src_fv,
+ if stream_cfg['udp_port_step'] == 'random':
+ src_fv_port = STLVmFlowVarRepeatableRandom(
+ name="p_src",
+ min_value=udp_args['sport'],
+ max_value=udp_args['sport_max'],
+ size=2,
+ seed=random.randint(0, 32767),
+ limit=udp_args['udp_src_count'])
+ dst_fv_port = STLVmFlowVarRepeatableRandom(
+ name="p_dst",
+ min_value=udp_args['dport'],
+ max_value=udp_args['dport_max'],
+ size=2,
+ seed=random.randint(0, 32767),
+ limit=stream_cfg['udp_dst_count'])
+ else:
+ src_fv_port = STLVmFlowVar(
+ name="p_src",
+ min_value=udp_args['sport'],
+ max_value=udp_args['sport_max'],
+ size=2,
+ op="inc",
+ step=udp_args['sport_step'])
+ dst_fv_port = STLVmFlowVar(
+ name="p_dst",
+ min_value=udp_args['dport'],
+ max_value=udp_args['dport_max'],
+ size=2,
+ op="inc",
+ step=udp_args['dport_step'])
+ vm_param = [
+ src_fv_ip,
STLVmWrFlowVar(fv_name="ip_src", pkt_offset="IP:{}.src".format(encap_level)),
- dst_fv,
- STLVmWrFlowVar(fv_name="ip_dst", pkt_offset="IP:{}.dst".format(encap_level))
- ])
-
- for encap in range(int(encap_level), -1, -1):
- # Fixing the checksums for all encap levels
- vm_param.append(STLVmFixChecksumHw(l3_offset="IP:{}".format(encap),
- l4_offset="UDP:{}".format(encap),
- l4_type=CTRexVmInsFixHwCs.L4_TYPE_UDP))
+ src_fv_port,
+ STLVmWrFlowVar(fv_name="p_src", pkt_offset="UDP:{}.sport".format(encap_level)),
+ dst_fv_ip,
+ STLVmWrFlowVar(fv_name="ip_dst", pkt_offset="IP:{}.dst".format(encap_level)),
+ dst_fv_port,
+ STLVmWrFlowVar(fv_name="p_dst", pkt_offset="UDP:{}.dport".format(encap_level)),
+ STLVmFixChecksumHw(l3_offset="IP:{}".format(encap_level),
+ l4_offset="UDP:{}".format(encap_level),
+ l4_type=CTRexVmInsFixHwCs.L4_TYPE_UDP)
+ ]
pad = max(0, frame_size - len(pkt_base)) * 'x'
return STLPktBuilder(pkt=pkt_base / pad,
@@ -849,8 +889,8 @@ class TRex(AbstractTrafficGenerator):
bpf_filter = "ether dst %s or ether dst %s" % (src_mac_list[0], src_mac_list[1])
# ports must be set in service in order to enable capture
self.client.set_service_mode(ports=self.port_handle)
- self.capture_id = self.client.start_capture(rx_ports=self.port_handle,
- bpf_filter=bpf_filter)
+ self.capture_id = self.client.start_capture \
+ (rx_ports=self.port_handle, bpf_filter=bpf_filter)
def fetch_capture_packets(self):
"""Fetch capture packets in capture mode."""
diff --git a/test/test_nfvbench.py b/test/test_nfvbench.py
index fa0e098..5274557 100644
--- a/test/test_nfvbench.py
+++ b/test/test_nfvbench.py
@@ -13,14 +13,15 @@
# License for the specific language governing permissions and limitations
# under the License.
#
+from mock import patch
+import pytest
+
+from .mock_trex import no_op
+
import json
import logging
import sys
-
from attrdict import AttrDict
-from mock import patch
-import pytest
-
from nfvbench.config import config_loads
from nfvbench.credentials import Credentials
from nfvbench.fluentd import FluentLogHandler
@@ -32,7 +33,6 @@ from nfvbench.traffic_client import IpBlock
from nfvbench.traffic_client import TrafficClient
import nfvbench.traffic_gen.traffic_utils as traffic_utils
-from .mock_trex import no_op
# just to get rid of the unused function warning
no_op()
@@ -173,6 +173,63 @@ def test_ip_block():
with pytest.raises(IndexError):
ipb.get_ip(256)
+ ipb = IpBlock('10.0.0.0', '0.0.0.2', 128)
+ assert ipb.get_ip() == '10.0.0.0'
+ assert ipb.get_ip(1) == '10.0.0.2'
+ assert ipb.get_ip(127) == '10.0.0.254'
+ with pytest.raises(IndexError):
+ ipb.get_ip(128)
+
+ ipb = IpBlock('10.0.0.0', '0.0.0.4', 64)
+ assert ipb.get_ip() == '10.0.0.0'
+ assert ipb.get_ip(1) == '10.0.0.4'
+ assert ipb.get_ip(63) == '10.0.0.252'
+ with pytest.raises(IndexError):
+ ipb.get_ip(64)
+
+ ipb = IpBlock('10.0.0.0', '0.0.0.10', 1)
+ assert ipb.get_ip() == '10.0.0.0'
+ with pytest.raises(IndexError):
+ ipb.get_ip(1)
+
+
+def test_lcm():
+ assert Device.lcm(10, 2) == 10
+ assert Device.lcm(1, 256) == 256
+ assert Device.lcm(10, 256) == 1280
+ assert Device.lcm(Device.lcm(10, 2), Device.lcm(1, 256))
+ with pytest.raises(TypeError):
+ Device.lcm(0, 0)
+
+
+def test_check_ipsize():
+ assert Device.check_ipsize(256, 1) == 256
+ assert Device.check_ipsize(256, 3) == 86
+ assert Device.check_ipsize(256, 4) == 64
+ assert Device.check_ipsize(16, 10) == 2
+ assert Device.check_ipsize(1, 10) == 1
+ with pytest.raises(ZeroDivisionError):
+ Device.check_ipsize(256, 0)
+
+
+def test_reserve_ip_range():
+ ipb = IpBlock('10.0.0.0', '0.0.0.1', 256)
+ src_ip_first, src_ip_last = ipb.reserve_ip_range(256, False)
+ assert src_ip_first == "10.0.0.0"
+ assert src_ip_last == "10.0.0.255"
+ ipb = IpBlock('20.0.0.0', '0.0.0.1', 2)
+ src_ip_first, src_ip_last = ipb.reserve_ip_range(2, False)
+ assert src_ip_first == "20.0.0.0"
+ assert src_ip_last == "20.0.0.1"
+ ipb = IpBlock('30.0.0.0', '0.0.0.1', 2)
+ src_ip_first, src_ip_last = ipb.reserve_ip_range(256, True)
+ assert src_ip_first == "30.0.0.0"
+ assert src_ip_last == "30.0.0.1"
+ ipb = IpBlock('40.0.0.0', '0.0.0.1', 2)
+ with pytest.raises(IndexError):
+ ipb.reserve_ip_range(256, False)
+
+
def check_stream_configs(gen_config):
"""Verify that the range for each chain have adjacent IP ranges without holes between chains."""
config = gen_config.config
@@ -202,6 +259,78 @@ def test_device_flow_config():
_check_device_flow_config('0.0.0.1')
_check_device_flow_config('0.0.0.2')
+
+def check_udp_stream_configs(gen_config, expected):
+ """Verify that the range for each chain have adjacent UDP ports without holes between chains."""
+ config = gen_config.config
+ stream_configs = gen_config.devices[0].get_stream_configs()
+ for index in range(config['service_chain_count']):
+ stream_cfg = stream_configs[index]
+ assert stream_cfg['ip_src_addr'] == expected['ip_src_addr']
+ assert stream_cfg['ip_src_addr_max'] == expected['ip_src_addr_max']
+ assert stream_cfg['ip_dst_addr'] == expected['ip_dst_addr']
+ assert stream_cfg['ip_dst_addr_max'] == expected['ip_dst_addr_max']
+ assert stream_cfg['udp_src_port'] == expected['udp_src_port']
+ assert stream_cfg['udp_src_port_max'] == expected['udp_src_port_max']
+ assert stream_cfg['udp_src_count'] == expected['udp_src_count']
+ assert stream_cfg['udp_dst_port'] == expected['udp_dst_port']
+ assert stream_cfg['udp_dst_port_max'] == expected['udp_dst_port_max']
+ assert stream_cfg['udp_dst_count'] == expected['udp_dst_count']
+
+
+def _check_device_udp_flow_config(param, expected):
+ config = _get_dummy_tg_config('PVP', '1Mpps',
+ ip_src_static=param['ip_src_static'],
+ fc=param['flow_count'],
+ ip0=param['ip_src_addr'],
+ ip1=param['ip_dst_addr'],
+ src_udp=param['udp_src_port'],
+ dst_udp=param['udp_dst_port'])
+ gen_config = GeneratorConfig(config)
+ check_udp_stream_configs(gen_config, expected)
+
+
+def test_device_udp_flow_config():
+ param = {'ip_src_static': True,
+ 'ip_src_addr': '110.0.0.0/32',
+ 'ip_dst_addr': '120.0.0.0/32',
+ 'udp_src_port': 53,
+ 'udp_dst_port': 53,
+ 'flow_count': 2}
+ expected = {'ip_src_addr': '110.0.0.0',
+ 'ip_src_addr_max': '110.0.0.0',
+ 'ip_dst_addr': '120.0.0.0',
+ 'ip_dst_addr_max': '120.0.0.0',
+ 'udp_src_port': 53,
+ 'udp_src_port_max': 53,
+ 'udp_src_count': 1,
+ 'udp_dst_port': 53,
+ 'udp_dst_port_max': 53,
+ 'udp_dst_count': 1}
+ _check_device_udp_flow_config(param, expected)
+ # Overwrite the udp_src_port value to define a large range of ports
+ # instead of a single port, in order to check if the imposed
+ # flow count is respected. Notice that udp range >> flow count.
+ param['udp_src_port'] = [53, 1024]
+ param['flow_count'] = 10
+ expected['udp_src_port_max'] = 57
+ expected['udp_src_count'] = 5
+ _check_device_udp_flow_config(param, expected)
+ # Re affect the default udp_src_port values and
+ # overwrite the ip_dst_addr value to define a large range of addresses
+ # instead of a single one, in order to check if the imposed
+ # flow count is respected. Notice that the netmask allows a very larger
+ # range of possible addresses than the flow count value.
+ param['udp_src_port'] = 53
+ expected['udp_src_port_max'] = 53
+ expected['udp_src_count'] = 1
+ param['ip_src_static'] = False
+ param['ip_dst_addr'] = '120.0.0.0/24'
+ expected['ip_dst_addr'] = '120.0.0.0'
+ expected['ip_dst_addr_max'] = '120.0.0.4'
+ _check_device_udp_flow_config(param, expected)
+
+
def test_config():
refcfg = {1: 100, 2: {21: 100, 22: 200}, 3: None}
res1 = {1: 10, 2: {21: 100, 22: 200}, 3: None}
@@ -290,7 +419,8 @@ def assert_ndr_pdr(stats, ndr, ndr_dr, pdr, pdr_dr):
assert_equivalence(pdr_dr, stats['pdr']['stats']['overall']['drop_percentage'])
def _get_dummy_tg_config(chain_type, rate, scc=1, fc=10, step_ip='0.0.0.1',
- ip0='10.0.0.0/8', ip1='20.0.0.0/8'):
+ ip0='10.0.0.0/8', ip1='20.0.0.0/8',
+ step_udp=1, src_udp=None, dst_udp=None, ip_src_static=True):
return AttrDict({
'traffic_generator': {'host_name': 'nfvbench_tg',
'default_profile': 'dummy',
@@ -302,14 +432,16 @@ def _get_dummy_tg_config(chain_type, rate, scc=1, fc=10, step_ip='0.0.0.1',
{'port': 1, 'pci': '0.0'}]}],
'ip_addrs_step': step_ip,
'ip_addrs': [ip0, ip1],
+ 'ip_src_static': ip_src_static,
'tg_gateway_ip_addrs': ['1.1.0.100', '2.2.0.100'],
'tg_gateway_ip_addrs_step': step_ip,
'gateway_ip_addrs': ['1.1.0.2', '2.2.0.2'],
'gateway_ip_addrs_step': step_ip,
'mac_addrs_left': None,
'mac_addrs_right': None,
- 'udp_src_port': None,
- 'udp_dst_port': None},
+ 'udp_src_port': src_udp,
+ 'udp_dst_port': dst_udp,
+ 'udp_port_step': step_udp},
'traffic': {'profile': 'profile_64',
'bidirectional': True},
'traffic_profile': [{'name': 'profile_64', 'l2frame_size': ['64']}],