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 /nfvbench | |
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>
Diffstat (limited to 'nfvbench')
-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 |
3 files changed, 389 insertions, 153 deletions
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 |