aboutsummaryrefslogtreecommitdiffstats
path: root/yardstick
diff options
context:
space:
mode:
authorVolodymyr Mytnyk <volodymyrx.mytnyk@intel.com>2019-02-25 09:12:16 +0000
committerGerrit Code Review <gerrit@opnfv.org>2019-02-25 09:12:16 +0000
commit35c58692b05d82ee8400324dfea23530c0a4b539 (patch)
tree0f163a74f1ef7beaf5aa475cb258fa6dde460063 /yardstick
parent5be7080aa01c0d108769ef04d93e3a96ee8e4bd6 (diff)
parentb3d3d4e0454be6175f161fba7d22113e5dd8a150 (diff)
Merge "Add VPP traffic profiles for TRex traffic generator"
Diffstat (limited to 'yardstick')
-rw-r--r--yardstick/network_services/traffic_profile/__init__.py1
-rw-r--r--yardstick/network_services/traffic_profile/vpp_rfc2544.py321
-rw-r--r--yardstick/tests/unit/network_services/traffic_profile/test_vpp_rfc2544.py819
3 files changed, 1141 insertions, 0 deletions
diff --git a/yardstick/network_services/traffic_profile/__init__.py b/yardstick/network_services/traffic_profile/__init__.py
index 72a61b6b4..c5d8eff0b 100644
--- a/yardstick/network_services/traffic_profile/__init__.py
+++ b/yardstick/network_services/traffic_profile/__init__.py
@@ -30,6 +30,7 @@ def register_modules():
'yardstick.network_services.traffic_profile.rfc2544',
'yardstick.network_services.traffic_profile.pktgen',
'yardstick.network_services.traffic_profile.landslide_profile',
+ 'yardstick.network_services.traffic_profile.vpp_rfc2544',
]
for module in modules:
diff --git a/yardstick/network_services/traffic_profile/vpp_rfc2544.py b/yardstick/network_services/traffic_profile/vpp_rfc2544.py
new file mode 100644
index 000000000..0f8185f90
--- /dev/null
+++ b/yardstick/network_services/traffic_profile/vpp_rfc2544.py
@@ -0,0 +1,321 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import datetime
+import logging
+from random import choice
+from string import ascii_letters
+
+from ipaddress import ip_address
+from trex_stl_lib import api as Pkt
+from trex_stl_lib import trex_stl_client
+from trex_stl_lib import trex_stl_packet_builder_scapy
+from trex_stl_lib import trex_stl_streams
+
+from yardstick.common import constants
+from yardstick.network_services.traffic_profile.rfc2544 import RFC2544Profile, \
+ PortPgIDMap
+from yardstick.network_services.traffic_profile.trex_traffic_profile import IP, \
+ DST
+
+LOGGING = logging.getLogger(__name__)
+
+
+class VppRFC2544Profile(RFC2544Profile):
+
+ def __init__(self, traffic_generator):
+ super(VppRFC2544Profile, self).__init__(traffic_generator)
+
+ self.duration = self.config.duration
+ self.precision = self.config.test_precision
+ self.lower_bound = self.config.lower_bound
+ self.upper_bound = self.config.upper_bound
+ self.step_interval = self.config.step_interval
+ self.enable_latency = self.config.enable_latency
+
+ self.pkt_size = None
+ self.flow = None
+
+ self.tolerance_low = 0
+ self.tolerance_high = 0
+
+ self.queue = None
+ self.port_pg_id = None
+
+ self.current_lower = self.lower_bound
+ self.current_upper = self.upper_bound
+
+ self.ports = []
+ self.profiles = {}
+
+ @property
+ def delta(self):
+ return self.current_upper - self.current_lower
+
+ @property
+ def mid_point(self):
+ return (self.current_lower + self.current_upper) / 2
+
+ @staticmethod
+ def calculate_frame_size(imix):
+ if not imix:
+ return 64, 100
+
+ imix_count = {size.upper().replace('B', ''): int(weight)
+ for size, weight in imix.items()}
+ imix_sum = sum(imix_count.values())
+ if imix_sum <= 0:
+ return 64, 100
+ packets_total = sum([int(size) * weight
+ for size, weight in imix_count.items()])
+ return packets_total / imix_sum, imix_sum
+
+ @staticmethod
+ def _gen_payload(length):
+ payload = ""
+ for _ in range(length):
+ payload += choice(ascii_letters)
+
+ return payload
+
+ def bounds_iterator(self, logger=None):
+ self.current_lower = self.lower_bound
+ self.current_upper = self.upper_bound
+
+ test_value = self.current_upper
+ while abs(self.delta) >= self.precision:
+ if logger:
+ logger.debug("New interval [%s, %s), precision: %d",
+ self.current_lower,
+ self.current_upper, self.step_interval)
+ logger.info("Testing with value %s", test_value)
+
+ yield test_value
+ test_value = self.mid_point
+
+ def register_generator(self, generator):
+ super(VppRFC2544Profile, self).register_generator(generator)
+ self.init_traffic_params(generator)
+
+ def init_queue(self, queue):
+ self.queue = queue
+ self.queue.cancel_join_thread()
+
+ def init_traffic_params(self, generator):
+ if generator.rfc2544_helper.latency:
+ self.enable_latency = True
+ self.tolerance_low = generator.rfc2544_helper.tolerance_low
+ self.tolerance_high = generator.rfc2544_helper.tolerance_high
+ self.max_rate = generator.scenario_helper.all_options.get('vpp_config',
+ {}).get(
+ 'max_rate', self.rate)
+
+ def create_profile(self, profile_data, current_port):
+ streams = []
+ for packet_name in profile_data:
+ imix = (profile_data[packet_name].
+ get('outer_l2', {}).get('framesize'))
+ self.pkt_size, imix_sum = self.calculate_frame_size(imix)
+ self._create_vm(profile_data[packet_name])
+ if self.max_rate > 100:
+ imix_data = self._create_imix_data(imix,
+ constants.DISTRIBUTION_IN_PACKETS)
+ else:
+ imix_data = self._create_imix_data(imix)
+ _streams = self._create_single_stream(current_port, imix_data,
+ imix_sum)
+ streams.extend(_streams)
+ return trex_stl_streams.STLProfile(streams)
+
+ def _set_outer_l3v4_fields(self, outer_l3v4):
+ """ setup outer l3v4 fields from traffic profile """
+ ip_params = {}
+ if 'proto' in outer_l3v4:
+ ip_params['proto'] = outer_l3v4['proto']
+ self._set_proto_fields(IP, **ip_params)
+
+ self.flow = int(outer_l3v4['count'])
+ src_start_ip, _ = outer_l3v4['srcip4'].split('-')
+ dst_start_ip, _ = outer_l3v4['dstip4'].split('-')
+
+ self.ip_packet = Pkt.IP(src=src_start_ip,
+ dst=dst_start_ip,
+ proto=outer_l3v4['proto'])
+ if self.flow > 1:
+ dst_start_int = int(ip_address(str(dst_start_ip)))
+ dst_end_ip_new = ip_address(dst_start_int + self.flow - 1)
+ # self._set_proto_addr(IP, SRC, outer_l3v4['srcip4'], outer_l3v4['count'])
+ self._set_proto_addr(IP, DST,
+ "{start_ip}-{end_ip}".format(
+ start_ip=dst_start_ip,
+ end_ip=str(dst_end_ip_new)),
+ self.flow)
+
+ def _create_single_packet(self, size=64):
+ ether_packet = self.ether_packet
+ ip_packet = self.ip6_packet if self.ip6_packet else self.ip_packet
+ base_pkt = ether_packet / ip_packet
+ payload_len = max(0, size - len(base_pkt) - 4)
+ packet = trex_stl_packet_builder_scapy.STLPktBuilder(
+ pkt=base_pkt / self._gen_payload(payload_len),
+ vm=self.trex_vm)
+ packet_lat = trex_stl_packet_builder_scapy.STLPktBuilder(
+ pkt=base_pkt / self._gen_payload(payload_len))
+
+ return packet, packet_lat
+
+ def _create_single_stream(self, current_port, imix_data, imix_sum,
+ isg=0.0):
+ streams = []
+ for size, weight in ((int(size), float(weight)) for (size, weight)
+ in imix_data.items() if float(weight) > 0):
+ if current_port == 1:
+ isg += 10.0
+ if self.max_rate > 100:
+ mode = trex_stl_streams.STLTXCont(
+ pps=int(weight * imix_sum / 100))
+ mode_lat = mode
+ else:
+ mode = trex_stl_streams.STLTXCont(
+ percentage=weight * self.max_rate / 100)
+ mode_lat = trex_stl_streams.STLTXCont(pps=9000)
+
+ packet, packet_lat = self._create_single_packet(size)
+ streams.append(
+ trex_stl_client.STLStream(isg=isg, packet=packet, mode=mode))
+ if self.enable_latency:
+ pg_id = self.port_pg_id.increase_pg_id(current_port)
+ stl_flow = trex_stl_streams.STLFlowLatencyStats(pg_id=pg_id)
+ stream_lat = trex_stl_client.STLStream(isg=isg,
+ packet=packet_lat,
+ mode=mode_lat,
+ flow_stats=stl_flow)
+ streams.append(stream_lat)
+ return streams
+
+ def execute_traffic(self, traffic_generator=None):
+ if traffic_generator is not None and self.generator is None:
+ self.generator = traffic_generator
+
+ self.ports = []
+ self.profiles = {}
+ self.port_pg_id = PortPgIDMap()
+ for vld_id, intfs in sorted(self.generator.networks.items()):
+ profile_data = self.params.get(vld_id)
+ if not profile_data:
+ continue
+ if (vld_id.startswith(self.DOWNLINK) and
+ self.generator.rfc2544_helper.correlated_traffic):
+ continue
+ for intf in intfs:
+ current_port = int(self.generator.port_num(intf))
+ self.port_pg_id.add_port(current_port)
+ profile = self.create_profile(profile_data, current_port)
+ self.generator.client.add_streams(profile,
+ ports=[current_port])
+
+ self.ports.append(current_port)
+ self.profiles[current_port] = profile
+
+ timeout = self.generator.scenario_helper.scenario_cfg["runner"][
+ "duration"]
+ test_data = {
+ "test_duration": timeout,
+ "test_precision": self.precision,
+ "tolerated_loss": self.tolerance_high,
+ "duration": self.duration,
+ "packet_size": self.pkt_size,
+ "flow": self.flow
+ }
+
+ if self.max_rate > 100:
+ self.binary_search_with_optimized(self.generator, self.duration,
+ timeout, test_data)
+ else:
+ self.binary_search(self.generator, self.duration,
+ self.tolerance_high, test_data)
+
+ def binary_search_with_optimized(self, traffic_generator, duration,
+ timeout, test_data):
+ # TODO Support FD.io Multiple Loss Ratio search (MLRsearch)
+ pass
+
+ def binary_search(self, traffic_generator, duration, tolerance_value,
+ test_data):
+ theor_max_thruput = 0
+ result_samples = {}
+
+ for test_value in self.bounds_iterator(LOGGING):
+ stats = traffic_generator.send_traffic_on_tg(self.ports,
+ self.port_pg_id,
+ duration,
+ str(
+ test_value / self.max_rate / 2),
+ latency=self.enable_latency)
+ traffic_generator.client.reset(ports=self.ports)
+ traffic_generator.client.clear_stats(ports=self.ports)
+ traffic_generator.client.remove_all_streams(ports=self.ports)
+ for port, profile in self.profiles.items():
+ traffic_generator.client.add_streams(profile, ports=[port])
+
+ loss_ratio = (float(traffic_generator.loss) / float(
+ traffic_generator.sent)) * 100
+
+ samples = traffic_generator.generate_samples(stats, self.ports,
+ self.port_pg_id,
+ self.enable_latency)
+ samples.update(test_data)
+ LOGGING.info("Collect TG KPIs %s %s %s", datetime.datetime.now(),
+ test_value, samples)
+ self.queue.put(samples)
+
+ if float(loss_ratio) > float(tolerance_value):
+ LOGGING.debug("Failure... Decreasing upper bound")
+ self.current_upper = test_value
+ else:
+ LOGGING.debug("Success! Increasing lower bound")
+ self.current_lower = test_value
+
+ rate_total = float(traffic_generator.sent) / float(duration)
+ bandwidth_total = float(rate_total) * (
+ float(self.pkt_size) + 20) * 8 / (10 ** 9)
+
+ success_samples = {'Result_' + key: value for key, value in
+ samples.items()}
+ success_samples["Result_{}".format('PDR')] = {
+ "rate_total_pps": float(rate_total),
+ "bandwidth_total_Gbps": float(bandwidth_total),
+ "packet_loss_ratio": float(loss_ratio),
+ "packets_lost": int(traffic_generator.loss),
+ }
+ self.queue.put(success_samples)
+
+ # Store Actual throughput for result samples
+ for intf in traffic_generator.vnfd_helper.interfaces:
+ name = intf["name"]
+ result_samples[name] = {
+ "Result_Actual_throughput": float(
+ success_samples["Result_{}".format(name)][
+ "rx_throughput_bps"]),
+ }
+
+ for intf in traffic_generator.vnfd_helper.interfaces:
+ name = intf["name"]
+ if theor_max_thruput < samples[name]["tx_throughput_bps"]:
+ theor_max_thruput = samples[name]['tx_throughput_bps']
+ self.queue.put({'theor_max_throughput': theor_max_thruput})
+
+ result_samples["Result_theor_max_throughput"] = theor_max_thruput
+ self.queue.put(result_samples)
+ return result_samples
diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_vpp_rfc2544.py b/yardstick/tests/unit/network_services/traffic_profile/test_vpp_rfc2544.py
new file mode 100644
index 000000000..4e5a0f708
--- /dev/null
+++ b/yardstick/tests/unit/network_services/traffic_profile/test_vpp_rfc2544.py
@@ -0,0 +1,819 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import mock
+from trex_stl_lib import trex_stl_client
+from trex_stl_lib import trex_stl_packet_builder_scapy
+from trex_stl_lib import trex_stl_streams
+
+from yardstick.common import constants
+from yardstick.network_services.traffic_profile import base as tp_base
+from yardstick.network_services.traffic_profile import rfc2544, vpp_rfc2544
+from yardstick.tests.unit import base
+
+
+class TestVppRFC2544Profile(base.BaseUnitTestCase):
+ TRAFFIC_PROFILE = {
+ "schema": "isb:traffic_profile:0.1",
+ "name": "fixed",
+ "description": "Fixed traffic profile to run UDP traffic",
+ "traffic_profile": {
+ "traffic_type": "FixedTraffic",
+ "duration": 30,
+ "enable_latency": True,
+ "frame_rate": 100,
+ "intermediate_phases": 2,
+ "lower_bound": 1.0,
+ "step_interval": 0.5,
+ "test_precision": 0.1,
+ "upper_bound": 100.0}}
+
+ TRAFFIC_PROFILE_MAX_RATE = {
+ "schema": "isb:traffic_profile:0.1",
+ "name": "fixed",
+ "description": "Fixed traffic profile to run UDP traffic",
+ "traffic_profile": {
+ "traffic_type": "FixedTraffic",
+ "duration": 30,
+ "enable_latency": True,
+ "frame_rate": 10000,
+ "intermediate_phases": 2,
+ "lower_bound": 1.0,
+ "step_interval": 0.5,
+ "test_precision": 0.1,
+ "upper_bound": 100.0}}
+
+ PROFILE = {
+ "description": "Traffic profile to run RFC2544 latency",
+ "downlink_0": {
+ "ipv4": {
+ "id": 2,
+ "outer_l2": {
+ "framesize": {
+ "1024B": "0",
+ "1280B": "0",
+ "128B": "0",
+ "1400B": "0",
+ "1500B": "0",
+ "1518B": "0",
+ "256B": "0",
+ "373b": "0",
+ "512B": "0",
+ "570B": "0",
+ "64B": "100"
+ }
+ },
+ "outer_l3v4": {
+ "count": "1",
+ "dstip4": "10.0.0.0-10.0.0.100",
+ "proto": 61,
+ "srcip4": "20.0.0.0-20.0.0.100"
+ }
+ }
+ },
+ "name": "rfc2544",
+ "schema": "nsb:traffic_profile:0.1",
+ "traffic_profile": {
+ "duration": 30,
+ "enable_latency": True,
+ "frame_rate": 100,
+ "intermediate_phases": 2,
+ "lower_bound": 1.0,
+ "step_interval": 0.5,
+ "test_precision": 0.1,
+ "traffic_type": "VppRFC2544Profile",
+ "upper_bound": 100.0
+ },
+ "uplink": {
+ "ipv4": {
+ "id": 1,
+ "outer_l2": {
+ "framesize": {
+ "1024B": "0",
+ "1280B": "0",
+ "128B": "0",
+ "1400B": "0",
+ "1500B": "0",
+ "1518B": "0",
+ "256B": "0",
+ "373B": "0",
+ "512B": "0",
+ "570B": "0",
+ "64B": "100"
+ }
+ },
+ "outer_l3v4": {
+ "count": "10",
+ "dstip4": "20.0.0.0-20.0.0.100",
+ "proto": 61,
+ "srcip4": "10.0.0.0-10.0.0.100"
+ }
+ }
+ }
+ }
+
+ def test___init__(self):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ self.assertEqual(vpp_rfc2544_profile.max_rate,
+ vpp_rfc2544_profile.rate)
+ self.assertEqual(0, vpp_rfc2544_profile.min_rate)
+ self.assertEqual(30, vpp_rfc2544_profile.duration)
+ self.assertEqual(0.1, vpp_rfc2544_profile.precision)
+ self.assertEqual(1.0, vpp_rfc2544_profile.lower_bound)
+ self.assertEqual(100.0, vpp_rfc2544_profile.upper_bound)
+ self.assertEqual(0.5, vpp_rfc2544_profile.step_interval)
+ self.assertEqual(True, vpp_rfc2544_profile.enable_latency)
+
+ def test_init_traffic_params(self):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ mock_generator = mock.MagicMock()
+ mock_generator.rfc2544_helper.latency = True
+ mock_generator.rfc2544_helper.tolerance_low = 0.0
+ mock_generator.rfc2544_helper.tolerance_high = 0.005
+ mock_generator.scenario_helper.all_options = {
+ "vpp_config": {
+ "max_rate": 14880000
+ }
+ }
+ vpp_rfc2544_profile.init_traffic_params(mock_generator)
+ self.assertEqual(0.0, vpp_rfc2544_profile.tolerance_low)
+ self.assertEqual(0.005, vpp_rfc2544_profile.tolerance_high)
+ self.assertEqual(14880000, vpp_rfc2544_profile.max_rate)
+ self.assertEqual(True, vpp_rfc2544_profile.enable_latency)
+
+ def test_calculate_frame_size(self):
+ imix = {'40B': 7, '576B': 4, '1500B': 1}
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ self.assertEqual((4084 / 12, 12),
+ vpp_rfc2544_profile.calculate_frame_size(imix))
+
+ def test_calculate_frame_size_empty(self):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ self.assertEqual((64, 100),
+ vpp_rfc2544_profile.calculate_frame_size(None))
+
+ def test_calculate_frame_size_error(self):
+ imix = {'40B': -7, '576B': 4, '1500B': 1}
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ self.assertEqual((64, 100),
+ vpp_rfc2544_profile.calculate_frame_size(imix))
+
+ def test__gen_payload(self):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ self.assertIsNotNone(vpp_rfc2544_profile._gen_payload(4))
+
+ def test_register_generator(self):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ mock_generator = mock.MagicMock()
+ mock_generator.rfc2544_helper.latency = True
+ mock_generator.rfc2544_helper.tolerance_low = 0.0
+ mock_generator.rfc2544_helper.tolerance_high = 0.005
+ mock_generator.scenario_helper.all_options = {
+ "vpp_config": {
+ "max_rate": 14880000
+ }
+ }
+ vpp_rfc2544_profile.register_generator(mock_generator)
+ self.assertEqual(0.0, vpp_rfc2544_profile.tolerance_low)
+ self.assertEqual(0.005, vpp_rfc2544_profile.tolerance_high)
+ self.assertEqual(14880000, vpp_rfc2544_profile.max_rate)
+ self.assertEqual(True, vpp_rfc2544_profile.enable_latency)
+
+ def test_stop_traffic(self):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ mock_generator = mock.Mock()
+ vpp_rfc2544_profile.stop_traffic(traffic_generator=mock_generator)
+ mock_generator.client.stop.assert_called_once()
+ mock_generator.client.reset.assert_called_once()
+ mock_generator.client.remove_all_streams.assert_called_once()
+
+ def test_execute_traffic(self):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ vpp_rfc2544_profile.init_queue(mock.MagicMock())
+ vpp_rfc2544_profile.params = {
+ 'downlink_0': 'profile1',
+ 'uplink_0': 'profile2'}
+ mock_generator = mock.MagicMock()
+ mock_generator.networks = {
+ 'downlink_0': ['xe0', 'xe1'],
+ 'uplink_0': ['xe2', 'xe3'],
+ 'uplink_1': ['xe2', 'xe3']}
+ mock_generator.port_num.side_effect = [10, 20, 30, 40]
+ mock_generator.rfc2544_helper.correlated_traffic = False
+
+ with mock.patch.object(vpp_rfc2544_profile, 'create_profile') as \
+ mock_create_profile:
+ vpp_rfc2544_profile.execute_traffic(
+ traffic_generator=mock_generator)
+ mock_create_profile.assert_has_calls([
+ mock.call('profile1', 10),
+ mock.call('profile1', 20),
+ mock.call('profile2', 30),
+ mock.call('profile2', 40)])
+ mock_generator.client.add_streams.assert_has_calls([
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20]),
+ mock.call(mock.ANY, ports=[30]),
+ mock.call(mock.ANY, ports=[40])])
+
+ def test_execute_traffic_correlated_traffic(self):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ vpp_rfc2544_profile.init_queue(mock.MagicMock())
+ vpp_rfc2544_profile.params = {
+ 'downlink_0': 'profile1',
+ 'uplink_0': 'profile2'}
+ mock_generator = mock.MagicMock()
+ mock_generator.networks = {
+ 'downlink_0': ['xe0', 'xe1'],
+ 'uplink_0': ['xe2', 'xe3']}
+ mock_generator.port_num.side_effect = [10, 20, 30, 40]
+ mock_generator.rfc2544_helper.correlated_traffic = True
+
+ with mock.patch.object(vpp_rfc2544_profile, 'create_profile') as \
+ mock_create_profile:
+ vpp_rfc2544_profile.execute_traffic(
+ traffic_generator=mock_generator)
+ mock_create_profile.assert_has_calls([
+ mock.call('profile2', 10),
+ mock.call('profile2', 20)])
+ mock_generator.client.add_streams.assert_has_calls([
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20]),
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20]),
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20]),
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20]),
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20]),
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20]),
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20]),
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20]),
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20]),
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20]),
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20]),
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20])])
+
+ def test_execute_traffic_max_rate(self):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE_MAX_RATE)
+ vpp_rfc2544_profile.init_queue(mock.MagicMock())
+ vpp_rfc2544_profile.params = {
+ 'downlink_0': 'profile1',
+ 'uplink_0': 'profile2'}
+ mock_generator = mock.MagicMock()
+ mock_generator.networks = {
+ 'downlink_0': ['xe0', 'xe1'],
+ 'uplink_0': ['xe2', 'xe3']}
+ mock_generator.port_num.side_effect = [10, 20, 30, 40]
+ mock_generator.rfc2544_helper.correlated_traffic = False
+
+ with mock.patch.object(vpp_rfc2544_profile, 'create_profile') as \
+ mock_create_profile:
+ vpp_rfc2544_profile.execute_traffic(
+ traffic_generator=mock_generator)
+ mock_create_profile.assert_has_calls([
+ mock.call('profile1', 10),
+ mock.call('profile1', 20),
+ mock.call('profile2', 30),
+ mock.call('profile2', 40)])
+ mock_generator.client.add_streams.assert_has_calls([
+ mock.call(mock.ANY, ports=[10]),
+ mock.call(mock.ANY, ports=[20]),
+ mock.call(mock.ANY, ports=[30]),
+ mock.call(mock.ANY, ports=[40])])
+
+ @mock.patch.object(trex_stl_streams, 'STLProfile')
+ def test_create_profile(self, mock_stl_profile):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ port = mock.ANY
+ profile_data = {'packetid_1': {'outer_l2': {'framesize': 'imix_info'}}}
+ with mock.patch.object(vpp_rfc2544_profile, 'calculate_frame_size') as \
+ mock_calculate_frame_size, \
+ mock.patch.object(vpp_rfc2544_profile, '_create_imix_data') as \
+ mock_create_imix, \
+ mock.patch.object(vpp_rfc2544_profile, '_create_vm') as \
+ mock_create_vm, \
+ mock.patch.object(vpp_rfc2544_profile,
+ '_create_single_stream') as \
+ mock_create_single_stream:
+ mock_calculate_frame_size.return_value = 64, 100
+ mock_create_imix.return_value = 'imix_data'
+ mock_create_single_stream.return_value = ['stream1']
+ vpp_rfc2544_profile.create_profile(profile_data, port)
+
+ mock_create_imix.assert_called_once_with('imix_info')
+ mock_create_vm.assert_called_once_with(
+ {'outer_l2': {'framesize': 'imix_info'}})
+ mock_create_single_stream.assert_called_once_with(port, 'imix_data',
+ 100)
+ mock_stl_profile.assert_called_once_with(['stream1'])
+
+ @mock.patch.object(trex_stl_streams, 'STLProfile')
+ def test_create_profile_max_rate(self, mock_stl_profile):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE_MAX_RATE)
+ port = mock.ANY
+ profile_data = {'packetid_1': {'outer_l2': {'framesize': 'imix_info'}}}
+ with mock.patch.object(vpp_rfc2544_profile, 'calculate_frame_size') as \
+ mock_calculate_frame_size, \
+ mock.patch.object(vpp_rfc2544_profile, '_create_imix_data') as \
+ mock_create_imix, \
+ mock.patch.object(vpp_rfc2544_profile, '_create_vm') as \
+ mock_create_vm, \
+ mock.patch.object(vpp_rfc2544_profile,
+ '_create_single_stream') as \
+ mock_create_single_stream:
+ mock_calculate_frame_size.return_value = 64, 100
+ mock_create_imix.return_value = 'imix_data'
+ mock_create_single_stream.return_value = ['stream1']
+ vpp_rfc2544_profile.create_profile(profile_data, port)
+
+ mock_create_imix.assert_called_once_with('imix_info', 'mode_DIP')
+ mock_create_vm.assert_called_once_with(
+ {'outer_l2': {'framesize': 'imix_info'}})
+ mock_create_single_stream.assert_called_once_with(port, 'imix_data',
+ 100)
+ mock_stl_profile.assert_called_once_with(['stream1'])
+
+ def test__create_imix_data_mode_DIP(self):
+ rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(self.TRAFFIC_PROFILE)
+ data = {'64B': 50, '128B': 50}
+ self.assertEqual(
+ {'64': 50.0, '128': 50.0},
+ rfc2544_profile._create_imix_data(
+ data, weight_mode=constants.DISTRIBUTION_IN_PACKETS))
+ data = {'64B': 1, '128b': 3}
+ self.assertEqual(
+ {'64': 25.0, '128': 75.0},
+ rfc2544_profile._create_imix_data(
+ data, weight_mode=constants.DISTRIBUTION_IN_PACKETS))
+ data = {}
+ self.assertEqual(
+ {},
+ rfc2544_profile._create_imix_data(
+ data, weight_mode=constants.DISTRIBUTION_IN_PACKETS))
+
+ def test__create_imix_data_mode_DIB(self):
+ rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(self.TRAFFIC_PROFILE)
+ data = {'64B': 25, '128B': 25, '512B': 25, '1518B': 25}
+ byte_total = 64 * 25 + 128 * 25 + 512 * 25 + 1518 * 25
+ self.assertEqual(
+ {'64': 64 * 25.0 * 100 / byte_total,
+ '128': 128 * 25.0 * 100 / byte_total,
+ '512': 512 * 25.0 * 100 / byte_total,
+ '1518': 1518 * 25.0 * 100 / byte_total},
+ rfc2544_profile._create_imix_data(
+ data, weight_mode=constants.DISTRIBUTION_IN_BYTES))
+ data = {}
+ self.assertEqual(
+ {},
+ rfc2544_profile._create_imix_data(
+ data, weight_mode=constants.DISTRIBUTION_IN_BYTES))
+ data = {'64B': 100}
+ self.assertEqual(
+ {'64': 100.0},
+ rfc2544_profile._create_imix_data(
+ data, weight_mode=constants.DISTRIBUTION_IN_BYTES))
+
+ def test__create_vm(self):
+ packet = {'outer_l2': 'l2_definition'}
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ with mock.patch.object(vpp_rfc2544_profile, '_set_outer_l2_fields') as \
+ mock_l2_fileds:
+ vpp_rfc2544_profile._create_vm(packet)
+ mock_l2_fileds.assert_called_once_with('l2_definition')
+
+ @mock.patch.object(trex_stl_packet_builder_scapy, 'STLPktBuilder',
+ return_value='packet')
+ def test__create_single_packet(self, mock_pktbuilder):
+ size = 128
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ vpp_rfc2544_profile.ether_packet = mock.MagicMock()
+ vpp_rfc2544_profile.ip_packet = mock.MagicMock()
+ vpp_rfc2544_profile.udp_packet = mock.MagicMock()
+ vpp_rfc2544_profile.trex_vm = 'trex_vm'
+ # base_pkt = (
+ # vpp_rfc2544_profile.ether_packet / vpp_rfc2544_profile.ip_packet /
+ # vpp_rfc2544_profile.udp_packet)
+ # pad = (size - len(base_pkt)) * 'x'
+ output = vpp_rfc2544_profile._create_single_packet(size=size)
+ self.assertEqual(mock_pktbuilder.call_count, 2)
+ # mock_pktbuilder.assert_called_once_with(pkt=base_pkt / pad,
+ # vm='trex_vm')
+ self.assertEqual(output, ('packet', 'packet'))
+
+ def test__set_outer_l3v4_fields(self):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ outer_l3v4 = self.PROFILE[
+ tp_base.TrafficProfile.UPLINK]['ipv4']['outer_l3v4']
+ outer_l3v4['proto'] = 'tcp'
+ self.assertIsNone(
+ vpp_rfc2544_profile._set_outer_l3v4_fields(outer_l3v4))
+
+ @mock.patch.object(trex_stl_streams, 'STLFlowLatencyStats')
+ @mock.patch.object(trex_stl_streams, 'STLTXCont')
+ @mock.patch.object(trex_stl_client, 'STLStream')
+ def test__create_single_stream(self, mock_stream, mock_txcont,
+ mock_latency):
+ imix_data = {'64': 25, '512': 75}
+ mock_stream.side_effect = ['stream1', 'stream2', 'stream3', 'stream4']
+ mock_txcont.side_effect = ['txcont1', 'txcont2', 'txcont3', 'txcont4']
+ mock_latency.side_effect = ['latency1', 'latency2']
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ vpp_rfc2544_profile.port_pg_id = rfc2544.PortPgIDMap()
+ vpp_rfc2544_profile.port_pg_id.add_port(10)
+ with mock.patch.object(vpp_rfc2544_profile, '_create_single_packet') as \
+ mock_create_single_packet:
+ mock_create_single_packet.return_value = 64, 100
+ output = vpp_rfc2544_profile._create_single_stream(10, imix_data,
+ 100, 0.0)
+ self.assertEqual(['stream1', 'stream2', 'stream3', 'stream4'], output)
+ mock_latency.assert_has_calls([
+ mock.call(pg_id=1), mock.call(pg_id=2)])
+ mock_txcont.assert_has_calls([
+ mock.call(percentage=25 * 100 / 100),
+ mock.call(percentage=75 * 100 / 100)], any_order=True)
+
+ @mock.patch.object(trex_stl_streams, 'STLFlowLatencyStats')
+ @mock.patch.object(trex_stl_streams, 'STLTXCont')
+ @mock.patch.object(trex_stl_client, 'STLStream')
+ def test__create_single_stream_max_rate(self, mock_stream, mock_txcont,
+ mock_latency):
+ imix_data = {'64': 25, '512': 75}
+ mock_stream.side_effect = ['stream1', 'stream2', 'stream3', 'stream4']
+ mock_txcont.side_effect = ['txcont1', 'txcont2', 'txcont3', 'txcont4']
+ mock_latency.side_effect = ['latency1', 'latency2']
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE_MAX_RATE)
+ vpp_rfc2544_profile.port_pg_id = rfc2544.PortPgIDMap()
+ vpp_rfc2544_profile.port_pg_id.add_port(1)
+ with mock.patch.object(vpp_rfc2544_profile, '_create_single_packet') as \
+ mock_create_single_packet:
+ mock_create_single_packet.return_value = 64, 100
+ output = vpp_rfc2544_profile._create_single_stream(1, imix_data,
+ 100, 0.0)
+ self.assertEqual(['stream1', 'stream2', 'stream3', 'stream4'], output)
+ mock_latency.assert_has_calls([
+ mock.call(pg_id=1), mock.call(pg_id=2)])
+ mock_txcont.assert_has_calls([
+ mock.call(pps=int(25 * 100 / 100)),
+ mock.call(pps=int(75 * 100 / 100))], any_order=True)
+
+ @mock.patch.object(trex_stl_streams, 'STLFlowLatencyStats')
+ @mock.patch.object(trex_stl_streams, 'STLTXCont')
+ @mock.patch.object(trex_stl_client, 'STLStream')
+ def test__create_single_stream_mlr_search(self, mock_stream, mock_txcont,
+ mock_latency):
+ imix_data = {'64': 25, '512': 75}
+ mock_stream.side_effect = ['stream1', 'stream2', 'stream3', 'stream4']
+ mock_txcont.side_effect = ['txcont1', 'txcont2', 'txcont3', 'txcont4']
+ mock_latency.side_effect = ['latency1', 'latency2']
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ vpp_rfc2544_profile.max_rate = 14880000
+ vpp_rfc2544_profile.port_pg_id = rfc2544.PortPgIDMap()
+ vpp_rfc2544_profile.port_pg_id.add_port(10)
+ with mock.patch.object(vpp_rfc2544_profile, '_create_single_packet') as \
+ mock_create_single_packet:
+ mock_create_single_packet.return_value = 64, 100
+ output = vpp_rfc2544_profile._create_single_stream(10, imix_data,
+ 100, 0.0)
+ self.assertEqual(['stream1', 'stream2', 'stream3', 'stream4'], output)
+ mock_latency.assert_has_calls([
+ mock.call(pg_id=1), mock.call(pg_id=2)])
+ mock_txcont.assert_has_calls([
+ mock.call(pps=25 * 100 / 100),
+ mock.call(pps=75 * 100 / 100)], any_order=True)
+
+ def test_binary_search_with_optimized(self):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ mock_generator = mock.MagicMock()
+ self.assertIsNone(
+ vpp_rfc2544_profile.binary_search_with_optimized(mock_generator,
+ 30, 720, ''))
+
+ def test_binary_search(self):
+ vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
+ self.TRAFFIC_PROFILE)
+ vpp_rfc2544_profile.pkt_size = 64
+ vpp_rfc2544_profile.init_queue(mock.MagicMock())
+ mock_generator = mock.MagicMock()
+ mock_generator.vnfd_helper.interfaces = [
+ {"name": "xe0"}, {"name": "xe1"}
+ ]
+ stats = {
+ "0": {
+ "ibytes": 55549120,
+ "ierrors": 0,
+ "ipackets": 867955,
+ "obytes": 55549696,
+ "oerrors": 0,
+ "opackets": 867964,
+ "rx_bps": 104339032.0,
+ "rx_bps_L1": 136944984.0,
+ "rx_pps": 203787.2,
+ "rx_util": 1.36944984,
+ "tx_bps": 134126008.0,
+ "tx_bps_L1": 176040392.0,
+ "tx_pps": 261964.9,
+ "tx_util": 1.7604039200000001
+ },
+ "1": {
+ "ibytes": 55549696,
+ "ierrors": 0,
+ "ipackets": 867964,
+ "obytes": 55549120,
+ "oerrors": 0,
+ "opackets": 867955,
+ "rx_bps": 134119648.0,
+ "rx_bps_L1": 176032032.0,
+ "rx_pps": 261952.4,
+ "rx_util": 1.76032032,
+ "tx_bps": 104338192.0,
+ "tx_bps_L1": 136943872.0,
+ "tx_pps": 203785.5,
+ "tx_util": 1.36943872
+ },
+ "flow_stats": {
+ "1": {
+ "rx_bps": {
+ "0": 0,
+ "1": 0,
+ "total": 0
+ },
+ "rx_bps_l1": {
+ "0": 0.0,
+ "1": 0.0,
+ "total": 0.0
+ },
+ "rx_bytes": {
+ "0": 6400,
+ "1": 0,
+ "total": 6400
+ },
+ "rx_pkts": {
+ "0": 100,
+ "1": 0,
+ "total": 100
+ },
+ "rx_pps": {
+ "0": 0,
+ "1": 0,
+ "total": 0
+ },
+ "tx_bps": {
+ "0": 0,
+ "1": 0,
+ "total": 0
+ },
+ "tx_bps_l1": {
+ "0": 0.0,
+ "1": 0.0,
+ "total": 0.0
+ },
+ "tx_bytes": {
+ "0": 0,
+ "1": 6400,
+ "total": 6400
+ },
+ "tx_pkts": {
+ "0": 0,
+ "1": 100,
+ "total": 100
+ },
+ "tx_pps": {
+ "0": 0,
+ "1": 0,
+ "total": 0
+ }
+ },
+ "2": {
+ "rx_bps": {
+ "0": 0,
+ "1": 0,
+ "total": 0
+ },
+ "rx_bps_l1": {
+ "0": 0.0,
+ "1": 0.0,
+ "total": 0.0
+ },
+ "rx_bytes": {
+ "0": 0,
+ "1": 6464,
+ "total": 6464
+ },
+ "rx_pkts": {
+ "0": 0,
+ "1": 101,
+ "total": 101
+ },
+ "rx_pps": {
+ "0": 0,
+ "1": 0,
+ "total": 0
+ },
+ "tx_bps": {
+ "0": 0,
+ "1": 0,
+ "total": 0
+ },
+ "tx_bps_l1": {
+ "0": 0.0,
+ "1": 0.0,
+ "total": 0.0
+ },
+ "tx_bytes": {
+ "0": 6464,
+ "1": 0,
+ "total": 6464
+ },
+ "tx_pkts": {
+ "0": 101,
+ "1": 0,
+ "total": 101
+ },
+ "tx_pps": {
+ "0": 0,
+ "1": 0,
+ "total": 0
+ }
+ },
+ "global": {
+ "rx_err": {
+ "0": 0,
+ "1": 0
+ },
+ "tx_err": {
+ "0": 0,
+ "1": 0
+ }
+ }
+ },
+ "global": {
+ "bw_per_core": 45.6,
+ "cpu_util": 0.1494,
+ "queue_full": 0,
+ "rx_bps": 238458672.0,
+ "rx_cpu_util": 4.751e-05,
+ "rx_drop_bps": 0.0,
+ "rx_pps": 465739.6,
+ "tx_bps": 238464208.0,
+ "tx_pps": 465750.4
+ },
+ "latency": {
+ "1": {
+ "err_cntrs": {
+ "dropped": 0,
+ "dup": 0,
+ "out_of_order": 0,
+ "seq_too_high": 0,
+ "seq_too_low": 0
+ },
+ "latency": {
+ "average": 63.375,
+ "histogram": {
+ "20": 1,
+ "30": 18,
+ "40": 12,
+ "50": 10,
+ "60": 12,
+ "70": 11,
+ "80": 6,
+ "90": 10,
+ "100": 20
+ },
+ "jitter": 23,
+ "last_max": 122,
+ "total_max": 123,
+ "total_min": 20
+ }
+ },
+ "2": {
+ "err_cntrs": {
+ "dropped": 0,
+ "dup": 0,
+ "out_of_order": 0,
+ "seq_too_high": 0,
+ "seq_too_low": 0
+ },
+ "latency": {
+ "average": 74,
+ "histogram": {
+ "60": 20,
+ "70": 10,
+ "80": 3,
+ "90": 4,
+ "100": 64
+ },
+ "jitter": 6,
+ "last_max": 83,
+ "total_max": 135,
+ "total_min": 60
+ }
+ },
+ "global": {
+ "bad_hdr": 0,
+ "old_flow": 0
+ }
+ },
+ "total": {
+ "ibytes": 111098816,
+ "ierrors": 0,
+ "ipackets": 1735919,
+ "obytes": 111098816,
+ "oerrors": 0,
+ "opackets": 1735919,
+ "rx_bps": 238458680.0,
+ "rx_bps_L1": 312977016.0,
+ "rx_pps": 465739.6,
+ "rx_util": 3.1297701599999996,
+ "tx_bps": 238464200.0,
+ "tx_bps_L1": 312984264.0,
+ "tx_pps": 465750.4,
+ "tx_util": 3.12984264
+ }
+ }
+ samples = {
+ "xe0": {
+ "in_packets": 867955,
+ "latency": {
+ "2": {
+ "avg_latency": 74.0,
+ "max_latency": 135.0,
+ "min_latency": 60.0
+ }
+ },
+ "out_packets": 867964,
+ "rx_throughput_bps": 104339032.0,
+ "rx_throughput_fps": 203787.2,
+ "tx_throughput_bps": 134126008.0,
+ "tx_throughput_fps": 261964.9
+ },
+ "xe1": {
+ "in_packets": 867964,
+ "latency": {
+ "1": {
+ "avg_latency": 63.375,
+ "max_latency": 123.0,
+ "min_latency": 20.0
+ }
+ },
+ "out_packets": 867955,
+ "rx_throughput_bps": 134119648.0,
+ "rx_throughput_fps": 261952.4,
+ "tx_throughput_bps": 104338192.0,
+ "tx_throughput_fps": 203785.5
+ }
+ }
+
+ mock_generator.loss = 0
+ mock_generator.sent = 2169700
+ mock_generator.send_traffic_on_tg = mock.Mock(return_value=stats)
+ mock_generator.generate_samples = mock.Mock(return_value=samples)
+
+ result_samples = vpp_rfc2544_profile.binary_search(
+ traffic_generator=mock_generator, duration=30,
+ tolerance_value=0.005,
+ test_data={})
+
+ expected = {'Result_theor_max_throughput': 134126008.0,
+ 'xe0': {'Result_Actual_throughput': 104339032.0},
+ 'xe1': {'Result_Actual_throughput': 134119648.0}}
+ self.assertEqual(expected, result_samples)