diff options
author | fmenguy <francoisregis.menguy@orange.com> | 2020-10-08 11:46:55 +0200 |
---|---|---|
committer | fmenguy <francoisregis.menguy@orange.com> | 2020-10-16 11:56:33 +0200 |
commit | f2d211ad71a54286b1179434744abb7bbc2598b4 (patch) | |
tree | 3512f40301b58417642a4904e487d44693f86f35 | |
parent | 94845d2bf7416d8b59e2eaf017244832cf3277f4 (diff) |
NFVBENCH-171 Not accurate flow count with some IP and UDP ranges combinations
Change-Id: Ic68db4ee54c508ebb24ca2e605dcec2a6b2fb3bf
Signed-off-by: fmenguy <francoisregis.menguy@orange.com>
-rw-r--r-- | docs/testing/user/userguide/advanced.rst | 294 | ||||
-rwxr-xr-x | nfvbench/traffic_client.py | 275 | ||||
-rw-r--r-- | nfvbench/traffic_gen/trex_gen.py | 223 | ||||
-rw-r--r-- | nfvbench/utils.py | 44 | ||||
-rw-r--r-- | test/mock_trex.py | 1 | ||||
-rw-r--r-- | test/test_nfvbench.py | 677 |
6 files changed, 1320 insertions, 194 deletions
diff --git a/docs/testing/user/userguide/advanced.rst b/docs/testing/user/userguide/advanced.rst index 62d17b6..d7dc53f 100644 --- a/docs/testing/user/userguide/advanced.rst +++ b/docs/testing/user/userguide/advanced.rst @@ -321,7 +321,7 @@ UDP ports can be controlled with the following NFVbench configuration options: udp_src_port: ['1024', '65000'] udp_dst_port: 53 - udp_port_step: 1 + 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. @@ -333,6 +333,248 @@ 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. +Examples of multiflow +^^^^^^^^^^^^^^^^^^^^^ + +1. Source IP is static and one UDP port used (default configuration) + +NFVbench configuration options: + +.. code-block:: bash + + ip_addrs: ['110.0.0.0/8', '120.0.0.0/8'] + ip_addrs_step: 0.0.0.1 + tg_gateway_ip_addrs: ['1.1.0.100', '2.2.0.100'] + tg_gateway_ip_addrs_step: 0.0.0.1 + gateway_ip_addrs: ['1.1.0.2', '2.2.0.2'] + gateway_ip_addrs_step: 0.0.0.1 + +To run NFVbench with 3 chains and 100 flows, use the following command: + +.. code-block:: bash + + nfvbench -c nfvbench.cfg --rate 10000pps -scc 3 -fc 100 + +The least common multiple for this configuration is lcm(16 777 216, 16 777 216, 1, 1) = 16 777 216. +.. note:: LCM method used IP pools sizes and UDP source and destination range sizes + +Requested flow count is lower than configuration capacity. So, NFVbench will limit IP range to generate accurate flows: + +.. code-block:: bash + + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: IP src range [110.0.0.0,110.0.0.0] + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: IP dst range [120.0.0.0,120.0.0.15] + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: UDP src range [53,53] + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: UDP dst range [53,53] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: IP src range [120.0.0.0,120.0.0.0] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: IP dst range [110.0.0.0,110.0.0.15] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: UDP src range [53,53] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: UDP dst range [53,53] + + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: IP src range [110.0.0.1,110.0.0.1] + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: IP dst range [120.0.0.16,120.0.0.32] + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: UDP src range [53,53] + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: UDP dst range [53,53] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: IP src range [120.0.0.1,120.0.0.1] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: IP dst range [110.0.0.16,110.0.0.32] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: UDP src range [53,53] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: UDP dst range [53,53] + + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: IP src range [110.0.0.2,110.0.0.2] + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: IP dst range [120.0.0.33,120.0.0.49] + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: UDP src range [53,53] + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: UDP dst range [53,53] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: IP src range [120.0.0.2,120.0.0.2] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: IP dst range [110.0.0.33,110.0.0.49] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: UDP src range [53,53] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: UDP dst range [53,53] + + +2. Source IP is static, IP step is random and one UDP port used + +NFVbench configuration options: + +.. code-block:: bash + + ip_addrs: ['110.0.0.0/8', '120.0.0.0/8'] + ip_addrs_step: 'random' + tg_gateway_ip_addrs: ['1.1.0.100', '2.2.0.100'] + tg_gateway_ip_addrs_step: 0.0.0.1 + gateway_ip_addrs: ['1.1.0.2', '2.2.0.2'] + gateway_ip_addrs_step: 0.0.0.1 + +To run NFVbench with 3 chains and 100 flows, use the following command: + +.. code-block:: bash + + nfvbench -c nfvbench.cfg --rate 10000pps -scc 3 -fc 100 + +The least common multiple for this configuration is lcm(16 777 216, 16 777 216, 1, 1) = 16 777 216. +.. note:: LCM method used IP pools sizes and UDP source and destination range sizes + +Requested flow count is lower than configuration capacity. So, NFVbench will limit IP range to generate accurate flows: + +.. code-block:: bash + + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: IP src range [110.0.0.0,110.0.0.0] + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: IP dst range [120.0.0.0,120.0.0.15] + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: UDP src range [53,53] + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: UDP dst range [53,53] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: IP src range [120.0.0.0,120.0.0.0] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: IP dst range [110.0.0.0,110.0.0.15] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: UDP src range [53,53] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: UDP dst range [53,53] + + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: IP src range [110.0.0.1,110.0.0.1] + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: IP dst range [120.0.0.16,120.0.0.32] + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: UDP src range [53,53] + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: UDP dst range [53,53] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: IP src range [120.0.0.1,120.0.0.1] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: IP dst range [110.0.0.16,110.0.0.32] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: UDP src range [53,53] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: UDP dst range [53,53] + + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: IP src range [110.0.0.2,110.0.0.2] + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: IP dst range [120.0.0.33,120.0.0.49] + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: UDP src range [53,53] + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: UDP dst range [53,53] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: IP src range [120.0.0.2,120.0.0.2] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: IP dst range [110.0.0.33,110.0.0.49] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: UDP src range [53,53] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: UDP dst range [53,53] + 2020-06-17 07:39:47,015 WARNING Using random step, the number of flows can be less than the requested number of flows due to repeatable multivariate random generation which can reproduce the same pattern of values + +By using a random step the number of generated flows may be less than the number of requested flows. This is due to the probability of drawing the same value several times (Bernouillian drawing) from the IP range used and thus generating the same flow sequence. +By using a high range of UDP ports couple with ``udp_port_step='random'`` the probability to reach the requested flow counts is greater. +As latency stream is a separate stream than data one and have his own random draw, NFVbench will use only one packet signature (same IP and ports used for all latency packets) to avoid flow count overflow. +So in some cases, generated flow count can be equal to the requested flow count + 1 (latency stream). + +**For deterministic flow count we recommend to use a step different from random.** + + +3. Source IP is static, IP step is 5 and one UDP port used + +NFVbench configuration options: + +.. code-block:: bash + + ip_addrs: ['110.0.0.0/8', '120.0.0.0/8'] + ip_addrs_step: '0.0.0.5' + tg_gateway_ip_addrs: ['1.1.0.100', '2.2.0.100'] + tg_gateway_ip_addrs_step: 0.0.0.1 + gateway_ip_addrs: ['1.1.0.2', '2.2.0.2'] + gateway_ip_addrs_step: 0.0.0.1 + +To run NFVbench with 3 chains and 100 flows, use the following command: + +.. code-block:: bash + + nfvbench -c nfvbench.cfg --rate 10000pps -scc 3 -fc 100 + +The least common multiple for this configuration is lcm(16 777 216, 16 777 216, 1, 1) = 16 777 216. +.. note:: LCM method used IP pools sizes and UDP source and destination range sizes + +Requested flow count is lower than configuration capacity. So, NFVbench will limit IP range to generate accurate flows: + +.. code-block:: bash + + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: IP src range [110.0.0.0,110.0.0.0] + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: IP dst range [120.0.0.0,120.0.0.75] + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: UDP src range [53,53] + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: UDP dst range [53,53] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: IP src range [120.0.0.0,120.0.0.0] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: IP dst range [110.0.0.0,110.0.0.75] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: UDP src range [53,53] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: UDP dst range [53,53] + + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: IP src range [110.0.0.5,110.0.0.5] + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: IP dst range [120.0.0.80,120.0.0.160] + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: UDP src range [53,53] + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: UDP dst range [53,53] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: IP src range [120.0.0.5,120.0.0.5] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: IP dst range [110.0.0.80,110.0.0.160] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: UDP src range [53,53] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: UDP dst range [53,53] + + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: IP src range [110.0.0.10,110.0.0.10] + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: IP dst range [120.0.0.165,120.0.0.245] + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: UDP src range [53,53] + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: UDP dst range [53,53] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: IP src range [120.0.0.10,120.0.0.10] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: IP dst range [110.0.0.165,110.0.0.245] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: UDP src range [53,53] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: UDP dst range [53,53] + +4. Source IP is static, IP and UDP ranges sizes greater than requested flow count, UDP step is random + +NFVbench configuration options: + +.. code-block:: bash + + ip_addrs: ['110.0.0.0/29', '120.0.0.0/30'] + tg_gateway_ip_addrs: ['1.1.0.100', '2.2.0.100'] + tg_gateway_ip_addrs_step: 0.0.0.1 + gateway_ip_addrs: ['1.1.0.2', '2.2.0.2'] + gateway_ip_addrs_step: 0.0.0.1 + udp_src_port: ['10', '14'] + udp_dst_port: ['20', '25'] + udp_port_step: 'random' + +To run NFVbench with 3 chains and 100 flows, use the following command: + +.. code-block:: bash + + nfvbench -c nfvbench.cfg --rate 10000pps -scc 3 -fc 100 + +The least common multiple for this configuration is lcm(8, 4, 5, 6) = 120. +.. note:: LCM method used IP pools sizes and UDP source and destination range sizes + +Requested flow count is higher than IP range (8 and 4 IP addresses available) and UDP (5 and 6 ports available) configuration capacity. +As the combination of ranges does not permit to obtain an accurate flow count, NFVbench will override the `udp_port_step` property to '1' (was 'random') to allow flows creation. +A warning log will appear to inform NFVbench user that step properties will be overriden +So, NFVbench will determine each pool size to generate accurate flows: + +.. code-block:: bash + + 2020-06-17 07:37:47,010 WARNING Current values of ip_addrs_step and/or udp_port_step properties do not allow to control an accurate flow count. Values will be overridden as follows: + 2020-06-17 07:37:47,011 INFO udp_port_step='1' (previous value: udp_port_step='random') + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: IP src range [110.0.0.0,110.0.0.0] + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: IP dst range [120.0.0.0,120.0.0.0] + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: UDP src range [10,14] + 2020-06-17 07:37:47,012 INFO Port 0, chain 0: UDP dst range [20,25] + 2020-06-17 07:37:47,013 WARNING Current values of ip_addrs_step and/or udp_port_step properties do not allow to control an accurate flow count. Values will be overridden as follows: + 2020-06-17 07:37:47,013 INFO udp_port_step='1' (previous value: udp_port_step='random' + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: IP src range [120.0.0.0,120.0.0.0] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: IP dst range [110.0.0.0,110.0.0.0] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: UDP src range [10,14] + 2020-06-17 07:37:47,015 INFO Port 1, chain 0: UDP dst range [20,25] + + 2020-06-17 07:38:47,010 WARNING Current values of ip_addrs_step and/or udp_port_step properties do not allow to control an accurate flow count. Values will be overridden as follows: + 2020-06-17 07:38:47,011 INFO udp_port_step='1' (previous value: udp_port_step='random' + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: IP src range [110.0.0.1,110.0.0.1] + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: IP dst range [120.0.0.1,120.0.0.1] + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: UDP src range [10,14] + 2020-06-17 07:38:47,012 INFO Port 0, chain 1: UDP dst range [20,25] + 2020-06-17 07:38:47,013 WARNING Current values of ip_addrs_step and/or udp_port_step properties do not allow to control an accurate flow count. Values will be overridden as follows: + 2020-06-17 07:38:47,013 INFO udp_port_step='1' (previous value: udp_port_step='random' + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: IP src range [120.0.0.1,120.0.0.1] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: IP dst range [110.0.0.1,110.0.0.1] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: UDP src range [10,14] + 2020-06-17 07:38:47,015 INFO Port 1, chain 1: UDP dst range [20,25] + + 2020-06-17 07:39:47,010 WARNING Current values of ip_addrs_step and/or udp_port_step properties do not allow to control an accurate flow count. Values will be overridden as follows: + 2020-06-17 07:39:47,011 INFO udp_port_step='1' (previous value: udp_port_step='random' + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: IP src range [110.0.0.2,110.0.0.2] + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: IP dst range [120.0.0.2,120.0.0.2] + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: UDP src range [10,14] + 2020-06-17 07:39:47,012 INFO Port 0, chain 2: UDP dst range [20,25] + 2020-06-17 07:39:47,013 WARNING Current values of ip_addrs_step and/or udp_port_step properties do not allow to control an accurate flow count. Values will be overridden as follows: + 2020-06-17 07:39:47,013 INFO udp_port_step='1' (previous value: udp_port_step='random' + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: IP src range [120.0.0.2,120.0.0.2] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: IP dst range [110.0.0.2,110.0.0.2] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: UDP src range [10,14] + 2020-06-17 07:39:47,015 INFO Port 1, chain 2: UDP dst range [20,25] + Traffic Configuration via CLI ----------------------------- @@ -436,7 +678,7 @@ Example of use : .. note:: It is preferable to define the minimum rate (2002 pps) to have a better capture -In another bash window, you should connect to the TRex console doing : +In another bash window, you should connect to the TRex console doing: .. code-block:: bash @@ -448,7 +690,7 @@ In another bash window, you should connect to the TRex console doing : # to stop capture capture monitor stop -Start this capture once you have started the NFVBench test, and you will observe packets on the TRex console : +Start this capture once you have started the NFVBench test, and you will observe packets on the TRex console: .. code-block:: bash @@ -492,7 +734,7 @@ Start this capture once you have started the NFVBench test, and you will observe ###[ Padding ]### load = '6\x85' -Check on the NFVBench window that the following log appears just before the testing phase : +Check on the NFVBench window that the following log appears just before the testing phase: .. code-block:: bash @@ -502,6 +744,50 @@ Check on the NFVBench window that the following log appears just before the test 2019-10-21 09:38:52,552 INFO TX: 2004; RX: 2003; Est. Dropped: 1; Est. Drop rate: 0.0499% 2019-10-21 09:38:53,559 INFO TX: 4013; RX: 4011; Est. Dropped: 2; Est. Drop rate: 0.0498% +Recording packet using service mode for TRex +-------------------------------------------- + +Check on the NFVBench window that the following log appears just before the testing phase: + +.. code-block:: bash + + 2019-10-21 09:38:51,532 INFO Starting to generate traffic... + 2019-10-21 09:38:51,532 INFO Running traffic generator + 2019-10-21 09:38:51,541 INFO ``Service mode is enabled`` + 2019-10-21 09:38:52,552 INFO TX: 2004; RX: 2003; Est. Dropped: 1; Est. Drop rate: 0.0499% + +In another bash window, you should connect to the TRex console doing : + +.. code-block:: bash + + cd /opt/trex/vX.XX/ #use completion here to find your corresponding TRex version + ./trex-console -r + capture record start --rx [port number] --limit 10000 +.. note::Start this capture once traffic generation is started (after ``Service mode is enabled`` log) + +Check on the TRex window that the following log appears just after capture is started: + +.. code-block:: bash + + Starting packet capturing up to 10000 packets [SUCCESS] + *** Capturing ID is set to '8' *** + *** Please call 'capture record stop --id 8 -o <out.pcap>' when done *** + +Then **before end of traffic generation**, stop capture and save it as a PCAP file: + +.. code-block:: bash + + capture record stop --id 8 -o /tmp/nfvb/record.pcap +.. note:: Provide a shared path with between NFVbench container and your host to retrieve pcap file + +Check on the TRex window that the following log appears just after capture is started: + +.. code-block:: bash + + Stopping packet capture 8 [SUCCESS] + Writing up to 10000 packets to '/tmp/nfvb/record.pcap' [SUCCESS] + Removing PCAP capture 8 from server [SUCCESS] + User info data -------------- diff --git a/nfvbench/traffic_client.py b/nfvbench/traffic_client.py index 72fbb5d..c711cfb 100755 --- a/nfvbench/traffic_client.py +++ b/nfvbench/traffic_client.py @@ -13,7 +13,6 @@ # under the License. """Interface to the traffic generator clients including NDR/PDR binary search.""" -from math import gcd import socket import struct import time @@ -37,7 +36,7 @@ from .packet_stats import PacketPathStats from .stats_collector import IntervalCollector from .stats_collector import IterationCollector from .traffic_gen import traffic_utils as utils -from .utils import cast_integer +from .utils import cast_integer, find_max_size, find_tuples_equal_to_lcm_value, get_divisors, lcm class TrafficClientException(Exception): """Generic traffic client exception.""" @@ -116,6 +115,8 @@ class IpBlock(object): def __init__(self, base_ip, step_ip, count_ip): """Create an IP block.""" self.base_ip_int = Device.ip_to_int(base_ip) + if step_ip == 'random': + step_ip = '0.0.0.1' self.step = Device.ip_to_int(step_ip) self.max_available = count_ip self.next_free = 0 @@ -126,25 +127,23 @@ 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, force_ip_reservation=False): + def get_ip_from_chain_first_ip(self, first_ip, index=0): + """Return the IP address at given index starting from chain first ip.""" + if index < 0 or index >= self.max_available: + raise IndexError('Index out of bounds: %d (max=%d)' % (index, self.max_available)) + return Device.int_to_ip(first_ip + index * self.step) + + def reserve_ip_range(self, count): """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: + if self.next_free + count > self.max_available: raise IndexError('No more IP addresses next free=%d max_available=%d requested=%d' % (self.next_free, self.max_available, 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 + 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): @@ -154,14 +153,24 @@ class IpBlock(object): class UdpPorts(object): - def __init__(self, src_min, src_max, dst_min, dst_max, step): + def __init__(self, src_min, src_max, dst_min, dst_max, udp_src_size, udp_dst_size, step): - self.src_min = src_min - self.src_max = src_max - self.dst_min = dst_min - self.dst_max = dst_max + self.src_min = int(src_min) + self.src_max = int(src_max) + self.dst_min = int(dst_min) + self.dst_max = int(dst_max) + self.udp_src_size = udp_src_size + self.udp_dst_size = udp_dst_size self.step = step + def get_src_max(self, index=0): + """Return the UDP src port at given index.""" + return int(self.src_min) + index * int(self.step) + + def get_dst_max(self, index=0): + """Return the UDP dst port at given index.""" + return int(self.dst_min) + index * int(self.step) + class Device(object): """Represent a port device and all information associated to it. @@ -202,17 +211,17 @@ class Device(object): self.ip_src_static = generator_config.ip_src_static self.ip_addrs_step = generator_config.ip_addrs_step if self.ip_addrs_step == 'random': - # Set step to 1 to calculate the IP range size (see check_ip_size below) + # Set step to 1 to calculate the IP range size (see check_range_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_size = self.check_range_size(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))} + 'left': self.check_range_size(IPNetwork(ip_addrs_left).size, Device.ip_to_int(step)), + 'right': self.check_range_size(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 @@ -221,33 +230,28 @@ class Device(object): 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 generator_config.gen_config.udp_port_step == 'random': + # Set step to 1 to calculate the UDP range size + udp_step = 1 + else: + udp_step = int(generator_config.gen_config.udp_port_step) + udp_src_size = self.check_range_size(int(src_max) - int(src_min) + 1, udp_step) + udp_dst_size = self.check_range_size(int(dst_max) - int(dst_min) + 1, udp_step) + lcm_port = lcm(udp_src_size, udp_dst_size) if self.ip_src_static is True: - lcm_ip = self.lcm(1, min(self.ip_addrs_size['left'], self.ip_addrs_size['right'])) + lcm_ip = 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) + lcm_ip = lcm(self.ip_addrs_size['left'], self.ip_addrs_size['right']) + flow_max = 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) - - self.udp_ports = UdpPorts(src_min, src_max, dst_min, dst_max, + self.udp_ports = UdpPorts(src_min, src_max, dst_min, dst_max, udp_src_size, udp_dst_size, generator_config.gen_config.udp_port_step) + + self.ip_block = IpBlock(self.ip, step, self.ip_size) + self.gw_ip_block = IpBlock(generator_config.gateway_ips[port], generator_config.gateway_ip_addrs_step, self.chain_count) @@ -256,6 +260,120 @@ class Device(object): generator_config.tg_gateway_ip_addrs_step, self.chain_count) + def limit_ip_udp_ranges(self, peer_ip_size, cur_chain_flow_count): + # init to min value in case of no matching values found with lcm calculation + new_src_ip_size = 1 + new_peer_ip_size = 1 + new_src_udp_size = 1 + new_dst_udp_size = 1 + + if self.ip_src_static is True: + src_ip_size = 1 + else: + src_ip_size = self.ip_size + ip_src_divisors = list(get_divisors(src_ip_size)) + ip_dst_divisors = list(get_divisors(peer_ip_size)) + udp_src_divisors = list(get_divisors(self.udp_ports.udp_src_size)) + udp_dst_divisors = list(get_divisors(self.udp_ports.udp_dst_size)) + fc = int(cur_chain_flow_count) + tuples_ip = list(find_tuples_equal_to_lcm_value(ip_src_divisors, ip_dst_divisors, fc)) + tuples_udp = list(find_tuples_equal_to_lcm_value(udp_src_divisors, udp_dst_divisors, fc)) + + if tuples_ip: + new_src_ip_size = tuples_ip[-1][0] + new_peer_ip_size = tuples_ip[-1][1] + + if tuples_udp: + new_src_udp_size = tuples_udp[-1][0] + new_dst_udp_size = tuples_udp[-1][1] + + tuples_src = [] + tuples_dst = [] + if not tuples_ip and not tuples_udp: + # in case of not divisors in common matching LCM value (i.e. requested flow count) + # try to find an accurate UDP range to fit requested flow count + udp_src_int = range(self.udp_ports.src_min, self.udp_ports.src_max) + udp_dst_int = range(self.udp_ports.dst_min, self.udp_ports.dst_max) + tuples_src = list(find_tuples_equal_to_lcm_value(ip_src_divisors, udp_src_int, fc)) + tuples_dst = list(find_tuples_equal_to_lcm_value(ip_dst_divisors, udp_dst_int, fc)) + + if not tuples_src and not tuples_dst: + # iterate IP and UDP ranges to find a tuple that match flow count values + src_ip_range = range(1,src_ip_size) + dst_ip_range = range(1, peer_ip_size) + tuples_src = list(find_tuples_equal_to_lcm_value(src_ip_range, udp_src_int, fc)) + tuples_dst = list(find_tuples_equal_to_lcm_value(dst_ip_range, udp_dst_int, fc)) + + if tuples_src or tuples_dst: + if tuples_src: + new_src_ip_size = tuples_src[-1][0] + new_src_udp_size = tuples_src[-1][1] + if tuples_dst: + new_peer_ip_size = tuples_dst[-1][0] + new_dst_udp_size = tuples_dst[-1][1] + else: + if not tuples_ip: + if src_ip_size != 1: + if src_ip_size > fc: + new_src_ip_size = fc + else: + new_src_ip_size = find_max_size(src_ip_size, tuples_udp, fc) + if peer_ip_size != 1: + if peer_ip_size > fc: + new_peer_ip_size = fc + else: + new_peer_ip_size = find_max_size(peer_ip_size, tuples_udp, fc) + + if not tuples_udp: + if self.udp_ports.udp_src_size != 1: + if self.udp_ports.udp_src_size > fc: + new_src_udp_size = fc + else: + new_src_udp_size = find_max_size(self.udp_ports.udp_src_size, + tuples_ip, fc) + if self.udp_ports.udp_dst_size != 1: + if self.udp_ports.udp_dst_size > fc: + new_dst_udp_size = fc + else: + new_dst_udp_size = find_max_size(self.udp_ports.udp_dst_size, + tuples_ip, fc) + max_possible_flows = lcm(lcm(new_src_ip_size, new_peer_ip_size), + lcm(new_src_udp_size, new_dst_udp_size)) + + LOG.debug("IP dst size: %d", new_peer_ip_size) + LOG.debug("LCM IP: %d", lcm(new_src_ip_size, new_peer_ip_size)) + LOG.debug("LCM UDP: %d", lcm(new_src_udp_size, new_dst_udp_size)) + LOG.debug("Global LCM: %d", max_possible_flows) + LOG.debug("IP src size: %d, IP dst size: %d, UDP src size: %d, UDP dst size: %d", + new_src_ip_size, new_peer_ip_size, self.udp_ports.udp_src_size, + self.udp_ports.udp_dst_size) + if not max_possible_flows == cur_chain_flow_count: + if (self.ip_addrs_step != '0.0.0.1' or self.udp_ports.step != '1') and not ( + self.ip_addrs_step == 'random' and self.udp_ports.step == 'random'): + LOG.warning("Current values of ip_addrs_step and/or udp_port_step properties " + "do not allow to control an accurate flow count. " + "Values will be overridden as follows:") + if self.ip_addrs_step != '0.0.0.1': + LOG.info("ip_addrs_step='0.0.0.1' (previous value: ip_addrs_step='%s')", + self.ip_addrs_step) + self.ip_addrs_step = '0.0.0.1' + + if self.udp_ports.step != '1': + LOG.info("udp_port_step='1' (previous value: udp_port_step='%s')", + self.udp_ports.step) + self.udp_ports.step = '1' + # override config for not logging random step warning message in trex_gen.py + self.generator_config.gen_config.udp_port_step = self.udp_ports.step + else: + LOG.error("Current values of ip_addrs_step and udp_port_step properties " + "do not allow to control an accurate flow count.") + else: + src_ip_size = new_src_ip_size + peer_ip_size = new_peer_ip_size + self.udp_ports.udp_src_size = new_src_udp_size + self.udp_ports.udp_dst_size = new_dst_udp_size + return src_ip_size, peer_ip_size + @staticmethod def define_udp_range(udp_port, property_name): if isinstance(udp_port, int): @@ -269,23 +387,15 @@ class Device(object): % 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.""" + def check_range_size(range_size, step): + """Check and set the available IPs or UDP ports, considering the step.""" try: - if ip_size % step == 0: - value = int(ip_size / step) + if range_size % step == 0: + value = range_size // step else: - value = int((ip_size / step)) + 1 + value = range_size // step + 1 return value except ZeroDivisionError: raise ZeroDivisionError("step can't be zero !") from ZeroDivisionError @@ -389,27 +499,42 @@ 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() + # limit ranges of UDP ports and IP to avoid overflow of the number of flows + peer_size = peer.ip_size // self.chain_count + for chain_idx in range(self.chain_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) + src_ip_size, peer_ip_size = self.limit_ip_udp_ranges(peer_size, cur_chain_flow_count) + + src_ip_first, src_ip_last = self.ip_block.reserve_ip_range \ + (src_ip_size) + dst_ip_first, dst_ip_last = peer.ip_block.reserve_ip_range \ + (peer_ip_size) + + if self.ip_addrs_step != 'random': + src_ip_last = self.ip_block.get_ip_from_chain_first_ip( + Device.ip_to_int(src_ip_first), src_ip_size - 1) + dst_ip_last = peer.ip_block.get_ip_from_chain_first_ip( + Device.ip_to_int(dst_ip_first), peer_ip_size - 1) + if self.udp_ports.step != 'random': + self.udp_ports.src_max = self.udp_ports.get_src_max(self.udp_ports.udp_src_size - 1) + self.udp_ports.dst_max = self.udp_ports.get_dst_max(self.udp_ports.udp_dst_size - 1) + if self.ip_src_static: + src_ip_last = src_ip_first + + LOG.info("Port %d, chain %d: IP src range [%s,%s]", self.port, chain_idx, + src_ip_first, src_ip_last) + LOG.info("Port %d, chain %d: IP dst range [%s,%s]", self.port, chain_idx, + dst_ip_first, dst_ip_last) + LOG.info("Port %d, chain %d: UDP src range [%s,%s]", self.port, chain_idx, + self.udp_ports.src_min, self.udp_ports.src_max) + LOG.info("Port %d, chain %d: UDP dst range [%s,%s]", self.port, chain_idx, + self.udp_ports.dst_min, self.udp_ports.dst_max) configs.append({ 'count': cur_chain_flow_count, @@ -417,18 +542,18 @@ class Device(object): 'mac_dst': dest_macs[chain_idx], 'ip_src_addr': src_ip_first, 'ip_src_addr_max': src_ip_last, - 'ip_src_count': cur_chain_flow_count, + 'ip_src_count': src_ip_size, 'ip_dst_addr': dst_ip_first, 'ip_dst_addr_max': dst_ip_last, - 'ip_dst_count': cur_chain_flow_count, + 'ip_dst_count': peer_ip_size, 'ip_addrs_step': self.ip_addrs_step, '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_src_count': self.udp_ports.udp_src_size, '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_dst_count': self.udp_ports.udp_dst_size, '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), diff --git a/nfvbench/traffic_gen/trex_gen.py b/nfvbench/traffic_gen/trex_gen.py index f7250da..f5c2afb 100644 --- a/nfvbench/traffic_gen/trex_gen.py +++ b/nfvbench/traffic_gen/trex_gen.py @@ -53,6 +53,7 @@ from trex.stl.api import STLVmFixChecksumHw from trex.stl.api import STLVmFixIpv4 from trex.stl.api import STLVmFlowVar from trex.stl.api import STLVmFlowVarRepeatableRandom +from trex.stl.api import STLVmTupleGen from trex.stl.api import STLVmWrFlowVar from trex.stl.api import ThreeBytesField from trex.stl.api import UDP @@ -375,7 +376,7 @@ class TRex(AbstractTrafficGenerator): bind_layers(UDP, VXLAN, dport=4789) bind_layers(VXLAN, Ether) - def _create_pkt(self, stream_cfg, l2frame_size): + def _create_pkt(self, stream_cfg, l2frame_size, disable_random_latency_flow=False): """Create a packet of given size. l2frame_size: size of the L2 frame in bytes (including the 32-bit FCS) @@ -440,81 +441,128 @@ class TRex(AbstractTrafficGenerator): 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_ip = STLVmFlowVarRepeatableRandom( - name="ip_src", - min_value=stream_cfg['ip_src_addr'], - max_value=src_max_ip_value, - size=4, - seed=random.randint(0, 32767), - limit=stream_cfg['ip_src_count']) - dst_fv_ip = STLVmFlowVarRepeatableRandom( - name="ip_dst", - min_value=stream_cfg['ip_dst_addr'], - max_value=stream_cfg['ip_dst_addr_max'], - size=4, - seed=random.randint(0, 32767), - limit=stream_cfg['ip_dst_count']) - else: - src_fv_ip = STLVmFlowVar( - name="ip_src", - min_value=stream_cfg['ip_src_addr'], - max_value=src_max_ip_value, - size=4, - op="inc", - step=stream_cfg['ip_addrs_step']) - dst_fv_ip = STLVmFlowVar( - name="ip_dst", - min_value=stream_cfg['ip_dst_addr'], - max_value=stream_cfg['ip_dst_addr_max'], - size=4, - op="inc", - step=stream_cfg['ip_addrs_step']) - - 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=stream_cfg['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']) + + # STLVmTupleGen need flow count >= cores used by TRex, if FC < cores we used STLVmFlowVar + if stream_cfg['ip_addrs_step'] == '0.0.0.1' and stream_cfg['udp_port_step'] == '1' and \ + stream_cfg['count'] >= self.generator_config.cores: + src_fv = STLVmTupleGen(ip_min=stream_cfg['ip_src_addr'], + ip_max=stream_cfg['ip_src_addr_max'], + port_min=udp_args['sport'], + port_max=udp_args['sport_max'], + name="tuple_src", + limit_flows=stream_cfg['count']) + dst_fv = STLVmTupleGen(ip_min=stream_cfg['ip_dst_addr'], + ip_max=stream_cfg['ip_dst_addr_max'], + port_min=udp_args['dport'], + port_max=udp_args['dport_max'], + name="tuple_dst", + limit_flows=stream_cfg['count']) + vm_param = [ + src_fv, + STLVmWrFlowVar(fv_name="tuple_src.ip", + pkt_offset="IP:{}.src".format(encap_level)), + STLVmWrFlowVar(fv_name="tuple_src.port", + pkt_offset="UDP:{}.sport".format(encap_level)), + dst_fv, + STLVmWrFlowVar(fv_name="tuple_dst.ip", + pkt_offset="IP:{}.dst".format(encap_level)), + STLVmWrFlowVar(fv_name="tuple_dst.port", + pkt_offset="UDP:{}.dport".format(encap_level)), + ] 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)), - 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)), - ] + if disable_random_latency_flow: + src_fv_ip = STLVmFlowVar( + name="ip_src", + min_value=stream_cfg['ip_src_addr'], + max_value=stream_cfg['ip_src_addr'], + size=4) + dst_fv_ip = STLVmFlowVar( + name="ip_dst", + min_value=stream_cfg['ip_dst_addr'], + max_value=stream_cfg['ip_dst_addr'], + size=4) + elif stream_cfg['ip_addrs_step'] == 'random': + src_fv_ip = STLVmFlowVarRepeatableRandom( + name="ip_src", + min_value=stream_cfg['ip_src_addr'], + max_value=stream_cfg['ip_src_addr_max'], + size=4, + seed=random.randint(0, 32767), + limit=stream_cfg['ip_src_count']) + dst_fv_ip = STLVmFlowVarRepeatableRandom( + name="ip_dst", + min_value=stream_cfg['ip_dst_addr'], + max_value=stream_cfg['ip_dst_addr_max'], + size=4, + seed=random.randint(0, 32767), + limit=stream_cfg['ip_dst_count']) + else: + src_fv_ip = STLVmFlowVar( + name="ip_src", + min_value=stream_cfg['ip_src_addr'], + max_value=stream_cfg['ip_src_addr_max'], + size=4, + op="inc", + step=stream_cfg['ip_addrs_step']) + dst_fv_ip = STLVmFlowVar( + name="ip_dst", + min_value=stream_cfg['ip_dst_addr'], + max_value=stream_cfg['ip_dst_addr_max'], + size=4, + op="inc", + step=stream_cfg['ip_addrs_step']) + + if disable_random_latency_flow: + src_fv_port = STLVmFlowVar( + name="p_src", + min_value=udp_args['sport'], + max_value=udp_args['sport'], + size=2) + dst_fv_port = STLVmFlowVar( + name="p_dst", + min_value=udp_args['dport'], + max_value=udp_args['dport'], + size=2) + elif 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=stream_cfg['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)), + 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)), + ] # Use HW Offload to calculate the outter IP/UDP packet vm_param.append(STLVmFixChecksumHw(l3_offset="IP:0", l4_offset="UDP:0", @@ -563,7 +611,13 @@ class TRex(AbstractTrafficGenerator): if latency: # for IMIX, the latency packets have the average IMIX packet size - pkt = self._create_pkt(stream_cfg, IMIX_AVG_L2_FRAME_SIZE) + if stream_cfg['ip_addrs_step'] == 'random' or \ + stream_cfg['udp_port_step'] == 'random': + # Force latency flow to only one flow to avoid creating flows + # over requested flow count + pkt = self._create_pkt(stream_cfg, IMIX_AVG_L2_FRAME_SIZE, True) + else: + pkt = self._create_pkt(stream_cfg, IMIX_AVG_L2_FRAME_SIZE) else: l2frame_size = int(l2frame) @@ -589,8 +643,16 @@ class TRex(AbstractTrafficGenerator): # without vlan, the min l2 frame size is 64 # with vlan it is 68 # This only applies to the latency stream - if latency and stream_cfg['vlan_tag'] and l2frame_size < 68: - pkt = self._create_pkt(stream_cfg, 68) + if latency: + if stream_cfg['vlan_tag'] and l2frame_size < 68: + l2frame_size = 68 + if stream_cfg['ip_addrs_step'] == 'random' or \ + stream_cfg['udp_port_step'] == 'random': + # Force latency flow to only one flow to avoid creating flows + # over requested flow count + pkt = self._create_pkt(stream_cfg, l2frame_size, True) + else: + pkt = self._create_pkt(stream_cfg, l2frame_size) if latency: if self.config.no_latency_stats: @@ -865,6 +927,11 @@ class TRex(AbstractTrafficGenerator): for port in self.port_handle: streamblock[port] = [] stream_cfgs = [d.get_stream_configs() for d in self.generator_config.devices] + if self.generator_config.ip_addrs_step == 'random' \ + or self.generator_config.gen_config.udp_port_step == 'random': + LOG.warning("Using random step, the number of flows can be less than " + "the requested number of flows due to repeatable multivariate random " + "generation which can reproduce the same pattern of values") self.rates = [utils.to_rate_str(rate) for rate in rates] for chain_id, (fwd_stream_cfg, rev_stream_cfg) in enumerate(zip(*stream_cfgs)): streamblock[0].extend(self.generate_streams(self.port_handle[0], diff --git a/nfvbench/utils.py b/nfvbench/utils.py index 06f643c..2ce735b 100644 --- a/nfvbench/utils.py +++ b/nfvbench/utils.py @@ -13,6 +13,7 @@ # under the License. import glob +from math import gcd from math import isnan import os import re @@ -203,3 +204,46 @@ class RunLock(object): os.unlink(self._path) except (OSError, IOError): pass + + +def get_divisors(n): + for i in range(1, int(n / 2) + 1): + if n % i == 0: + yield i + yield n + + +def lcm(a, b): + """ + Calculate the maximum possible value for both IP and ports, + eventually for maximum possible flow. + """ + 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 !") + + +def find_tuples_equal_to_lcm_value(a, b, lcm_value): + """ + Find numbers from two list matching a LCM value. + """ + for x in a: + for y in b: + if lcm(x, y) == lcm_value: + yield (x, y) + + +def find_max_size(max_size, tuples, flow): + if tuples: + if max_size > tuples[-1][0]: + max_size = tuples[-1][0] + return int(max_size) + if max_size > tuples[-1][1]: + max_size = tuples[-1][1] + return int(max_size) + + for i in range(max_size, 1, -1): + if flow % i == 0: + return int(i) + return 1 diff --git a/test/mock_trex.py b/test/mock_trex.py index 49fe5a9..4884c98 100644 --- a/test/mock_trex.py +++ b/test/mock_trex.py @@ -55,6 +55,7 @@ except ImportError: api_mod.STLVmFixIpv4 = STLDummy api_mod.STLVmFlowVar = STLDummy api_mod.STLVmFlowVarRepeatableRandom = STLDummy + api_mod.STLVmTupleGen = STLDummy api_mod.STLVmWrFlowVar = STLDummy api_mod.UDP = STLDummy api_mod.bind_layers = STLDummy diff --git a/test/test_nfvbench.py b/test/test_nfvbench.py index fe8742f..c772d7b 100644 --- a/test/test_nfvbench.py +++ b/test/test_nfvbench.py @@ -31,8 +31,9 @@ from nfvbench.traffic_client import Device from nfvbench.traffic_client import GeneratorConfig from nfvbench.traffic_client import IpBlock from nfvbench.traffic_client import TrafficClient +from nfvbench.traffic_client import TrafficClientException import nfvbench.traffic_gen.traffic_utils as traffic_utils - +import nfvbench.utils as utils # just to get rid of the unused function warning no_op() @@ -194,40 +195,53 @@ def test_ip_block(): 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)) + assert utils.lcm(10, 2) == 10 + assert utils.lcm(1, 256) == 256 + assert utils.lcm(10, 256) == 1280 + assert utils.lcm(utils.lcm(10, 2), utils.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 + utils.lcm(0, 0) + + +def test_flow_count_limit(): + # lcm ip src and dst /32 + lcm_ip = utils.lcm(1, 1) == 1 + # port udp src = 1 port udp dst [1,29] + src_min = 1 + src_max = 1 + dst_min = 1 + dst_max = 29 + udp_step = 3 + udp_src_size = Device.check_range_size(int(src_max) - int(src_min) + 1, + udp_step) + udp_dst_size = Device.check_range_size(int(dst_max) - int(dst_min) + 1, + udp_step) + lcm_port = utils.lcm(udp_src_size, udp_dst_size) + assert utils.lcm(lcm_ip, lcm_port) < 29 + + +def test_check_range_size(): + assert Device.check_range_size(256, 1) == 256 + assert Device.check_range_size(256, 3) == 86 + assert Device.check_range_size(256, 4) == 64 + assert Device.check_range_size(16, 10) == 2 + assert Device.check_range_size(1, 10) == 1 with pytest.raises(ZeroDivisionError): - Device.check_ipsize(256, 0) + Device.check_range_size(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) + src_ip_first, src_ip_last = ipb.reserve_ip_range(256) 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) + src_ip_first, src_ip_last = ipb.reserve_ip_range(2) 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) + ipb.reserve_ip_range(256) def check_stream_configs(gen_config): @@ -241,12 +255,18 @@ def check_stream_configs(gen_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_count'] == stream_cfg['ip_dst_count'] + # ip_src_static == True + assert stream_cfg['ip_src_count'] == 1 + if index == 0: + assert stream_cfg['ip_dst_count'] == 4999 + else: + assert stream_cfg['ip_dst_count'] == 5000 + assert stream_cfg['ip_src_addr'] == Device.int_to_ip(sip) assert Device.ip_to_int(stream_cfg['ip_src_addr']) == sip assert Device.ip_to_int(stream_cfg['ip_dst_addr']) == dip - count = stream_cfg['ip_src_count'] + count = stream_cfg['ip_dst_count'] cfc += count - sip += count * step + sip += step dip += count * step assert cfc == int(config['flow_count'] / 2) @@ -260,54 +280,89 @@ def test_device_flow_config(): _check_device_flow_config('0.0.0.2') -def check_udp_stream_configs(gen_config, expected): +def check_udp_stream_configs(gen_config, expected_cfg): """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] + expected = expected_cfg[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_src_count'] == expected['ip_src_count'] + 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['ip_dst_count'] == expected['ip_dst_count'] + 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'] + lcm_ip = utils.lcm(stream_cfg['ip_src_count'], stream_cfg['ip_dst_count']) + udp_src_size = int(stream_cfg['udp_src_port_max']) - int(stream_cfg['udp_src_port']) + 1 + udp_dst_size = int(stream_cfg['udp_dst_port_max']) - int(stream_cfg['udp_dst_port']) + 1 + lcm_udp = utils.lcm(udp_src_size, udp_dst_size) + assert utils.lcm(lcm_ip, lcm_udp) >= stream_cfg['count'] -def _check_device_udp_flow_config(param, expected): + +def _check_device_udp_flow_config(param, expected_cfg): config = _get_dummy_tg_config('PVP', '1Mpps', + scc=param['scc'], ip_src_static=param['ip_src_static'], fc=param['flow_count'], ip0=param['ip_src_addr'], ip1=param['ip_dst_addr'], + step_ip=param['ip_addrs_step'], src_udp=param['udp_src_port'], - dst_udp=param['udp_dst_port']) + dst_udp=param['udp_dst_port'], + step_udp=param['udp_port_step']) gen_config = GeneratorConfig(config) - check_udp_stream_configs(gen_config, expected) + check_udp_stream_configs(gen_config, expected_cfg) -def test_device_udp_flow_config(): +def __get_udp_params(): param = {'ip_src_static': True, 'ip_src_addr': '110.0.0.0/32', 'ip_dst_addr': '120.0.0.0/32', + 'ip_addrs_step': '0.0.0.1', 'udp_src_port': 53, 'udp_dst_port': 53, - 'flow_count': 2} + 'flow_count': 2, + 'scc': 1, + 'udp_port_step': '1'} + return param + + +def __get_udp_expected_list(): expected = {'ip_src_addr': '110.0.0.0', 'ip_src_addr_max': '110.0.0.0', + 'ip_src_count': 1, 'ip_dst_addr': '120.0.0.0', 'ip_dst_addr_max': '120.0.0.0', + 'ip_dst_count': 1, '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) + return expected + + +def test_device_udp_flow_config_single_ip_single_port(): + param = __get_udp_params() + expected = __get_udp_expected_list() + _check_device_udp_flow_config(param, [expected]) + + +def test_device_udp_flow_config_single_ip_multiple_src_ports(): + param = __get_udp_params() + expected = __get_udp_expected_list() # 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. @@ -315,20 +370,568 @@ def test_device_udp_flow_config(): param['flow_count'] = 10 expected['udp_src_port_max'] = 57 expected['udp_src_count'] = 5 - _check_device_udp_flow_config(param, expected) + _check_device_udp_flow_config(param, [expected]) + + +def test_device_udp_flow_config_multiple_ip_src_single_port(): + param = __get_udp_params() + expected = __get_udp_expected_list() # 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['flow_count'] = 10 param['ip_src_static'] = False param['ip_dst_addr'] = '120.0.0.0/24' + + expected['udp_src_port_max'] = 53 + expected['udp_src_count'] = 1 expected['ip_dst_addr'] = '120.0.0.0' expected['ip_dst_addr_max'] = '120.0.0.4' - _check_device_udp_flow_config(param, expected) + expected['ip_dst_count'] = 5 + _check_device_udp_flow_config(param, [expected]) + + +def test_device_udp_flow_config_multiple_ip_src_dst_multiple_src_dst_ports(): + param = __get_udp_params() + expected = __get_udp_expected_list() + + param['udp_src_port'] = [49000, 49031] + param['udp_dst_port'] = [50000, 50033] + param['ip_src_static'] = False + param['flow_count'] = 1000 + param['ip_src_addr'] = '110.0.0.0/16' + param['ip_dst_addr'] = '120.0.0.0/16' + + expected['udp_src_port'] = 49000 + expected['udp_src_port_max'] = 49024 + expected['udp_dst_port'] = 50000 + expected['udp_dst_port_max'] = 50024 + expected['udp_src_count'] = 25 + expected['udp_dst_count'] = 25 + expected['ip_src_addr_max'] = '110.0.1.243' + expected['ip_src_count'] = 500 + expected['ip_dst_addr'] = '120.0.0.0' + expected['ip_dst_addr_max'] = '120.0.1.243' + expected['ip_dst_count'] = 500 + _check_device_udp_flow_config(param, [expected]) + + + + +def test_device_udp_flow_config_random_multiple_ip_src_dst_multiple_src_dst_ports(): + param = __get_udp_params() + expected = __get_udp_expected_list() + + param['udp_src_port'] = [1025, 65000] + param['udp_dst_port'] = [1024, 65000] + param['ip_src_static'] = False + param['ip_addrs_step'] = 'random' + param['udp_port_step'] = 'random' + param['flow_count'] = 1000000 + param['ip_src_addr'] = '110.0.0.0/16' + param['ip_dst_addr'] = '120.0.0.0/16' + + expected['udp_src_port'] = 1025 + expected['udp_src_port_max'] = 65000 + expected['udp_dst_port'] = 1024 + expected['udp_dst_port_max'] = 65000 + expected['udp_src_count'] = 62500 + expected['udp_dst_count'] = 62500 + expected['ip_src_addr_max'] = '110.0.0.31' + expected['ip_src_count'] = 32 + expected['ip_dst_addr'] = '120.0.0.0' + expected['ip_dst_addr_max'] = '120.0.0.31' + expected['ip_dst_count'] = 32 + _check_device_udp_flow_config(param, [expected]) + +def test_device_udp_flow_config_random_multiple_ip_srcstatic_dst_multiple_src_dst_ports(): + param = __get_udp_params() + expected = __get_udp_expected_list() + + param['udp_src_port'] = [1025, 65000] + param['udp_dst_port'] = [1024, 65000] + param['ip_src_static'] = True + param['ip_addrs_step'] = 'random' + param['udp_port_step'] = 'random' + param['flow_count'] = 1000000 + param['ip_src_addr'] = '110.0.0.0/16' + param['ip_dst_addr'] = '120.0.0.0/16' + + expected['udp_src_port'] = 1025 + expected['udp_src_port_max'] = 65000 + expected['udp_dst_port'] = 1024 + expected['udp_dst_port_max'] = 65000 + expected['udp_src_count'] = 1 + expected['udp_dst_count'] = 62500 + expected['ip_src_addr_max'] = '110.0.0.0' + expected['ip_src_count'] = 1 + expected['ip_dst_addr'] = '120.0.0.0' + expected['ip_dst_addr_max'] = '120.0.0.31' + expected['ip_dst_count'] = 32 + _check_device_udp_flow_config(param, [expected]) + + + +def test_device_udp_flow_config_single_ip_src_dst_multiple_src_dst_ports(): + param = __get_udp_params() + expected = __get_udp_expected_list() + + param['udp_src_port'] = [49152, 49154] + param['udp_dst_port'] = [50001, 50005] + param['ip_src_static'] = False + param['flow_count'] = 10 + param['ip_src_addr'] = '110.0.0.0/32' + param['ip_dst_addr'] = '120.0.0.0/32' + + expected['udp_src_port'] = 49152 + expected['udp_src_port_max'] = 49152 + expected['udp_dst_port'] = 50001 + expected['udp_dst_port_max'] = 50005 + expected['udp_src_count'] = 1 + expected['udp_dst_count'] = 5 + expected['ip_src_addr_max'] = '110.0.0.0' + expected['ip_src_count'] = 1 + expected['ip_dst_addr'] = '120.0.0.0' + expected['ip_dst_addr_max'] = '120.0.0.0' + expected['ip_dst_count'] = 1 + _check_device_udp_flow_config(param, [expected]) + + +def test_device_udp_flow_config_single_ip_src_dst_single_src_multiple_dst_ports(): + param = __get_udp_params() + expected = __get_udp_expected_list() + + param['udp_src_port'] = 49152 + param['udp_dst_port'] = [50001, 50029] + param['udp_port_step'] = '3' + param['flow_count'] = 58 + param['ip_src_addr'] = '110.0.0.0/32' + param['ip_dst_addr'] = '120.0.0.0/32' + + expected['udp_src_port'] = 49152 + expected['udp_src_port_max'] = 49152 + expected['udp_dst_port'] = 50001 + expected['udp_dst_port_max'] = 50029 + expected['udp_src_count'] = 1 + expected['udp_dst_count'] = 29 + expected['ip_src_addr_max'] = '110.0.0.0' + expected['ip_src_count'] = 1 + expected['ip_dst_addr'] = '120.0.0.0' + expected['ip_dst_addr_max'] = '120.0.0.0' + expected['ip_dst_count'] = 1 + with pytest.raises(TrafficClientException): + _check_device_udp_flow_config(param, [expected]) + + +def test_device_udp_flow_config_scc3(): + param = __get_udp_params() + expected = __get_udp_expected_list() + + param['scc'] = 3 + param['udp_src_port'] = [49000, 49031] + param['udp_dst_port'] = [50000, 50033] + param['ip_src_static'] = False + param['flow_count'] = 10000 + param['ip_src_addr'] = '110.0.0.0/16' + param['ip_dst_addr'] = '120.0.0.0/16' + + expected_cfg = [] + # chain 0 + expected_scc0 = dict(expected) + expected_scc0['udp_src_port'] = 49000 + expected_scc0['udp_src_port_max'] = 49016 + expected_scc0['udp_dst_port'] = 50000 + expected_scc0['udp_dst_port_max'] = 50033 + expected_scc0['udp_src_count'] = 17 + expected_scc0['udp_dst_count'] = 34 + expected_scc0['ip_src_addr_max'] = '110.0.6.129' + expected_scc0['ip_src_count'] = 1666 + expected_scc0['ip_dst_addr'] = '120.0.0.0' + expected_scc0['ip_dst_addr_max'] = '120.0.6.129' + expected_scc0['ip_dst_count'] = 1666 + expected_cfg.append(expected_scc0) + + # chain 1 + expected_scc1 = dict(expected) + expected_scc1['udp_src_port'] = 49000 + expected_scc1['udp_src_port_max'] = 49000 + expected_scc1['udp_dst_port'] = 50000 + expected_scc1['udp_dst_port_max'] = 50000 + expected_scc1['udp_src_count'] = 1 + expected_scc1['udp_dst_count'] = 1 + expected_scc1['ip_src_addr'] = '110.0.6.130' + expected_scc1['ip_src_addr_max'] = '110.0.13.4' + expected_scc1['ip_src_count'] = 1667 + expected_scc1['ip_dst_addr'] = '120.0.6.130' + expected_scc1['ip_dst_addr_max'] = '120.0.13.4' + expected_scc1['ip_dst_count'] = 1667 + expected_cfg.append(expected_scc1) + + # chain 2 + expected_scc2 = dict(expected) + expected_scc2['udp_src_port'] = 49000 + expected_scc2['udp_src_port_max'] = 49000 + expected_scc2['udp_dst_port'] = 50000 + expected_scc2['udp_dst_port_max'] = 50000 + expected_scc2['udp_src_count'] = 1 + expected_scc2['udp_dst_count'] = 1 + expected_scc2['ip_src_addr'] = '110.0.13.5' + expected_scc2['ip_src_addr_max'] = '110.0.19.135' + expected_scc2['ip_src_count'] = 1667 + expected_scc2['ip_dst_addr'] = '120.0.13.5' + expected_scc2['ip_dst_addr_max'] = '120.0.19.135' + expected_scc2['ip_dst_count'] = 1667 + expected_cfg.append(expected_scc2) + + _check_device_udp_flow_config(param, expected_cfg) + + +def test_device_udp_flow_config_doc_example1(caplog): + caplog.clear() + caplog.set_level(logging.INFO) + param = __get_udp_params() + expected = __get_udp_expected_list() + + # Multiflow unitary test corresponding to first example in documentation + param['scc'] = 3 + param['udp_src_port'] = 53 + param['udp_dst_port'] = 53 + param['ip_src_static'] = True + param['flow_count'] = 100 + param['ip_src_addr'] = '110.0.0.0/8' + param['ip_dst_addr'] = '120.0.0.0/8' + + expected_cfg = [] + # chain 0 + expected_scc0 = dict(expected) + expected_scc0['udp_src_port'] = 53 + expected_scc0['udp_src_port_max'] = 53 + expected_scc0['udp_dst_port'] = 53 + expected_scc0['udp_dst_port_max'] = 53 + expected_scc0['udp_src_count'] = 1 + expected_scc0['udp_dst_count'] = 1 + expected_scc0['ip_src_addr'] = '110.0.0.0' + expected_scc0['ip_src_addr_max'] = '110.0.0.0' + expected_scc0['ip_src_count'] = 1 + expected_scc0['ip_dst_addr'] = '120.0.0.0' + expected_scc0['ip_dst_addr_max'] = '120.0.0.15' + expected_scc0['ip_dst_count'] = 16 + expected_cfg.append(expected_scc0) + + # chain 1 + expected_scc1 = dict(expected) + expected_scc1['udp_src_port'] = 53 + expected_scc1['udp_src_port_max'] = 53 + expected_scc1['udp_dst_port'] = 53 + expected_scc1['udp_dst_port_max'] = 53 + expected_scc1['udp_src_count'] = 1 + expected_scc1['udp_dst_count'] = 1 + expected_scc1['ip_src_addr'] = '110.0.0.1' + expected_scc1['ip_src_addr_max'] = '110.0.0.1' + expected_scc1['ip_src_count'] = 1 + expected_scc1['ip_dst_addr'] = '120.0.0.16' + expected_scc1['ip_dst_addr_max'] = '120.0.0.32' + expected_scc1['ip_dst_count'] = 17 + expected_cfg.append(expected_scc1) + + # chain 2 + expected_scc2 = dict(expected) + expected_scc2['udp_src_port'] = 53 + expected_scc2['udp_src_port_max'] = 53 + expected_scc2['udp_dst_port'] = 53 + expected_scc2['udp_dst_port_max'] = 53 + expected_scc2['udp_src_count'] = 1 + expected_scc2['udp_dst_count'] = 1 + expected_scc2['ip_src_addr'] = '110.0.0.2' + expected_scc2['ip_src_addr_max'] = '110.0.0.2' + expected_scc2['ip_src_count'] = 1 + expected_scc2['ip_dst_addr'] = '120.0.0.33' + expected_scc2['ip_dst_addr_max'] = '120.0.0.49' + expected_scc2['ip_dst_count'] = 17 + expected_cfg.append(expected_scc2) + + _check_device_udp_flow_config(param, expected_cfg) + assert "Current values of ip_addrs_step and/or udp_port_step properties" not in caplog.text + + +def test_device_udp_flow_config_doc_example2(caplog): + caplog.clear() + caplog.set_level(logging.INFO) + param = __get_udp_params() + expected = __get_udp_expected_list() + + # Multiflow unitary test corresponding to second example in documentation + param['scc'] = 3 + param['udp_src_port'] = 53 + param['udp_dst_port'] = 53 + param['ip_src_static'] = True + param['ip_addrs_step'] = 'random' + param['flow_count'] = 100 + param['ip_src_addr'] = '110.0.0.0/8' + param['ip_dst_addr'] = '120.0.0.0/8' + + expected_cfg = [] + # chain 0 + expected_scc0 = dict(expected) + expected_scc0['udp_src_port'] = 53 + expected_scc0['udp_src_port_max'] = 53 + expected_scc0['udp_dst_port'] = 53 + expected_scc0['udp_dst_port_max'] = 53 + expected_scc0['udp_src_count'] = 1 + expected_scc0['udp_dst_count'] = 1 + expected_scc0['ip_src_addr'] = '110.0.0.0' + expected_scc0['ip_src_addr_max'] = '110.0.0.0' + expected_scc0['ip_src_count'] = 1 + expected_scc0['ip_dst_addr'] = '120.0.0.0' + expected_scc0['ip_dst_addr_max'] = '120.0.0.15' + expected_scc0['ip_dst_count'] = 16 + expected_cfg.append(expected_scc0) + + # chain 1 + expected_scc1 = dict(expected) + expected_scc1['udp_src_port'] = 53 + expected_scc1['udp_src_port_max'] = 53 + expected_scc1['udp_dst_port'] = 53 + expected_scc1['udp_dst_port_max'] = 53 + expected_scc1['udp_src_count'] = 1 + expected_scc1['udp_dst_count'] = 1 + expected_scc1['ip_src_addr'] = '110.0.0.1' + expected_scc1['ip_src_addr_max'] = '110.0.0.1' + expected_scc1['ip_src_count'] = 1 + expected_scc1['ip_dst_addr'] = '120.0.0.16' + expected_scc1['ip_dst_addr_max'] = '120.0.0.32' + expected_scc1['ip_dst_count'] = 17 + expected_cfg.append(expected_scc1) + + # chain 2 + expected_scc2 = dict(expected) + expected_scc2['udp_src_port'] = 53 + expected_scc2['udp_src_port_max'] = 53 + expected_scc2['udp_dst_port'] = 53 + expected_scc2['udp_dst_port_max'] = 53 + expected_scc2['udp_src_count'] = 1 + expected_scc2['udp_dst_count'] = 1 + expected_scc2['ip_src_addr'] = '110.0.0.2' + expected_scc2['ip_src_addr_max'] = '110.0.0.2' + expected_scc2['ip_src_count'] = 1 + expected_scc2['ip_dst_addr'] = '120.0.0.33' + expected_scc2['ip_dst_addr_max'] = '120.0.0.49' + expected_scc2['ip_dst_count'] = 17 + expected_cfg.append(expected_scc2) + + _check_device_udp_flow_config(param, expected_cfg) + assert "Current values of ip_addrs_step and/or udp_port_step properties" not in caplog.text + + +def test_device_udp_flow_config_doc_example3(caplog): + caplog.clear() + param = __get_udp_params() + expected = __get_udp_expected_list() + + # Multiflow unitary test corresponding to third example in documentation + param['scc'] = 3 + param['udp_src_port'] = 53 + param['udp_dst_port'] = 53 + param['ip_src_static'] = True + param['ip_addrs_step'] = '0.0.0.5' + param['flow_count'] = 100 + param['ip_src_addr'] = '110.0.0.0/8' + param['ip_dst_addr'] = '120.0.0.0/8' + + expected_cfg = [] + # chain 0 + expected_scc0 = dict(expected) + expected_scc0['udp_src_port'] = 53 + expected_scc0['udp_src_port_max'] = 53 + expected_scc0['udp_dst_port'] = 53 + expected_scc0['udp_dst_port_max'] = 53 + expected_scc0['udp_src_count'] = 1 + expected_scc0['udp_dst_count'] = 1 + expected_scc0['ip_src_addr'] = '110.0.0.0' + expected_scc0['ip_src_addr_max'] = '110.0.0.0' + expected_scc0['ip_src_count'] = 1 + expected_scc0['ip_dst_addr'] = '120.0.0.0' + expected_scc0['ip_dst_addr_max'] = '120.0.0.75' + expected_scc0['ip_dst_count'] = 16 + expected_cfg.append(expected_scc0) + + # chain 1 + expected_scc1 = dict(expected) + expected_scc1['udp_src_port'] = 53 + expected_scc1['udp_src_port_max'] = 53 + expected_scc1['udp_dst_port'] = 53 + expected_scc1['udp_dst_port_max'] = 53 + expected_scc1['udp_src_count'] = 1 + expected_scc1['udp_dst_count'] = 1 + expected_scc1['ip_src_addr'] = '110.0.0.5' + expected_scc1['ip_src_addr_max'] = '110.0.0.5' + expected_scc1['ip_src_count'] = 1 + expected_scc1['ip_dst_addr'] = '120.0.0.80' + expected_scc1['ip_dst_addr_max'] = '120.0.0.160' + expected_scc1['ip_dst_count'] = 17 + expected_cfg.append(expected_scc1) + + # chain 2 + expected_scc2 = dict(expected) + expected_scc2['udp_src_port'] = 53 + expected_scc2['udp_src_port_max'] = 53 + expected_scc2['udp_dst_port'] = 53 + expected_scc2['udp_dst_port_max'] = 53 + expected_scc2['udp_src_count'] = 1 + expected_scc2['udp_dst_count'] = 1 + expected_scc2['ip_src_addr'] = '110.0.0.10' + expected_scc2['ip_src_addr_max'] = '110.0.0.10' + expected_scc2['ip_src_count'] = 1 + expected_scc2['ip_dst_addr'] = '120.0.0.165' + expected_scc2['ip_dst_addr_max'] = '120.0.0.245' + expected_scc2['ip_dst_count'] = 17 + expected_cfg.append(expected_scc2) + + caplog.set_level(logging.INFO) + _check_device_udp_flow_config(param, expected_cfg) + assert "Current values of ip_addrs_step and/or udp_port_step properties" not in caplog.text + + +def test_device_udp_flow_config_doc_example4(caplog): + caplog.clear() + param = __get_udp_params() + expected = __get_udp_expected_list() + + # Multiflow unitary test corresponding to fourth example in documentation + param['scc'] = 3 + param['udp_src_port'] = [10, 14] + param['udp_dst_port'] = [20, 25] + param['ip_src_static'] = True + param['ip_addrs_step'] = '0.0.0.1' + param['udp_port_step'] = 'random' + param['flow_count'] = 100 + param['ip_src_addr'] = '110.0.0.0/29' + param['ip_dst_addr'] = '120.0.0.0/30' + + expected_cfg = [] + # chain 0 + expected_scc0 = dict(expected) + expected_scc0['udp_src_port'] = 10 + expected_scc0['udp_src_port_max'] = 14 + expected_scc0['udp_dst_port'] = 20 + expected_scc0['udp_dst_port_max'] = 25 + expected_scc0['udp_src_count'] = 5 + expected_scc0['udp_dst_count'] = 6 + expected_scc0['ip_src_addr'] = '110.0.0.0' + expected_scc0['ip_src_addr_max'] = '110.0.0.0' + expected_scc0['ip_src_count'] = 1 + expected_scc0['ip_dst_addr'] = '120.0.0.0' + expected_scc0['ip_dst_addr_max'] = '120.0.0.0' + expected_scc0['ip_dst_count'] = 1 + expected_cfg.append(expected_scc0) + + # chain 1 + expected_scc1 = dict(expected) + expected_scc1['udp_src_port'] = 10 + expected_scc1['udp_src_port_max'] = 14 + expected_scc1['udp_dst_port'] = 20 + expected_scc1['udp_dst_port_max'] = 25 + expected_scc1['udp_src_count'] = 5 + expected_scc1['udp_dst_count'] = 6 + expected_scc1['ip_src_addr'] = '110.0.0.1' + expected_scc1['ip_src_addr_max'] = '110.0.0.1' + expected_scc1['ip_src_count'] = 1 + expected_scc1['ip_dst_addr'] = '120.0.0.1' + expected_scc1['ip_dst_addr_max'] = '120.0.0.1' + expected_scc1['ip_dst_count'] = 1 + expected_cfg.append(expected_scc1) + + # chain 2 + expected_scc2 = dict(expected) + expected_scc2['udp_src_port'] = 10 + expected_scc2['udp_src_port_max'] = 14 + expected_scc2['udp_dst_port'] = 20 + expected_scc2['udp_dst_port_max'] = 25 + expected_scc2['udp_src_count'] = 5 + expected_scc2['udp_dst_count'] = 6 + expected_scc2['ip_src_addr'] = '110.0.0.2' + expected_scc2['ip_src_addr_max'] = '110.0.0.2' + expected_scc2['ip_src_count'] = 1 + expected_scc2['ip_dst_addr'] = '120.0.0.2' + expected_scc2['ip_dst_addr_max'] = '120.0.0.2' + expected_scc2['ip_dst_count'] = 1 + expected_cfg.append(expected_scc2) + caplog.set_level(logging.INFO) + _check_device_udp_flow_config(param, expected_cfg) + assert "Current values of ip_addrs_step and/or udp_port_step properties" in caplog.text + assert "udp_port_step='1' (previous value: udp_port_step='random'" in caplog.text + + +def test_device_udp_flow_config_no_random_steps_overridden(caplog): + caplog.clear() + param = __get_udp_params() + expected = __get_udp_expected_list() + + # Multiflow unitary test corresponding to fifth example in documentation + param['scc'] = 3 + param['udp_src_port'] = [10, 14] + param['udp_dst_port'] = [20, 25] + param['ip_src_static'] = True + param['ip_addrs_step'] = 'random' + param['udp_port_step'] = 'random' + param['flow_count'] = 100 + param['ip_src_addr'] = '110.0.0.0/29' + param['ip_dst_addr'] = '120.0.0.0/30' + + expected_cfg = [] + # chain 0 + expected_scc0 = dict(expected) + expected_scc0['udp_src_port'] = 10 + expected_scc0['udp_src_port_max'] = 14 + expected_scc0['udp_dst_port'] = 20 + expected_scc0['udp_dst_port_max'] = 25 + expected_scc0['udp_src_count'] = 5 + expected_scc0['udp_dst_count'] = 6 + expected_scc0['ip_src_addr'] = '110.0.0.0' + expected_scc0['ip_src_addr_max'] = '110.0.0.0' + expected_scc0['ip_src_count'] = 1 + expected_scc0['ip_dst_addr'] = '120.0.0.0' + expected_scc0['ip_dst_addr_max'] = '120.0.0.0' + expected_scc0['ip_dst_count'] = 1 + expected_cfg.append(expected_scc0) + + # chain 1 + expected_scc1 = dict(expected) + expected_scc1['udp_src_port'] = 10 + expected_scc1['udp_src_port_max'] = 14 + expected_scc1['udp_dst_port'] = 20 + expected_scc1['udp_dst_port_max'] = 25 + expected_scc1['udp_src_count'] = 5 + expected_scc1['udp_dst_count'] = 6 + expected_scc1['ip_src_addr'] = '110.0.0.1' + expected_scc1['ip_src_addr_max'] = '110.0.0.1' + expected_scc1['ip_src_count'] = 1 + expected_scc1['ip_dst_addr'] = '120.0.0.1' + expected_scc1['ip_dst_addr_max'] = '120.0.0.1' + expected_scc1['ip_dst_count'] = 1 + expected_cfg.append(expected_scc1) + + # chain 2 + expected_scc2 = dict(expected) + expected_scc2['udp_src_port'] = 10 + expected_scc2['udp_src_port_max'] = 14 + expected_scc2['udp_dst_port'] = 20 + expected_scc2['udp_dst_port_max'] = 25 + expected_scc2['udp_src_count'] = 5 + expected_scc2['udp_dst_count'] = 6 + expected_scc2['ip_src_addr'] = '110.0.0.2' + expected_scc2['ip_src_addr_max'] = '110.0.0.2' + expected_scc2['ip_src_count'] = 1 + expected_scc2['ip_dst_addr'] = '120.0.0.2' + expected_scc2['ip_dst_addr_max'] = '120.0.0.2' + expected_scc2['ip_dst_count'] = 1 + expected_cfg.append(expected_scc2) + caplog.set_level(logging.INFO) + _check_device_udp_flow_config(param, expected_cfg) + assert "Current values of ip_addrs_step and/or udp_port_step properties" not in caplog.text def test_config(): @@ -420,7 +1023,7 @@ def assert_ndr_pdr(stats, ndr, ndr_dr, pdr, pdr_dr): 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', - step_udp=1, src_udp=None, dst_udp=None, ip_src_static=True): + step_udp='1', src_udp=None, dst_udp=None, ip_src_static=True): return AttrDict({ 'traffic_generator': {'host_name': 'nfvbench_tg', 'default_profile': 'dummy', |