From 710fd29fe4a5e6f747ff397a4f54f966999279a2 Mon Sep 17 00:00:00 2001 From: Ross Brattain Date: Mon, 12 Feb 2018 17:34:40 -0800 Subject: NSB: Rename traffic_profile to trex_traffic_profile Make it more clear that traffic_profile is not a generic class. Eventually we can refactor a more generic traffic profile. Change-Id: I1bf44a8cafcdeb8d74efb9e85a34f6d7b526d036 Signed-off-by: Ross Brattain --- .../traffic_profile/test_ixia_rfc2544.py | 2 +- .../traffic_profile/test_rfc2544.py | 6 +- .../traffic_profile/test_traffic_profile.py | 311 -------------- .../traffic_profile/test_trex_traffic_profile.py | 311 ++++++++++++++ .../traffic_profile/ixia_rfc2544.py | 2 +- .../network_services/traffic_profile/rfc2544.py | 2 +- .../traffic_profile/traffic_profile.py | 471 --------------------- .../traffic_profile/trex_traffic_profile.py | 471 +++++++++++++++++++++ 8 files changed, 786 insertions(+), 790 deletions(-) delete mode 100644 tests/unit/network_services/traffic_profile/test_traffic_profile.py create mode 100644 tests/unit/network_services/traffic_profile/test_trex_traffic_profile.py delete mode 100644 yardstick/network_services/traffic_profile/traffic_profile.py create mode 100644 yardstick/network_services/traffic_profile/trex_traffic_profile.py diff --git a/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py b/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py index f13945abf..f83d3341d 100644 --- a/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py +++ b/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py @@ -29,7 +29,7 @@ stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) stl_patch.start() if stl_patch: - from yardstick.network_services.traffic_profile.traffic_profile \ + from yardstick.network_services.traffic_profile.trex_traffic_profile \ import TrexProfile from yardstick.network_services.traffic_profile.ixia_rfc2544 import \ IXIARFC2544Profile diff --git a/tests/unit/network_services/traffic_profile/test_rfc2544.py b/tests/unit/network_services/traffic_profile/test_rfc2544.py index 221233710..fe563e9fd 100644 --- a/tests/unit/network_services/traffic_profile/test_rfc2544.py +++ b/tests/unit/network_services/traffic_profile/test_rfc2544.py @@ -29,7 +29,7 @@ stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) stl_patch.start() if stl_patch: - from yardstick.network_services.traffic_profile.traffic_profile \ + from yardstick.network_services.traffic_profile.trex_traffic_profile \ import TrexProfile from yardstick.network_services.traffic_profile.rfc2544 import \ RFC2544Profile @@ -164,8 +164,6 @@ class TestRFC2544Profile(unittest.TestCase): "in_packets": 1000, "out_packets": 1002, } - tol_min = 0.0 - tolerance = 1.0 expected = { 'DropPercentage': 0.1996, 'RxThroughput': 33.333333333333336, @@ -208,8 +206,6 @@ class TestRFC2544Profile(unittest.TestCase): "tx_throughput_mbps": 10, "in_packets": 1000, "out_packets": 0} - tol_min = 0.0 - tolerance = 0.0 r_f_c2544_profile.throughput_max = 0 expected = { 'DropPercentage': 100.0, 'RxThroughput': 100 / 3.0, diff --git a/tests/unit/network_services/traffic_profile/test_traffic_profile.py b/tests/unit/network_services/traffic_profile/test_traffic_profile.py deleted file mode 100644 index 37b9a08d0..000000000 --- a/tests/unit/network_services/traffic_profile/test_traffic_profile.py +++ /dev/null @@ -1,311 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016-2017 Intel 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 ipaddress - -import mock -import six -import unittest - -from tests.unit import STL_MOCKS -from yardstick.common import exceptions as y_exc - -STLClient = mock.MagicMock() -stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) -stl_patch.start() - -if stl_patch: - from yardstick.network_services.traffic_profile.base import TrafficProfile - from yardstick.network_services.traffic_profile.traffic_profile import TrexProfile - from yardstick.network_services.traffic_profile.traffic_profile import SRC - from yardstick.network_services.traffic_profile.traffic_profile import DST - from yardstick.network_services.traffic_profile.traffic_profile import ETHERNET - from yardstick.network_services.traffic_profile.traffic_profile import IP - from yardstick.network_services.traffic_profile.traffic_profile import IPv6 - from yardstick.network_services.traffic_profile.traffic_profile import UDP - from yardstick.network_services.traffic_profile.traffic_profile import SRC_PORT - from yardstick.network_services.traffic_profile.traffic_profile import DST_PORT - from yardstick.network_services.traffic_profile.traffic_profile import TYPE_OF_SERVICE - - -class TestTrexProfile(unittest.TestCase): - TRAFFIC_PROFILE = { - "schema": "isb:traffic_profile:0.1", - "name": "fixed", - "description": "Fixed traffic profile to run UDP traffic", - "traffic_profile": { - "traffic_type": "FixedTraffic", - "frame_rate": 100, # pps - "flow_number": 10, - "frame_size": 64}} - - EXAMPLE_ETHERNET_ADDR = "00:00:00:00:00:01" - EXAMPLE_IP_ADDR = "10.0.0.1" - EXAMPLE_IPv6_ADDR = "0064:ff9b:0:0:0:0:9810:6414" - - PROFILE = { - 'description': 'Traffic profile to run RFC2544 latency', - 'name': 'rfc2544', - 'traffic_profile': {'traffic_type': 'RFC2544Profile', - 'frame_rate': 100}, - TrafficProfile.DOWNLINK: { - 'ipv4': {'outer_l2': {'framesize': {'64B': '100', - '1518B': '0', - '128B': '0', - '1400B': '0', - '256B': '0', - '373b': '0', - '570B': '0'}, - "srcmac": "00:00:00:00:00:02", - "dstmac": "00:00:00:00:00:01"}, - 'outer_l3v4': {'dstip4': '1.1.1.1-1.1.2.2', - 'proto': 'udp', - 'srcip4': '9.9.1.1-90.1.2.2', - 'dscp': 0, 'ttl': 32, - 'count': 1}, - 'outer_l4': {'srcport': '2001', - 'dsrport': '1234', - 'count': 1}}}, - TrafficProfile.UPLINK: { - 'ipv4': - {'outer_l2': {'framesize': - {'64B': '100', '1518B': '0', - '128B': '0', '1400B': '0', - '256B': '0', '373b': '0', - '570B': '0'}, - "srcmac": "00:00:00:00:00:01", - "dstmac": "00:00:00:00:00:02"}, - 'outer_l3v4': {'dstip4': '9.9.1.1-90.105.255.255', - 'proto': 'udp', - 'srcip4': '1.1.1.1-1.15.255.255', - 'dscp': 0, 'ttl': 32, 'count': 1}, - 'outer_l4': {'dstport': '2001', - 'srcport': '1234', - 'count': 1}}}, - 'schema': 'isb:traffic_profile:0.1'} - PROFILE_v6 = { - 'description': 'Traffic profile to run RFC2544 latency', - 'name': 'rfc2544', - 'traffic_profile': {'traffic_type': 'RFC2544Profile', - 'frame_rate': 100}, - TrafficProfile.DOWNLINK: { - 'ipv6': {'outer_l2': {'framesize': - {'64B': '100', '1518B': '0', - '128B': '0', '1400B': '0', - '256B': '0', '373b': '0', - '570B': '0'}, - "srcmac": "00:00:00:00:00:02", - "dstmac": "00:00:00:00:00:01"}, - 'outer_l3v4': { - 'dstip6': - '0064:ff9b:0:0:0:0:9810:6414-0064:ff9b:0:0:0:0:9810:6420', - 'proto': 'udp', - 'srcip6': - '0064:ff9b:0:0:0:0:9810:2814-0064:ff9b:0:0:0:0:9810:2820', - 'dscp': 0, 'ttl': 32, - 'count': 1}, - 'outer_l4': {'srcport': '2001', - 'dsrport': '1234', - 'count': 1}}}, - TrafficProfile.UPLINK: { - 'ipv6': {'outer_l2': {'framesize': - {'64B': '100', '1518B': '0', - '128B': '0', '1400B': '0', - '256B': '0', '373b': '0', - '570B': '0'}, - "srcmac": "00:00:00:00:00:01", - "dstmac": "00:00:00:00:00:02"}, - 'outer_l3v4': { - 'dstip6': - '0064:ff9b:0:0:0:0:9810:2814-0064:ff9b:0:0:0:0:9810:2820', - 'proto': 'udp', - 'srcip6': - '0064:ff9b:0:0:0:0:9810:6414-0064:ff9b:0:0:0:0:9810:6420', - 'dscp': 0, 'ttl': 32, - 'count': 1}, - 'outer_l4': {'dstport': '2001', - 'srcport': '1234', - 'count': 1}}}, - 'schema': 'isb:traffic_profile:0.1'} - - def test___init__(self): - TrafficProfile.params = self.PROFILE - trex_profile = \ - TrexProfile(TrafficProfile) - self.assertEqual(trex_profile.pps, 100) - - def test_qinq(self): - qinq = {"S-VLAN": {"id": 128, "priority": 0, "cfi": 0}, - "C-VLAN": {"id": 512, "priority": 0, "cfi": 0}} - - trex_profile = \ - TrexProfile(TrafficProfile) - self.assertEqual(None, trex_profile.set_qinq(qinq)) - - qinq = {"S-VLAN": {"id": "128-130", "priority": 0, "cfi": 0}, - "C-VLAN": {"id": "512-515", "priority": 0, "cfi": 0}} - self.assertEqual(None, trex_profile.set_qinq(qinq)) - - def test__set_outer_l2_fields(self): - trex_profile = \ - TrexProfile(TrafficProfile) - qinq = {"S-VLAN": {"id": 128, "priority": 0, "cfi": 0}, - "C-VLAN": {"id": 512, "priority": 0, "cfi": 0}} - outer_l2 = self.PROFILE[TrafficProfile.UPLINK]['ipv4']['outer_l2'] - outer_l2['QinQ'] = qinq - self.assertEqual(None, trex_profile._set_outer_l2_fields(outer_l2)) - - def test__set_outer_l3v4_fields(self): - trex_profile = \ - TrexProfile(TrafficProfile) - outer_l3v4 = self.PROFILE[TrafficProfile.UPLINK]['ipv4']['outer_l3v4'] - outer_l3v4['proto'] = 'tcp' - self.assertEqual(None, trex_profile._set_outer_l3v4_fields(outer_l3v4)) - - def test__set_outer_l3v6_fields(self): - trex_profile = \ - TrexProfile(TrafficProfile) - outer_l3v6 = self.PROFILE_v6[TrafficProfile.UPLINK]['ipv6']['outer_l3v4'] - outer_l3v6['proto'] = 'tcp' - outer_l3v6['tc'] = 1 - outer_l3v6['hlim'] = 10 - self.assertEqual(None, trex_profile._set_outer_l3v6_fields(outer_l3v6)) - - def test__set_outer_l4_fields(self): - trex_profile = \ - TrexProfile(TrafficProfile) - outer_l4 = self.PROFILE[TrafficProfile.UPLINK]['ipv4']['outer_l4'] - self.assertEqual(None, trex_profile._set_outer_l4_fields(outer_l4)) - - def test_get_streams(self): - trex_profile = \ - TrexProfile(TrafficProfile) - trex_profile.params = self.PROFILE - profile_data = self.PROFILE[TrafficProfile.UPLINK] - self.assertIsNotNone(trex_profile.get_streams(profile_data)) - trex_profile.pg_id = 1 - self.assertIsNotNone(trex_profile.get_streams(profile_data)) - trex_profile.params = self.PROFILE_v6 - trex_profile.profile_data = self.PROFILE_v6[TrafficProfile.UPLINK] - self.assertIsNotNone(trex_profile.get_streams(profile_data)) - trex_profile.pg_id = 1 - self.assertIsNotNone(trex_profile.get_streams(profile_data)) - - def test_generate_packets(self): - trex_profile = \ - TrexProfile(TrafficProfile) - trex_profile.fsize = 10 - trex_profile.base_pkt = [10] - self.assertIsNone(trex_profile.generate_packets()) - - def test_generate_imix_data_error(self): - trex_profile = \ - TrexProfile(TrafficProfile) - self.assertEqual({}, trex_profile.generate_imix_data(False)) - - def test__count_ip_ipv4(self): - start, end, count = TrexProfile._count_ip('1.1.1.1', '1.2.3.4') - self.assertEqual('1.1.1.1', str(start)) - self.assertEqual('1.2.3.4', str(end)) - diff = (int(ipaddress.IPv4Address(six.u('1.2.3.4'))) - - int(ipaddress.IPv4Address(six.u('1.1.1.1')))) - self.assertEqual(diff, count) - - def test__count_ip_ipv6(self): - start_ip = '0064:ff9b:0:0:0:0:9810:6414' - end_ip = '0064:ff9b:0:0:0:0:9810:6420' - start, end, count = TrexProfile._count_ip(start_ip, end_ip) - self.assertEqual(0x98106414, start) - self.assertEqual(0x98106420, end) - self.assertEqual(0x98106420 - 0x98106414, count) - - def test__count_ip_ipv6_exception(self): - start_ip = '0064:ff9b:0:0:0:0:9810:6420' - end_ip = '0064:ff9b:0:0:0:0:9810:6414' - with self.assertRaises(y_exc.IPv6RangeError): - TrexProfile._count_ip(start_ip, end_ip) - - def test__dscp_range_action_partial_actual_count_zero(self): - traffic_profile = TrexProfile(TrafficProfile) - dscp_partial = traffic_profile._dscp_range_action_partial() - - flow_vars_initial_length = len(traffic_profile.vm_flow_vars) - dscp_partial('1', '1', 'unneeded') - self.assertEqual(len(traffic_profile.vm_flow_vars), flow_vars_initial_length + 2) - - def test__dscp_range_action_partial_count_greater_than_actual(self): - traffic_profile = TrexProfile(TrafficProfile) - dscp_partial = traffic_profile._dscp_range_action_partial() - - flow_vars_initial_length = len(traffic_profile.vm_flow_vars) - dscp_partial('1', '10', '100') - self.assertEqual(len(traffic_profile.vm_flow_vars), flow_vars_initial_length + 2) - - def test__udp_range_action_partial_actual_count_zero(self): - traffic_profile = TrexProfile(TrafficProfile) - traffic_profile.udp['field1'] = 'value1' - udp_partial = traffic_profile._udp_range_action_partial('field1') - - flow_vars_initial_length = len(traffic_profile.vm_flow_vars) - udp_partial('1', '1', 'unneeded') - self.assertEqual(len(traffic_profile.vm_flow_vars), flow_vars_initial_length + 2) - - def test__udp_range_action_partial_count_greater_than_actual(self): - traffic_profile = TrexProfile(TrafficProfile) - traffic_profile.udp['field1'] = 'value1' - udp_partial = traffic_profile._udp_range_action_partial('field1', 'not_used_count') - - flow_vars_initial_length = len(traffic_profile.vm_flow_vars) - udp_partial('1', '10', '100') - self.assertEqual(len(traffic_profile.vm_flow_vars), flow_vars_initial_length + 2) - - def test__general_single_action_partial(self): - trex_profile = TrexProfile(TrafficProfile) - - trex_profile._general_single_action_partial(ETHERNET)(SRC)( - self.EXAMPLE_ETHERNET_ADDR) - self.assertEqual(self.EXAMPLE_ETHERNET_ADDR, - trex_profile.ether_packet.src) - - trex_profile._general_single_action_partial(IP)(DST)( - self.EXAMPLE_IP_ADDR) - self.assertEqual(self.EXAMPLE_IP_ADDR, trex_profile.ip_packet.dst) - - trex_profile._general_single_action_partial(IPv6)(DST)( - self.EXAMPLE_IPv6_ADDR) - self.assertEqual(self.EXAMPLE_IPv6_ADDR, trex_profile.ip6_packet.dst) - - trex_profile._general_single_action_partial(UDP)(SRC_PORT)(5060) - self.assertEqual(5060, trex_profile.udp_packet.sport) - - trex_profile._general_single_action_partial(IP)(TYPE_OF_SERVICE)(0) - self.assertEqual(0, trex_profile.ip_packet.tos) - - def test__set_proto_addr(self): - trex_profile = TrexProfile(TrafficProfile) - - ether_range = "00:00:00:00:00:01-00:00:00:00:00:02" - ip_range = "1.1.1.2-1.1.1.10" - ipv6_range = '0064:ff9b:0:0:0:0:9810:6414-0064:ff9b:0:0:0:0:9810:6420' - - trex_profile._set_proto_addr(ETHERNET, SRC, ether_range) - trex_profile._set_proto_addr(ETHERNET, DST, ether_range) - trex_profile._set_proto_addr(IP, SRC, ip_range) - trex_profile._set_proto_addr(IP, DST, ip_range) - trex_profile._set_proto_addr(IPv6, SRC, ipv6_range) - trex_profile._set_proto_addr(IPv6, DST, ipv6_range) - trex_profile._set_proto_addr(UDP, SRC_PORT, "5060-5090") - trex_profile._set_proto_addr(UDP, DST_PORT, "5060") diff --git a/tests/unit/network_services/traffic_profile/test_trex_traffic_profile.py b/tests/unit/network_services/traffic_profile/test_trex_traffic_profile.py new file mode 100644 index 000000000..c34b97531 --- /dev/null +++ b/tests/unit/network_services/traffic_profile/test_trex_traffic_profile.py @@ -0,0 +1,311 @@ +#!/usr/bin/env python + +# Copyright (c) 2016-2017 Intel 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 ipaddress + +import mock +import six +import unittest + +from tests.unit import STL_MOCKS +from yardstick.common import exceptions as y_exc + +STLClient = mock.MagicMock() +stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) +stl_patch.start() + +if stl_patch: + from yardstick.network_services.traffic_profile.base import TrafficProfile + from yardstick.network_services.traffic_profile.trex_traffic_profile import TrexProfile + from yardstick.network_services.traffic_profile.trex_traffic_profile import SRC + from yardstick.network_services.traffic_profile.trex_traffic_profile import DST + from yardstick.network_services.traffic_profile.trex_traffic_profile import ETHERNET + from yardstick.network_services.traffic_profile.trex_traffic_profile import IP + from yardstick.network_services.traffic_profile.trex_traffic_profile import IPv6 + from yardstick.network_services.traffic_profile.trex_traffic_profile import UDP + from yardstick.network_services.traffic_profile.trex_traffic_profile import SRC_PORT + from yardstick.network_services.traffic_profile.trex_traffic_profile import DST_PORT + from yardstick.network_services.traffic_profile.trex_traffic_profile import TYPE_OF_SERVICE + + +class TestTrexProfile(unittest.TestCase): + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64}} + + EXAMPLE_ETHERNET_ADDR = "00:00:00:00:00:01" + EXAMPLE_IP_ADDR = "10.0.0.1" + EXAMPLE_IPv6_ADDR = "0064:ff9b:0:0:0:0:9810:6414" + + PROFILE = { + 'description': 'Traffic profile to run RFC2544 latency', + 'name': 'rfc2544', + 'traffic_profile': {'traffic_type': 'RFC2544Profile', + 'frame_rate': 100}, + TrafficProfile.DOWNLINK: { + 'ipv4': {'outer_l2': {'framesize': {'64B': '100', + '1518B': '0', + '128B': '0', + '1400B': '0', + '256B': '0', + '373b': '0', + '570B': '0'}, + "srcmac": "00:00:00:00:00:02", + "dstmac": "00:00:00:00:00:01"}, + 'outer_l3v4': {'dstip4': '1.1.1.1-1.1.2.2', + 'proto': 'udp', + 'srcip4': '9.9.1.1-90.1.2.2', + 'dscp': 0, 'ttl': 32, + 'count': 1}, + 'outer_l4': {'srcport': '2001', + 'dsrport': '1234', + 'count': 1}}}, + TrafficProfile.UPLINK: { + 'ipv4': + {'outer_l2': {'framesize': + {'64B': '100', '1518B': '0', + '128B': '0', '1400B': '0', + '256B': '0', '373b': '0', + '570B': '0'}, + "srcmac": "00:00:00:00:00:01", + "dstmac": "00:00:00:00:00:02"}, + 'outer_l3v4': {'dstip4': '9.9.1.1-90.105.255.255', + 'proto': 'udp', + 'srcip4': '1.1.1.1-1.15.255.255', + 'dscp': 0, 'ttl': 32, 'count': 1}, + 'outer_l4': {'dstport': '2001', + 'srcport': '1234', + 'count': 1}}}, + 'schema': 'isb:traffic_profile:0.1'} + PROFILE_v6 = { + 'description': 'Traffic profile to run RFC2544 latency', + 'name': 'rfc2544', + 'traffic_profile': {'traffic_type': 'RFC2544Profile', + 'frame_rate': 100}, + TrafficProfile.DOWNLINK: { + 'ipv6': {'outer_l2': {'framesize': + {'64B': '100', '1518B': '0', + '128B': '0', '1400B': '0', + '256B': '0', '373b': '0', + '570B': '0'}, + "srcmac": "00:00:00:00:00:02", + "dstmac": "00:00:00:00:00:01"}, + 'outer_l3v4': { + 'dstip6': + '0064:ff9b:0:0:0:0:9810:6414-0064:ff9b:0:0:0:0:9810:6420', + 'proto': 'udp', + 'srcip6': + '0064:ff9b:0:0:0:0:9810:2814-0064:ff9b:0:0:0:0:9810:2820', + 'dscp': 0, 'ttl': 32, + 'count': 1}, + 'outer_l4': {'srcport': '2001', + 'dsrport': '1234', + 'count': 1}}}, + TrafficProfile.UPLINK: { + 'ipv6': {'outer_l2': {'framesize': + {'64B': '100', '1518B': '0', + '128B': '0', '1400B': '0', + '256B': '0', '373b': '0', + '570B': '0'}, + "srcmac": "00:00:00:00:00:01", + "dstmac": "00:00:00:00:00:02"}, + 'outer_l3v4': { + 'dstip6': + '0064:ff9b:0:0:0:0:9810:2814-0064:ff9b:0:0:0:0:9810:2820', + 'proto': 'udp', + 'srcip6': + '0064:ff9b:0:0:0:0:9810:6414-0064:ff9b:0:0:0:0:9810:6420', + 'dscp': 0, 'ttl': 32, + 'count': 1}, + 'outer_l4': {'dstport': '2001', + 'srcport': '1234', + 'count': 1}}}, + 'schema': 'isb:traffic_profile:0.1'} + + def test___init__(self): + TrafficProfile.params = self.PROFILE + trex_profile = \ + TrexProfile(TrafficProfile) + self.assertEqual(trex_profile.pps, 100) + + def test_qinq(self): + qinq = {"S-VLAN": {"id": 128, "priority": 0, "cfi": 0}, + "C-VLAN": {"id": 512, "priority": 0, "cfi": 0}} + + trex_profile = \ + TrexProfile(TrafficProfile) + self.assertEqual(None, trex_profile.set_qinq(qinq)) + + qinq = {"S-VLAN": {"id": "128-130", "priority": 0, "cfi": 0}, + "C-VLAN": {"id": "512-515", "priority": 0, "cfi": 0}} + self.assertEqual(None, trex_profile.set_qinq(qinq)) + + def test__set_outer_l2_fields(self): + trex_profile = \ + TrexProfile(TrafficProfile) + qinq = {"S-VLAN": {"id": 128, "priority": 0, "cfi": 0}, + "C-VLAN": {"id": 512, "priority": 0, "cfi": 0}} + outer_l2 = self.PROFILE[TrafficProfile.UPLINK]['ipv4']['outer_l2'] + outer_l2['QinQ'] = qinq + self.assertEqual(None, trex_profile._set_outer_l2_fields(outer_l2)) + + def test__set_outer_l3v4_fields(self): + trex_profile = \ + TrexProfile(TrafficProfile) + outer_l3v4 = self.PROFILE[TrafficProfile.UPLINK]['ipv4']['outer_l3v4'] + outer_l3v4['proto'] = 'tcp' + self.assertEqual(None, trex_profile._set_outer_l3v4_fields(outer_l3v4)) + + def test__set_outer_l3v6_fields(self): + trex_profile = \ + TrexProfile(TrafficProfile) + outer_l3v6 = self.PROFILE_v6[TrafficProfile.UPLINK]['ipv6']['outer_l3v4'] + outer_l3v6['proto'] = 'tcp' + outer_l3v6['tc'] = 1 + outer_l3v6['hlim'] = 10 + self.assertEqual(None, trex_profile._set_outer_l3v6_fields(outer_l3v6)) + + def test__set_outer_l4_fields(self): + trex_profile = \ + TrexProfile(TrafficProfile) + outer_l4 = self.PROFILE[TrafficProfile.UPLINK]['ipv4']['outer_l4'] + self.assertEqual(None, trex_profile._set_outer_l4_fields(outer_l4)) + + def test_get_streams(self): + trex_profile = \ + TrexProfile(TrafficProfile) + trex_profile.params = self.PROFILE + profile_data = self.PROFILE[TrafficProfile.UPLINK] + self.assertIsNotNone(trex_profile.get_streams(profile_data)) + trex_profile.pg_id = 1 + self.assertIsNotNone(trex_profile.get_streams(profile_data)) + trex_profile.params = self.PROFILE_v6 + trex_profile.profile_data = self.PROFILE_v6[TrafficProfile.UPLINK] + self.assertIsNotNone(trex_profile.get_streams(profile_data)) + trex_profile.pg_id = 1 + self.assertIsNotNone(trex_profile.get_streams(profile_data)) + + def test_generate_packets(self): + trex_profile = \ + TrexProfile(TrafficProfile) + trex_profile.fsize = 10 + trex_profile.base_pkt = [10] + self.assertIsNone(trex_profile.generate_packets()) + + def test_generate_imix_data_error(self): + trex_profile = \ + TrexProfile(TrafficProfile) + self.assertEqual({}, trex_profile.generate_imix_data(False)) + + def test__count_ip_ipv4(self): + start, end, count = TrexProfile._count_ip('1.1.1.1', '1.2.3.4') + self.assertEqual('1.1.1.1', str(start)) + self.assertEqual('1.2.3.4', str(end)) + diff = (int(ipaddress.IPv4Address(six.u('1.2.3.4'))) - + int(ipaddress.IPv4Address(six.u('1.1.1.1')))) + self.assertEqual(diff, count) + + def test__count_ip_ipv6(self): + start_ip = '0064:ff9b:0:0:0:0:9810:6414' + end_ip = '0064:ff9b:0:0:0:0:9810:6420' + start, end, count = TrexProfile._count_ip(start_ip, end_ip) + self.assertEqual(0x98106414, start) + self.assertEqual(0x98106420, end) + self.assertEqual(0x98106420 - 0x98106414, count) + + def test__count_ip_ipv6_exception(self): + start_ip = '0064:ff9b:0:0:0:0:9810:6420' + end_ip = '0064:ff9b:0:0:0:0:9810:6414' + with self.assertRaises(y_exc.IPv6RangeError): + TrexProfile._count_ip(start_ip, end_ip) + + def test__dscp_range_action_partial_actual_count_zero(self): + traffic_profile = TrexProfile(TrafficProfile) + dscp_partial = traffic_profile._dscp_range_action_partial() + + flow_vars_initial_length = len(traffic_profile.vm_flow_vars) + dscp_partial('1', '1', 'unneeded') + self.assertEqual(len(traffic_profile.vm_flow_vars), flow_vars_initial_length + 2) + + def test__dscp_range_action_partial_count_greater_than_actual(self): + traffic_profile = TrexProfile(TrafficProfile) + dscp_partial = traffic_profile._dscp_range_action_partial() + + flow_vars_initial_length = len(traffic_profile.vm_flow_vars) + dscp_partial('1', '10', '100') + self.assertEqual(len(traffic_profile.vm_flow_vars), flow_vars_initial_length + 2) + + def test__udp_range_action_partial_actual_count_zero(self): + traffic_profile = TrexProfile(TrafficProfile) + traffic_profile.udp['field1'] = 'value1' + udp_partial = traffic_profile._udp_range_action_partial('field1') + + flow_vars_initial_length = len(traffic_profile.vm_flow_vars) + udp_partial('1', '1', 'unneeded') + self.assertEqual(len(traffic_profile.vm_flow_vars), flow_vars_initial_length + 2) + + def test__udp_range_action_partial_count_greater_than_actual(self): + traffic_profile = TrexProfile(TrafficProfile) + traffic_profile.udp['field1'] = 'value1' + udp_partial = traffic_profile._udp_range_action_partial('field1', 'not_used_count') + + flow_vars_initial_length = len(traffic_profile.vm_flow_vars) + udp_partial('1', '10', '100') + self.assertEqual(len(traffic_profile.vm_flow_vars), flow_vars_initial_length + 2) + + def test__general_single_action_partial(self): + trex_profile = TrexProfile(TrafficProfile) + + trex_profile._general_single_action_partial(ETHERNET)(SRC)( + self.EXAMPLE_ETHERNET_ADDR) + self.assertEqual(self.EXAMPLE_ETHERNET_ADDR, + trex_profile.ether_packet.src) + + trex_profile._general_single_action_partial(IP)(DST)( + self.EXAMPLE_IP_ADDR) + self.assertEqual(self.EXAMPLE_IP_ADDR, trex_profile.ip_packet.dst) + + trex_profile._general_single_action_partial(IPv6)(DST)( + self.EXAMPLE_IPv6_ADDR) + self.assertEqual(self.EXAMPLE_IPv6_ADDR, trex_profile.ip6_packet.dst) + + trex_profile._general_single_action_partial(UDP)(SRC_PORT)(5060) + self.assertEqual(5060, trex_profile.udp_packet.sport) + + trex_profile._general_single_action_partial(IP)(TYPE_OF_SERVICE)(0) + self.assertEqual(0, trex_profile.ip_packet.tos) + + def test__set_proto_addr(self): + trex_profile = TrexProfile(TrafficProfile) + + ether_range = "00:00:00:00:00:01-00:00:00:00:00:02" + ip_range = "1.1.1.2-1.1.1.10" + ipv6_range = '0064:ff9b:0:0:0:0:9810:6414-0064:ff9b:0:0:0:0:9810:6420' + + trex_profile._set_proto_addr(ETHERNET, SRC, ether_range) + trex_profile._set_proto_addr(ETHERNET, DST, ether_range) + trex_profile._set_proto_addr(IP, SRC, ip_range) + trex_profile._set_proto_addr(IP, DST, ip_range) + trex_profile._set_proto_addr(IPv6, SRC, ipv6_range) + trex_profile._set_proto_addr(IPv6, DST, ipv6_range) + trex_profile._set_proto_addr(UDP, SRC_PORT, "5060-5090") + trex_profile._set_proto_addr(UDP, DST_PORT, "5060") diff --git a/yardstick/network_services/traffic_profile/ixia_rfc2544.py b/yardstick/network_services/traffic_profile/ixia_rfc2544.py index 3ab157dc7..7f047226b 100644 --- a/yardstick/network_services/traffic_profile/ixia_rfc2544.py +++ b/yardstick/network_services/traffic_profile/ixia_rfc2544.py @@ -15,7 +15,7 @@ from __future__ import absolute_import import logging -from yardstick.network_services.traffic_profile.traffic_profile import \ +from yardstick.network_services.traffic_profile.trex_traffic_profile import \ TrexProfile LOG = logging.getLogger(__name__) diff --git a/yardstick/network_services/traffic_profile/rfc2544.py b/yardstick/network_services/traffic_profile/rfc2544.py index b1ca8a345..83020c85c 100644 --- a/yardstick/network_services/traffic_profile/rfc2544.py +++ b/yardstick/network_services/traffic_profile/rfc2544.py @@ -21,7 +21,7 @@ from trex_stl_lib.trex_stl_client import STLStream from trex_stl_lib.trex_stl_streams import STLFlowLatencyStats from trex_stl_lib.trex_stl_streams import STLTXCont -from yardstick.network_services.traffic_profile.traffic_profile \ +from yardstick.network_services.traffic_profile.trex_traffic_profile \ import TrexProfile LOGGING = logging.getLogger(__name__) diff --git a/yardstick/network_services/traffic_profile/traffic_profile.py b/yardstick/network_services/traffic_profile/traffic_profile.py deleted file mode 100644 index 8cde5e4a7..000000000 --- a/yardstick/network_services/traffic_profile/traffic_profile.py +++ /dev/null @@ -1,471 +0,0 @@ -# Copyright (c) 2016-2017 Intel 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 struct -import socket -import logging -from random import SystemRandom -import ipaddress - -import six - -from yardstick.common import exceptions as y_exc -from yardstick.network_services.traffic_profile.base import TrafficProfile -from trex_stl_lib.trex_stl_client import STLStream -from trex_stl_lib.trex_stl_streams import STLFlowLatencyStats -from trex_stl_lib.trex_stl_streams import STLTXCont -from trex_stl_lib.trex_stl_streams import STLProfile -from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmWrFlowVar -from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFlowVarRepeatableRandom -from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFlowVar -from trex_stl_lib.trex_stl_packet_builder_scapy import STLPktBuilder -from trex_stl_lib.trex_stl_packet_builder_scapy import STLScVmRaw -from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFixIpv4 -from trex_stl_lib import api as Pkt - -SRC = 'src' -DST = 'dst' -ETHERNET = 'Ethernet' -IP = 'IP' -IPv6 = 'IPv6' -UDP = 'UDP' -DSCP = 'DSCP' -SRC_PORT = 'sport' -DST_PORT = 'dport' -TYPE_OF_SERVICE = 'tos' - -LOG = logging.getLogger(__name__) - - -class TrexProfile(TrafficProfile): - """ This class handles Trex Traffic profile generation and execution """ - - PROTO_MAP = { - ETHERNET: ('ether_packet', Pkt.Ether), - IP: ('ip_packet', Pkt.IP), - IPv6: ('ip6_packet', Pkt.IPv6), - UDP: ('udp_packet', Pkt.UDP), - } - - def _general_single_action_partial(self, protocol): - def f(field): - def partial(value): - kwargs = { - field: value - } - self._set_proto_fields(protocol, **kwargs) - return partial - return f - - def _ethernet_range_action_partial(self, direction, _): - def partial(min_value, max_value, count): - # pylint: disable=unused-argument - stl_vm_flow_var = STLVmFlowVar(name="mac_{}".format(direction), - min_value=1, - max_value=30, - size=4, - op='inc', - step=1) - self.vm_flow_vars.append(stl_vm_flow_var) - stl_vm_wr_flow_var = STLVmWrFlowVar( - fv_name='mac_{}'.format(direction), - pkt_offset='Ether.{}'.format(direction)) - self.vm_flow_vars.append(stl_vm_wr_flow_var) - return partial - - def _ip_range_action_partial(self, direction, count=1): - # pylint: disable=unused-argument - def partial(min_value, max_value, count): - _, _, actual_count = self._count_ip(min_value, max_value) - if not actual_count: - count = 1 - elif actual_count < int(count): - count = actual_count - - stl_vm_flow_var = STLVmFlowVarRepeatableRandom( - name="ip4_{}".format(direction), - min_value=min_value, - max_value=max_value, - size=4, - limit=int(count), - seed=0x1235) - self.vm_flow_vars.append(stl_vm_flow_var) - stl_vm_wr_flow_var = STLVmWrFlowVar( - fv_name='ip4_{}'.format(direction), - pkt_offset='IP.{}'.format(direction)) - self.vm_flow_vars.append(stl_vm_wr_flow_var) - stl_vm_fix_ipv4 = STLVmFixIpv4(offset="IP") - self.vm_flow_vars.append(stl_vm_fix_ipv4) - return partial - - def _ip6_range_action_partial(self, direction, _): - def partial(min_value, max_value, count): - # pylint: disable=unused-argument - min_value, max_value, _ = self._count_ip(min_value, max_value) - stl_vm_flow_var = STLVmFlowVar(name="ip6_{}".format(direction), - min_value=min_value, - max_value=max_value, - size=8, - op='random', - step=1) - self.vm_flow_vars.append(stl_vm_flow_var) - stl_vm_wr_flow_var = STLVmWrFlowVar( - fv_name='ip6_{}'.format(direction), - pkt_offset='IPv6.{}'.format(direction), - offset_fixup=8) - self.vm_flow_vars.append(stl_vm_wr_flow_var) - return partial - - def _dscp_range_action_partial(self, *_): - def partial(min_value, max_value, count): - # pylint: disable=unused-argument - stl_vm_flow_var = STLVmFlowVar(name="dscp", - min_value=min_value, - max_value=max_value, - size=2, - op='inc', - step=8) - self.vm_flow_vars.append(stl_vm_flow_var) - stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='dscp', - pkt_offset='IP.tos') - self.vm_flow_vars.append(stl_vm_wr_flow_var) - return partial - - def _udp_range_action_partial(self, field, count=1): - # pylint: disable=unused-argument - def partial(min_value, max_value, count): - actual_count = int(max_value) - int(min_value) - if not actual_count: - count = 1 - elif int(count) > actual_count: - count = actual_count - - stl_vm_flow_var = STLVmFlowVarRepeatableRandom( - name="port_{}".format(field), - min_value=min_value, - max_value=max_value, - size=2, - limit=int(count), - seed=0x1235) - self.vm_flow_vars.append(stl_vm_flow_var) - stl_vm_wr_flow_var = STLVmWrFlowVar( - fv_name='port_{}'.format(field), - pkt_offset=self.udp[field]) - self.vm_flow_vars.append(stl_vm_wr_flow_var) - return partial - - def __init__(self, yaml_data): - super(TrexProfile, self).__init__(yaml_data) - self.flows = 100 - self.pps = 100 - self.pg_id = 0 - self.first_run = True - self.streams = 1 - self.profile_data = [] - self.profile = None - self.base_pkt = None - self.fsize = None - self.trex_vm = None - self.vms = [] - self.rate = None - self.ether_packet = None - self.ip_packet = None - self.ip6_packet = None - self.udp_packet = None - self.udp = { - SRC_PORT: '', - DST_PORT: '', - } - self.qinq_packet = None - self.qinq = False - self.vm_flow_vars = [] - self.packets = [] - - self._map_proto_actions = { - # the tuple is (single value function, range value function, if the values should be - # converted to integer). - ETHERNET: (self._general_single_action_partial(ETHERNET), - self._ethernet_range_action_partial, - False, - ), - IP: (self._general_single_action_partial(IP), - self._ip_range_action_partial, - False, - ), - IPv6: (self._general_single_action_partial(IPv6), - self._ip6_range_action_partial, - False, - ), - DSCP: (self._general_single_action_partial(IP), - self._dscp_range_action_partial, - True, - ), - UDP: (self._general_single_action_partial(UDP), - self._udp_range_action_partial, - True, - ), - } - - def execute_traffic(self, traffic_generator): - """ Generate the stream and run traffic on the given ports """ - raise NotImplementedError() - - def _call_on_range(self, range, single_action, range_action, count=1, to_int=False): - def convert_to_int(val): - return int(val) if to_int else val - - range_iter = iter(str(range).split('-')) - min_value = convert_to_int(next(range_iter)) - try: - max_value = convert_to_int(next(range_iter)) - except StopIteration: - single_action(min_value) - else: - range_action(min_value=min_value, max_value=max_value, count=count) - - def _set_proto_addr(self, protocol, field, address, count=1): - single_action, range_action, to_int = self._map_proto_actions[protocol] - self._call_on_range(address, - single_action(field), - range_action(field, count), - count=count, - to_int=to_int, - ) - - def _set_proto_fields(self, protocol, **kwargs): - _attr_name, _class = self.PROTO_MAP[protocol] - - if not getattr(self, _attr_name): - setattr(self, _attr_name, _class()) - - _attr = getattr(self, _attr_name) - for key, value in six.iteritems(kwargs): - setattr(_attr, key, value) - - def set_svlan_cvlan(self, svlan, cvlan): - """ set svlan & cvlan """ - self.qinq = True - ether_params = {'type': 0x8100} - self._set_proto_fields(ETHERNET, **ether_params) - svlans = str(svlan['id']).split('-') - svlan_min = int(svlans[0]) - svlan_max = int(svlans[1]) if len(svlans) == 2 else int(svlans[0]) - if len(svlans) == 2: - svlan = self._get_random_value(svlan_min, svlan_max) - else: - svlan = svlan_min - cvlans = str(cvlan['id']).split('-') - cvlan_min = int(cvlans[0]) - cvlan_max = int(cvlans[1]) if len(cvlans) == 2 else int(cvlans[0]) - if len(cvlans) == 2: - cvlan = self._get_random_value(cvlan_min, cvlan_max) - else: - cvlan = cvlan_min - self.qinq_packet = Pkt.Dot1Q(vlan=svlan) / Pkt.Dot1Q(vlan=cvlan) - - def set_qinq(self, qinq): - """ set qinq in packet """ - self.set_svlan_cvlan(qinq['S-VLAN'], qinq['C-VLAN']) - - def _set_outer_l2_fields(self, outer_l2): - """ setup outer l2 fields from traffic profile """ - ether_params = {'type': 0x800} - self._set_proto_fields(ETHERNET, **ether_params) - if 'srcmac' in outer_l2: - self._set_proto_addr(ETHERNET, SRC, outer_l2['srcmac']) - if 'dstmac' in outer_l2: - self._set_proto_addr(ETHERNET, DST, outer_l2['dstmac']) - if 'QinQ' in outer_l2: - self.set_qinq(outer_l2['QinQ']) - - 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'] = socket.getprotobyname(outer_l3v4['proto']) - if outer_l3v4['proto'] == 'tcp': - self.udp_packet = Pkt.TCP() - self.udp[DST_PORT] = 'TCP.dport' - self.udp[SRC_PORT] = 'TCP.sport' - tcp_params = {'flags': '', 'window': 0} - self._set_proto_fields(UDP, **tcp_params) - if 'ttl' in outer_l3v4: - ip_params['ttl'] = outer_l3v4['ttl'] - self._set_proto_fields(IP, **ip_params) - if 'dscp' in outer_l3v4: - self._set_proto_addr(DSCP, TYPE_OF_SERVICE, outer_l3v4['dscp']) - if 'srcip4' in outer_l3v4: - self._set_proto_addr(IP, SRC, outer_l3v4['srcip4'], outer_l3v4['count']) - if 'dstip4' in outer_l3v4: - self._set_proto_addr(IP, DST, outer_l3v4['dstip4'], outer_l3v4['count']) - - def _set_outer_l3v6_fields(self, outer_l3v6): - """ setup outer l3v6 fields from traffic profile """ - ether_params = {'type': 0x86dd} - self._set_proto_fields(ETHERNET, **ether_params) - ip6_params = {} - if 'proto' in outer_l3v6: - ip6_params['proto'] = outer_l3v6['proto'] - if outer_l3v6['proto'] == 'tcp': - self.udp_packet = Pkt.TCP() - self.udp[DST_PORT] = 'TCP.dport' - self.udp[SRC_PORT] = 'TCP.sport' - tcp_params = {'flags': '', 'window': 0} - self._set_proto_fields(UDP, **tcp_params) - if 'ttl' in outer_l3v6: - ip6_params['ttl'] = outer_l3v6['ttl'] - if 'tc' in outer_l3v6: - ip6_params['tc'] = outer_l3v6['tc'] - if 'hlim' in outer_l3v6: - ip6_params['hlim'] = outer_l3v6['hlim'] - self._set_proto_fields(IPv6, **ip6_params) - if 'srcip6' in outer_l3v6: - self._set_proto_addr(IPv6, SRC, outer_l3v6['srcip6']) - if 'dstip6' in outer_l3v6: - self._set_proto_addr(IPv6, DST, outer_l3v6['dstip6']) - - def _set_outer_l4_fields(self, outer_l4): - """ setup outer l4 fields from traffic profile """ - if 'srcport' in outer_l4: - self._set_proto_addr(UDP, SRC_PORT, outer_l4['srcport'], outer_l4['count']) - if 'dstport' in outer_l4: - self._set_proto_addr(UDP, DST_PORT, outer_l4['dstport'], outer_l4['count']) - - def generate_imix_data(self, packet_definition): - """ generate packet size for a given traffic profile """ - imix_count = {} - imix_data = {} - if not packet_definition: - return imix_count - imix = packet_definition.get('framesize') - if imix: - for size in imix: - data = imix[size] - imix_data[int(size[:-1])] = int(data) - imix_sum = sum(imix_data.values()) - if imix_sum > 100: - raise SystemExit("Error in IMIX data") - elif imix_sum < 100: - imix_data[64] = imix_data.get(64, 0) + (100 - imix_sum) - - avg_size = 0.0 - for size in imix_data: - count = int(imix_data[size]) - if count: - avg_size += round(size * count / 100, 2) - pps = round(self.pps * count / 100, 0) - imix_count[size] = pps - self.rate = round(1342177280 / avg_size, 0) * 2 - logging.debug("Imax: %s rate: %s", imix_count, self.rate) - return imix_count - - def get_streams(self, profile_data): - """ generate trex stream - :param profile_data: - :type profile_data: - """ - self.streams = [] - self.pps = self.params['traffic_profile'].get('frame_rate', 100) - for packet_name in profile_data: - outer_l2 = profile_data[packet_name].get('outer_l2') - imix_data = self.generate_imix_data(outer_l2) - if not imix_data: - imix_data = {64: self.pps} - self.generate_vm(profile_data[packet_name]) - for size in imix_data: - self._generate_streams(size, imix_data[size]) - self._generate_profile() - return self.profile - - def generate_vm(self, packet_definition): - """ generate trex vm with flows setup """ - self.ether_packet = Pkt.Ether() - self.ip_packet = Pkt.IP() - self.ip6_packet = None - self.udp_packet = Pkt.UDP() - self.udp[DST_PORT] = 'UDP.dport' - self.udp[SRC_PORT] = 'UDP.sport' - self.qinq = False - self.vm_flow_vars = [] - outer_l2 = packet_definition.get('outer_l2', None) - outer_l3v4 = packet_definition.get('outer_l3v4', None) - outer_l3v6 = packet_definition.get('outer_l3v6', None) - outer_l4 = packet_definition.get('outer_l4', None) - if outer_l2: - self._set_outer_l2_fields(outer_l2) - if outer_l3v4: - self._set_outer_l3v4_fields(outer_l3v4) - if outer_l3v6: - self._set_outer_l3v6_fields(outer_l3v6) - if outer_l4: - self._set_outer_l4_fields(outer_l4) - self.trex_vm = STLScVmRaw(self.vm_flow_vars) - - def generate_packets(self): - """ generate packets from trex TG """ - base_pkt = self.base_pkt - size = self.fsize - 4 - pad = max(0, size - len(base_pkt)) * 'x' - self.packets = [STLPktBuilder(pkt=base_pkt / pad, - vm=vm) for vm in self.vms] - - def _create_single_packet(self, size=64): - size = size - 4 - ether_packet = self.ether_packet - ip_packet = self.ip6_packet if self.ip6_packet else self.ip_packet - udp_packet = self.udp_packet - if self.qinq: - qinq_packet = self.qinq_packet - base_pkt = ether_packet / qinq_packet / ip_packet / udp_packet - else: - base_pkt = ether_packet / ip_packet / udp_packet - pad = max(0, size - len(base_pkt)) * 'x' - packet = STLPktBuilder(pkt=base_pkt / pad, vm=self.trex_vm) - return packet - - def _create_single_stream(self, packet_size, pps, isg=0): - packet = self._create_single_packet(packet_size) - if self.pg_id: - self.pg_id += 1 - stl_flow = STLFlowLatencyStats(pg_id=self.pg_id) - stream = STLStream(isg=isg, packet=packet, mode=STLTXCont(pps=pps), - flow_stats=stl_flow) - else: - stream = STLStream(isg=isg, packet=packet, mode=STLTXCont(pps=pps)) - return stream - - def _generate_streams(self, packet_size, pps): - self.streams.append(self._create_single_stream(packet_size, pps)) - - def _generate_profile(self): - self.profile = STLProfile(self.streams) - - @classmethod - def _count_ip(cls, start_ip, end_ip): - start = ipaddress.ip_address(six.u(start_ip)) - end = ipaddress.ip_address(six.u(end_ip)) - if start.version == 4: - return start, end, int(end) - int(start) - elif start.version == 6: - if int(start) > int(end): - raise y_exc.IPv6RangeError(start_ip=str(start), - end_ip=str(end)) - _, lo1 = struct.unpack('!QQ', start.packed) - _, lo2 = struct.unpack('!QQ', end.packed) - return lo1, lo2, lo2 - lo1 - - @classmethod - def _get_random_value(cls, min_port, max_port): - cryptogen = SystemRandom() - return cryptogen.randrange(min_port, max_port) diff --git a/yardstick/network_services/traffic_profile/trex_traffic_profile.py b/yardstick/network_services/traffic_profile/trex_traffic_profile.py new file mode 100644 index 000000000..8cde5e4a7 --- /dev/null +++ b/yardstick/network_services/traffic_profile/trex_traffic_profile.py @@ -0,0 +1,471 @@ +# Copyright (c) 2016-2017 Intel 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 struct +import socket +import logging +from random import SystemRandom +import ipaddress + +import six + +from yardstick.common import exceptions as y_exc +from yardstick.network_services.traffic_profile.base import TrafficProfile +from trex_stl_lib.trex_stl_client import STLStream +from trex_stl_lib.trex_stl_streams import STLFlowLatencyStats +from trex_stl_lib.trex_stl_streams import STLTXCont +from trex_stl_lib.trex_stl_streams import STLProfile +from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmWrFlowVar +from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFlowVarRepeatableRandom +from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFlowVar +from trex_stl_lib.trex_stl_packet_builder_scapy import STLPktBuilder +from trex_stl_lib.trex_stl_packet_builder_scapy import STLScVmRaw +from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFixIpv4 +from trex_stl_lib import api as Pkt + +SRC = 'src' +DST = 'dst' +ETHERNET = 'Ethernet' +IP = 'IP' +IPv6 = 'IPv6' +UDP = 'UDP' +DSCP = 'DSCP' +SRC_PORT = 'sport' +DST_PORT = 'dport' +TYPE_OF_SERVICE = 'tos' + +LOG = logging.getLogger(__name__) + + +class TrexProfile(TrafficProfile): + """ This class handles Trex Traffic profile generation and execution """ + + PROTO_MAP = { + ETHERNET: ('ether_packet', Pkt.Ether), + IP: ('ip_packet', Pkt.IP), + IPv6: ('ip6_packet', Pkt.IPv6), + UDP: ('udp_packet', Pkt.UDP), + } + + def _general_single_action_partial(self, protocol): + def f(field): + def partial(value): + kwargs = { + field: value + } + self._set_proto_fields(protocol, **kwargs) + return partial + return f + + def _ethernet_range_action_partial(self, direction, _): + def partial(min_value, max_value, count): + # pylint: disable=unused-argument + stl_vm_flow_var = STLVmFlowVar(name="mac_{}".format(direction), + min_value=1, + max_value=30, + size=4, + op='inc', + step=1) + self.vm_flow_vars.append(stl_vm_flow_var) + stl_vm_wr_flow_var = STLVmWrFlowVar( + fv_name='mac_{}'.format(direction), + pkt_offset='Ether.{}'.format(direction)) + self.vm_flow_vars.append(stl_vm_wr_flow_var) + return partial + + def _ip_range_action_partial(self, direction, count=1): + # pylint: disable=unused-argument + def partial(min_value, max_value, count): + _, _, actual_count = self._count_ip(min_value, max_value) + if not actual_count: + count = 1 + elif actual_count < int(count): + count = actual_count + + stl_vm_flow_var = STLVmFlowVarRepeatableRandom( + name="ip4_{}".format(direction), + min_value=min_value, + max_value=max_value, + size=4, + limit=int(count), + seed=0x1235) + self.vm_flow_vars.append(stl_vm_flow_var) + stl_vm_wr_flow_var = STLVmWrFlowVar( + fv_name='ip4_{}'.format(direction), + pkt_offset='IP.{}'.format(direction)) + self.vm_flow_vars.append(stl_vm_wr_flow_var) + stl_vm_fix_ipv4 = STLVmFixIpv4(offset="IP") + self.vm_flow_vars.append(stl_vm_fix_ipv4) + return partial + + def _ip6_range_action_partial(self, direction, _): + def partial(min_value, max_value, count): + # pylint: disable=unused-argument + min_value, max_value, _ = self._count_ip(min_value, max_value) + stl_vm_flow_var = STLVmFlowVar(name="ip6_{}".format(direction), + min_value=min_value, + max_value=max_value, + size=8, + op='random', + step=1) + self.vm_flow_vars.append(stl_vm_flow_var) + stl_vm_wr_flow_var = STLVmWrFlowVar( + fv_name='ip6_{}'.format(direction), + pkt_offset='IPv6.{}'.format(direction), + offset_fixup=8) + self.vm_flow_vars.append(stl_vm_wr_flow_var) + return partial + + def _dscp_range_action_partial(self, *_): + def partial(min_value, max_value, count): + # pylint: disable=unused-argument + stl_vm_flow_var = STLVmFlowVar(name="dscp", + min_value=min_value, + max_value=max_value, + size=2, + op='inc', + step=8) + self.vm_flow_vars.append(stl_vm_flow_var) + stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='dscp', + pkt_offset='IP.tos') + self.vm_flow_vars.append(stl_vm_wr_flow_var) + return partial + + def _udp_range_action_partial(self, field, count=1): + # pylint: disable=unused-argument + def partial(min_value, max_value, count): + actual_count = int(max_value) - int(min_value) + if not actual_count: + count = 1 + elif int(count) > actual_count: + count = actual_count + + stl_vm_flow_var = STLVmFlowVarRepeatableRandom( + name="port_{}".format(field), + min_value=min_value, + max_value=max_value, + size=2, + limit=int(count), + seed=0x1235) + self.vm_flow_vars.append(stl_vm_flow_var) + stl_vm_wr_flow_var = STLVmWrFlowVar( + fv_name='port_{}'.format(field), + pkt_offset=self.udp[field]) + self.vm_flow_vars.append(stl_vm_wr_flow_var) + return partial + + def __init__(self, yaml_data): + super(TrexProfile, self).__init__(yaml_data) + self.flows = 100 + self.pps = 100 + self.pg_id = 0 + self.first_run = True + self.streams = 1 + self.profile_data = [] + self.profile = None + self.base_pkt = None + self.fsize = None + self.trex_vm = None + self.vms = [] + self.rate = None + self.ether_packet = None + self.ip_packet = None + self.ip6_packet = None + self.udp_packet = None + self.udp = { + SRC_PORT: '', + DST_PORT: '', + } + self.qinq_packet = None + self.qinq = False + self.vm_flow_vars = [] + self.packets = [] + + self._map_proto_actions = { + # the tuple is (single value function, range value function, if the values should be + # converted to integer). + ETHERNET: (self._general_single_action_partial(ETHERNET), + self._ethernet_range_action_partial, + False, + ), + IP: (self._general_single_action_partial(IP), + self._ip_range_action_partial, + False, + ), + IPv6: (self._general_single_action_partial(IPv6), + self._ip6_range_action_partial, + False, + ), + DSCP: (self._general_single_action_partial(IP), + self._dscp_range_action_partial, + True, + ), + UDP: (self._general_single_action_partial(UDP), + self._udp_range_action_partial, + True, + ), + } + + def execute_traffic(self, traffic_generator): + """ Generate the stream and run traffic on the given ports """ + raise NotImplementedError() + + def _call_on_range(self, range, single_action, range_action, count=1, to_int=False): + def convert_to_int(val): + return int(val) if to_int else val + + range_iter = iter(str(range).split('-')) + min_value = convert_to_int(next(range_iter)) + try: + max_value = convert_to_int(next(range_iter)) + except StopIteration: + single_action(min_value) + else: + range_action(min_value=min_value, max_value=max_value, count=count) + + def _set_proto_addr(self, protocol, field, address, count=1): + single_action, range_action, to_int = self._map_proto_actions[protocol] + self._call_on_range(address, + single_action(field), + range_action(field, count), + count=count, + to_int=to_int, + ) + + def _set_proto_fields(self, protocol, **kwargs): + _attr_name, _class = self.PROTO_MAP[protocol] + + if not getattr(self, _attr_name): + setattr(self, _attr_name, _class()) + + _attr = getattr(self, _attr_name) + for key, value in six.iteritems(kwargs): + setattr(_attr, key, value) + + def set_svlan_cvlan(self, svlan, cvlan): + """ set svlan & cvlan """ + self.qinq = True + ether_params = {'type': 0x8100} + self._set_proto_fields(ETHERNET, **ether_params) + svlans = str(svlan['id']).split('-') + svlan_min = int(svlans[0]) + svlan_max = int(svlans[1]) if len(svlans) == 2 else int(svlans[0]) + if len(svlans) == 2: + svlan = self._get_random_value(svlan_min, svlan_max) + else: + svlan = svlan_min + cvlans = str(cvlan['id']).split('-') + cvlan_min = int(cvlans[0]) + cvlan_max = int(cvlans[1]) if len(cvlans) == 2 else int(cvlans[0]) + if len(cvlans) == 2: + cvlan = self._get_random_value(cvlan_min, cvlan_max) + else: + cvlan = cvlan_min + self.qinq_packet = Pkt.Dot1Q(vlan=svlan) / Pkt.Dot1Q(vlan=cvlan) + + def set_qinq(self, qinq): + """ set qinq in packet """ + self.set_svlan_cvlan(qinq['S-VLAN'], qinq['C-VLAN']) + + def _set_outer_l2_fields(self, outer_l2): + """ setup outer l2 fields from traffic profile """ + ether_params = {'type': 0x800} + self._set_proto_fields(ETHERNET, **ether_params) + if 'srcmac' in outer_l2: + self._set_proto_addr(ETHERNET, SRC, outer_l2['srcmac']) + if 'dstmac' in outer_l2: + self._set_proto_addr(ETHERNET, DST, outer_l2['dstmac']) + if 'QinQ' in outer_l2: + self.set_qinq(outer_l2['QinQ']) + + 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'] = socket.getprotobyname(outer_l3v4['proto']) + if outer_l3v4['proto'] == 'tcp': + self.udp_packet = Pkt.TCP() + self.udp[DST_PORT] = 'TCP.dport' + self.udp[SRC_PORT] = 'TCP.sport' + tcp_params = {'flags': '', 'window': 0} + self._set_proto_fields(UDP, **tcp_params) + if 'ttl' in outer_l3v4: + ip_params['ttl'] = outer_l3v4['ttl'] + self._set_proto_fields(IP, **ip_params) + if 'dscp' in outer_l3v4: + self._set_proto_addr(DSCP, TYPE_OF_SERVICE, outer_l3v4['dscp']) + if 'srcip4' in outer_l3v4: + self._set_proto_addr(IP, SRC, outer_l3v4['srcip4'], outer_l3v4['count']) + if 'dstip4' in outer_l3v4: + self._set_proto_addr(IP, DST, outer_l3v4['dstip4'], outer_l3v4['count']) + + def _set_outer_l3v6_fields(self, outer_l3v6): + """ setup outer l3v6 fields from traffic profile """ + ether_params = {'type': 0x86dd} + self._set_proto_fields(ETHERNET, **ether_params) + ip6_params = {} + if 'proto' in outer_l3v6: + ip6_params['proto'] = outer_l3v6['proto'] + if outer_l3v6['proto'] == 'tcp': + self.udp_packet = Pkt.TCP() + self.udp[DST_PORT] = 'TCP.dport' + self.udp[SRC_PORT] = 'TCP.sport' + tcp_params = {'flags': '', 'window': 0} + self._set_proto_fields(UDP, **tcp_params) + if 'ttl' in outer_l3v6: + ip6_params['ttl'] = outer_l3v6['ttl'] + if 'tc' in outer_l3v6: + ip6_params['tc'] = outer_l3v6['tc'] + if 'hlim' in outer_l3v6: + ip6_params['hlim'] = outer_l3v6['hlim'] + self._set_proto_fields(IPv6, **ip6_params) + if 'srcip6' in outer_l3v6: + self._set_proto_addr(IPv6, SRC, outer_l3v6['srcip6']) + if 'dstip6' in outer_l3v6: + self._set_proto_addr(IPv6, DST, outer_l3v6['dstip6']) + + def _set_outer_l4_fields(self, outer_l4): + """ setup outer l4 fields from traffic profile """ + if 'srcport' in outer_l4: + self._set_proto_addr(UDP, SRC_PORT, outer_l4['srcport'], outer_l4['count']) + if 'dstport' in outer_l4: + self._set_proto_addr(UDP, DST_PORT, outer_l4['dstport'], outer_l4['count']) + + def generate_imix_data(self, packet_definition): + """ generate packet size for a given traffic profile """ + imix_count = {} + imix_data = {} + if not packet_definition: + return imix_count + imix = packet_definition.get('framesize') + if imix: + for size in imix: + data = imix[size] + imix_data[int(size[:-1])] = int(data) + imix_sum = sum(imix_data.values()) + if imix_sum > 100: + raise SystemExit("Error in IMIX data") + elif imix_sum < 100: + imix_data[64] = imix_data.get(64, 0) + (100 - imix_sum) + + avg_size = 0.0 + for size in imix_data: + count = int(imix_data[size]) + if count: + avg_size += round(size * count / 100, 2) + pps = round(self.pps * count / 100, 0) + imix_count[size] = pps + self.rate = round(1342177280 / avg_size, 0) * 2 + logging.debug("Imax: %s rate: %s", imix_count, self.rate) + return imix_count + + def get_streams(self, profile_data): + """ generate trex stream + :param profile_data: + :type profile_data: + """ + self.streams = [] + self.pps = self.params['traffic_profile'].get('frame_rate', 100) + for packet_name in profile_data: + outer_l2 = profile_data[packet_name].get('outer_l2') + imix_data = self.generate_imix_data(outer_l2) + if not imix_data: + imix_data = {64: self.pps} + self.generate_vm(profile_data[packet_name]) + for size in imix_data: + self._generate_streams(size, imix_data[size]) + self._generate_profile() + return self.profile + + def generate_vm(self, packet_definition): + """ generate trex vm with flows setup """ + self.ether_packet = Pkt.Ether() + self.ip_packet = Pkt.IP() + self.ip6_packet = None + self.udp_packet = Pkt.UDP() + self.udp[DST_PORT] = 'UDP.dport' + self.udp[SRC_PORT] = 'UDP.sport' + self.qinq = False + self.vm_flow_vars = [] + outer_l2 = packet_definition.get('outer_l2', None) + outer_l3v4 = packet_definition.get('outer_l3v4', None) + outer_l3v6 = packet_definition.get('outer_l3v6', None) + outer_l4 = packet_definition.get('outer_l4', None) + if outer_l2: + self._set_outer_l2_fields(outer_l2) + if outer_l3v4: + self._set_outer_l3v4_fields(outer_l3v4) + if outer_l3v6: + self._set_outer_l3v6_fields(outer_l3v6) + if outer_l4: + self._set_outer_l4_fields(outer_l4) + self.trex_vm = STLScVmRaw(self.vm_flow_vars) + + def generate_packets(self): + """ generate packets from trex TG """ + base_pkt = self.base_pkt + size = self.fsize - 4 + pad = max(0, size - len(base_pkt)) * 'x' + self.packets = [STLPktBuilder(pkt=base_pkt / pad, + vm=vm) for vm in self.vms] + + def _create_single_packet(self, size=64): + size = size - 4 + ether_packet = self.ether_packet + ip_packet = self.ip6_packet if self.ip6_packet else self.ip_packet + udp_packet = self.udp_packet + if self.qinq: + qinq_packet = self.qinq_packet + base_pkt = ether_packet / qinq_packet / ip_packet / udp_packet + else: + base_pkt = ether_packet / ip_packet / udp_packet + pad = max(0, size - len(base_pkt)) * 'x' + packet = STLPktBuilder(pkt=base_pkt / pad, vm=self.trex_vm) + return packet + + def _create_single_stream(self, packet_size, pps, isg=0): + packet = self._create_single_packet(packet_size) + if self.pg_id: + self.pg_id += 1 + stl_flow = STLFlowLatencyStats(pg_id=self.pg_id) + stream = STLStream(isg=isg, packet=packet, mode=STLTXCont(pps=pps), + flow_stats=stl_flow) + else: + stream = STLStream(isg=isg, packet=packet, mode=STLTXCont(pps=pps)) + return stream + + def _generate_streams(self, packet_size, pps): + self.streams.append(self._create_single_stream(packet_size, pps)) + + def _generate_profile(self): + self.profile = STLProfile(self.streams) + + @classmethod + def _count_ip(cls, start_ip, end_ip): + start = ipaddress.ip_address(six.u(start_ip)) + end = ipaddress.ip_address(six.u(end_ip)) + if start.version == 4: + return start, end, int(end) - int(start) + elif start.version == 6: + if int(start) > int(end): + raise y_exc.IPv6RangeError(start_ip=str(start), + end_ip=str(end)) + _, lo1 = struct.unpack('!QQ', start.packed) + _, lo2 = struct.unpack('!QQ', end.packed) + return lo1, lo2, lo2 - lo1 + + @classmethod + def _get_random_value(cls, min_port, max_port): + cryptogen = SystemRandom() + return cryptogen.randrange(min_port, max_port) -- cgit