From 057486b092e0a4bb1989121588eb5f8afdb8e1d3 Mon Sep 17 00:00:00 2001 From: Jules Boudaud Date: Thu, 19 Dec 2019 11:18:41 +0100 Subject: NFVBENCH-158 Allow multiple UDP ports in traffic generation Change-Id: Id7c3ccad01fd9dda6c7cddb576735b429eb987a4 Signed-off-by: fmenguy --- docker/Dockerfile | 2 +- docs/testing/user/userguide/advanced.rst | 18 ++++ nfvbench/cfg.default.yaml | 23 +++++ nfvbench/traffic_client.py | 161 +++++++++++++++++++++++++++---- nfvbench/traffic_gen/trex_gen.py | 80 +++++++++++---- test/test_nfvbench.py | 148 ++++++++++++++++++++++++++-- 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']}], -- cgit 1.2.3-korg