diff options
-rwxr-xr-x | nsb_setup.sh | 2 | ||||
-rw-r--r-- | samples/vnf_samples/traffic_profiles/prox_mpls_tag_untag.yaml | 2 | ||||
-rwxr-xr-x | tests/functional/test_cli_scenario.py | 20 | ||||
-rw-r--r-- | tests/unit/benchmark/scenarios/test_base.py | 53 | ||||
-rw-r--r-- | tests/unit/network_services/traffic_profile/test_prox_mpls.py | 95 | ||||
-rw-r--r-- | tests/unit/network_services/vnf_generic/vnf/test_tg_trex.py | 54 | ||||
-rw-r--r-- | yardstick/benchmark/core/scenario.py | 7 | ||||
-rw-r--r-- | yardstick/benchmark/scenarios/base.py | 14 | ||||
-rw-r--r-- | yardstick/network_services/traffic_profile/prox_mpls_tag_untag.py | 99 | ||||
-rw-r--r-- | yardstick/network_services/traffic_profile/rfc2544.py | 4 | ||||
-rw-r--r-- | yardstick/network_services/vnf_generic/vnf/sample_vnf.py | 4 | ||||
-rw-r--r-- | yardstick/network_services/vnf_generic/vnf/tg_trex.py | 32 |
12 files changed, 166 insertions, 220 deletions
diff --git a/nsb_setup.sh b/nsb_setup.sh index 28d31967f..40293fef9 100755 --- a/nsb_setup.sh +++ b/nsb_setup.sh @@ -28,7 +28,7 @@ if [ $# -eq 1 ]; then OPENRC=$(readlink -f -- "$1") extra_args="-e openrc_file=${OPENRC}" source "${OPENRC}" - CONTROLLER_IP=$(echo ${OS_AUTH_URL} | sed -ne "s/http:\/\/\(.*\):.*/\1/p") + CONTROLLER_IP=$(echo ${OS_AUTH_URL} | sed -ne "s#http://\([0-9a-zA-Z.\-]*\):*[0-9]*/.*#\1#p") export no_proxy="localhost,127.0.0.1,${CONTROLLER_IP},$no_proxy" fi diff --git a/samples/vnf_samples/traffic_profiles/prox_mpls_tag_untag.yaml b/samples/vnf_samples/traffic_profiles/prox_mpls_tag_untag.yaml index 9ac6e6ecf..b65fb0526 100644 --- a/samples/vnf_samples/traffic_profiles/prox_mpls_tag_untag.yaml +++ b/samples/vnf_samples/traffic_profiles/prox_mpls_tag_untag.yaml @@ -18,7 +18,7 @@ name: prox_mpls_tag_untag description: MPLS tag/untag for max no-drop throughput over given packet sizes traffic_profile: - traffic_type: ProxMplsTagUntagProfile + traffic_type: ProxBinSearchProfile tolerated_loss: 0.001 test_precision: 0.1 # packet_sizes: [64, 128, 256, 512, 1024, 1280, 1518] diff --git a/tests/functional/test_cli_scenario.py b/tests/functional/test_cli_scenario.py index 4741e8244..63b533b85 100755 --- a/tests/functional/test_cli_scenario.py +++ b/tests/functional/test_cli_scenario.py @@ -32,31 +32,25 @@ class ScenarioTestCase(unittest.TestCase): def test_scenario_show_Lmbench(self): res = self.yardstick("scenario show Lmbench") - lmbench = "Execute lmbench memory read latency" - "or memory bandwidth benchmark in a host" in res - self.assertTrue(lmbench) + self.assertIn("Execute lmbench memory read latency or memory " + "bandwidth benchmark in a hos", res) def test_scenario_show_Perf(self): res = self.yardstick("scenario show Perf") - perf = "Execute perf benchmark in a host" in res - self.assertTrue(perf) + self.assertIn("Execute perf benchmark in a host", res) def test_scenario_show_Fio(self): res = self.yardstick("scenario show Fio") - fio = "Execute fio benchmark in a host" in res - self.assertTrue(fio) + self.assertIn("Execute fio benchmark in a host", res) def test_scenario_show_Ping(self): res = self.yardstick("scenario show Ping") - ping = "Execute ping between two hosts" in res - self.assertTrue(ping) + self.assertIn("Execute ping between two hosts", res) def test_scenario_show_Iperf3(self): res = self.yardstick("scenario show Iperf3") - iperf3 = "Execute iperf3 between two hosts" in res - self.assertTrue(iperf3) + self.assertIn("Execute iperf3 between two hosts", res) def test_scenario_show_Pktgen(self): res = self.yardstick("scenario show Pktgen") - pktgen = "Execute pktgen between two hosts" in res - self.assertTrue(pktgen) + self.assertIn("Execute pktgen between two hosts", res) diff --git a/tests/unit/benchmark/scenarios/test_base.py b/tests/unit/benchmark/scenarios/test_base.py new file mode 100644 index 000000000..78e342978 --- /dev/null +++ b/tests/unit/benchmark/scenarios/test_base.py @@ -0,0 +1,53 @@ +# Copyright 2017: Intel Ltd. +# All Rights Reserved. +# +# 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 unittest + +from yardstick.benchmark.scenarios import base + + +class ScenarioTestCase(unittest.TestCase): + + def test_get_scenario_type(self): + scenario_type = 'dummy scenario' + + class DummyScenario(base.Scenario): + __scenario_type__ = scenario_type + + self.assertEqual(scenario_type, DummyScenario.get_scenario_type()) + + def test_get_scenario_type_not_defined(self): + class DummyScenario(base.Scenario): + pass + + self.assertEqual(str(None), DummyScenario.get_scenario_type()) + + def test_get_description(self): + docstring = """First line + Second line + Third line + """ + + class DummyScenario(base.Scenario): + __doc__ = docstring + + self.assertEqual(docstring.splitlines()[0], + DummyScenario.get_description()) + + def test_get_description_empty(self): + class DummyScenario(base.Scenario): + pass + + self.assertEqual(str(None), DummyScenario.get_description()) diff --git a/tests/unit/network_services/traffic_profile/test_prox_mpls.py b/tests/unit/network_services/traffic_profile/test_prox_mpls.py deleted file mode 100644 index 00a690d2a..000000000 --- a/tests/unit/network_services/traffic_profile/test_prox_mpls.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) 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. -# - -from __future__ import absolute_import - -import unittest -import mock - -from tests.unit import STL_MOCKS - -STLClient = mock.MagicMock() -stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) -stl_patch.start() - -if stl_patch: - from yardstick.network_services.vnf_generic.vnf.prox_helpers import ProxTestDataTuple - from yardstick.network_services.traffic_profile.prox_mpls_tag_untag import ProxMplsTagUntagProfile - - -class TestProxMplsTagUntagProfile(unittest.TestCase): - - def test_mpls_1(self): - def target(*args, **kwargs): - runs.append(args[2]) - if args[2] < 0 or args[2] > 100: - raise RuntimeError(' '.join([str(args), str(runs)])) - if args[2] > 75.0: - return fail_tuple, {} - return success_tuple, {} - - tp_config = { - 'traffic_profile': { - 'packet_sizes': [200], - }, - } - - runs = [] - success_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.1, 5.2, 5.3], 995, 1000, 123.4) - fail_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.6, 5.7, 5.8], 850, 1000, 123.4) - - traffic_generator = mock.MagicMock() - - profile = ProxMplsTagUntagProfile(tp_config) - profile.init(mock.MagicMock()) - profile._profile_helper = profile_helper = mock.MagicMock() - profile_helper.run_test = target - - profile.execute_traffic(traffic_generator) - self.assertEqual(round(profile.current_lower, 2), 74.69) - self.assertEqual(round(profile.current_upper, 2), 75.39) - self.assertEqual(len(runs), 8) - - def test_mpls_2(self): - def target(*args, **kwargs): - runs.append(args[2]) - if args[2] < 0 or args[2] > 100: - raise RuntimeError(' '.join([str(args), str(runs)])) - if args[2] > 25.0: - return fail_tuple, {} - return success_tuple, {} - - tp_config = { - 'traffic_profile': { - 'packet_sizes': [200], - 'test_precision': 2.0, - }, - } - - runs = [] - success_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.1, 5.2, 5.3], 995, 1000, 123.4) - fail_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.6, 5.7, 5.8], 850, 1000, 123.4) - - traffic_generator = mock.MagicMock() - - profile = ProxMplsTagUntagProfile(tp_config) - profile.init(mock.MagicMock()) - profile._profile_helper = profile_helper = mock.MagicMock() - profile_helper.run_test = target - - profile.execute_traffic(traffic_generator) - self.assertEqual(round(profile.current_lower, 2), 24.06) - self.assertEqual(round(profile.current_upper, 2), 25.47) - self.assertEqual(len(runs), 7) diff --git a/tests/unit/network_services/vnf_generic/vnf/test_tg_trex.py b/tests/unit/network_services/vnf_generic/vnf/test_tg_trex.py index d08c62e0b..a2a5058fc 100644 --- a/tests/unit/network_services/vnf_generic/vnf/test_tg_trex.py +++ b/tests/unit/network_services/vnf_generic/vnf/test_tg_trex.py @@ -18,6 +18,8 @@ from __future__ import absolute_import import unittest + +import copy import mock SSH_HELPER = 'yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper' @@ -75,6 +77,8 @@ class TestTrexTrafficGen(unittest.TestCase): 'driver': "i40e", 'dst_ip': '152.16.100.20', 'local_iface_name': 'xe0', + 'vld_id': 'downlink_0', + 'ifname': 'xe0', 'local_mac': '00:00:00:00:00:02'}, 'vnfd-connection-point-ref': 'xe0', 'name': 'xe0'}, @@ -89,6 +93,8 @@ class TestTrexTrafficGen(unittest.TestCase): 'bandwidth': '10 Gbps', 'dst_ip': '152.16.40.20', 'local_iface_name': 'xe1', + 'vld_id': 'uplink_0', + 'ifname': 'xe1', 'local_mac': '00:00:00:00:00:01'}, 'vnfd-connection-point-ref': 'xe1', 'name': 'xe1'}]}], @@ -386,6 +392,8 @@ class TestTrexTrafficGen(unittest.TestCase): self.sut._connect_client.get_stats = mock.Mock(return_value="0") self.sut.resource_helper.RUN_DURATION = 0 self.sut.resource_helper.QUEUE_WAIT_TIME = 0 + # must generate cfg before we can run traffic so Trex port mapping is created + self.sut.resource_helper.generate_cfg() self.sut._traffic_runner(mock_traffic_profile) @mock.patch(SSH_HELPER) @@ -397,6 +405,52 @@ class TestTrexTrafficGen(unittest.TestCase): self.assertIsNone(trex_traffic_gen.resource_helper.generate_cfg()) @mock.patch(SSH_HELPER) + def test_build_ports_reversed_pci_ordering(self, ssh): + mock_ssh(ssh) + vnfd = copy.deepcopy(self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + vnfd['vdu'][0]['external-interface'] = [ + {'virtual-interface': + {'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 2, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'local_iface_name': 'xe0', + 'vld_id': 'downlink_0', + 'ifname': 'xe0', + 'local_mac': '00:00:00:00:00:02'}, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0'}, + {'virtual-interface': + {'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:04:00.0', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'vld_id': 'uplink_0', + 'ifname': 'xe1', + 'local_mac': '00:00:00:00:00:01'}, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1'}] + trex_traffic_gen = TrexTrafficGen(NAME, vnfd) + trex_traffic_gen.resource_helper.ssh_helper = mock.MagicMock() + trex_traffic_gen.resource_helper.generate_cfg() + trex_traffic_gen.resource_helper._build_ports() + self.assertEqual(sorted(trex_traffic_gen.resource_helper.all_ports), [0, 1]) + # there is a gap in ordering + self.assertEqual(dict(trex_traffic_gen.resource_helper.dpdk_to_trex_port_map), + {0: 0, 2: 1}) + + @mock.patch(SSH_HELPER) def test_run_traffic(self, ssh): mock_ssh(ssh) diff --git a/yardstick/benchmark/core/scenario.py b/yardstick/benchmark/core/scenario.py index cd119c24c..28eb65230 100644 --- a/yardstick/benchmark/core/scenario.py +++ b/yardstick/benchmark/core/scenario.py @@ -10,7 +10,6 @@ """ Handler for yardstick command 'scenario' """ from __future__ import absolute_import -from __future__ import print_function from yardstick.benchmark.scenarios.base import Scenario from yardstick.benchmark.core import print_hbar @@ -27,9 +26,9 @@ class Scenarios(object): # pragma: no cover print_hbar(78) print("| %-16s | %-60s" % ("Type", "Description")) print_hbar(78) - for stype in types: - print("| %-16s | %-60s" % (stype.__scenario_type__, - stype.__doc__.split("\n")[0])) + for scenario_class in types: + print("| %-16s | %-60s" % (scenario_class.get_scenario_type(), + scenario_class.get_description())) print_hbar(78) def show(self, args): diff --git a/yardstick/benchmark/scenarios/base.py b/yardstick/benchmark/scenarios/base.py index 3cb138dd8..7af85834c 100644 --- a/yardstick/benchmark/scenarios/base.py +++ b/yardstick/benchmark/scenarios/base.py @@ -64,6 +64,20 @@ class Scenario(object): raise RuntimeError("No such scenario type %s" % scenario_type) + @classmethod + def get_scenario_type(cls): + """Return a string with the scenario type, if defined""" + return str(getattr(cls, '__scenario_type__', None)) + + @classmethod + def get_description(cls): + """Return a single line string with the class description + + This function will retrieve the class docstring and return the first + line, or 'None' if it's empty. + """ + return cls.__doc__.splitlines()[0] if cls.__doc__ else str(None) + def _push_to_outputs(self, keys, values): return dict(zip(keys, values)) diff --git a/yardstick/network_services/traffic_profile/prox_mpls_tag_untag.py b/yardstick/network_services/traffic_profile/prox_mpls_tag_untag.py deleted file mode 100644 index 0e1048b5d..000000000 --- a/yardstick/network_services/traffic_profile/prox_mpls_tag_untag.py +++ /dev/null @@ -1,99 +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. -""" Fixed traffic profile definitions """ - -from __future__ import absolute_import - -import logging - -from yardstick.network_services.traffic_profile.prox_profile import ProxProfile - -LOG = logging.getLogger(__name__) - - -class ProxMplsTagUntagProfile(ProxProfile): - """ - This profile adds a single stream at the beginning of the traffic session - """ - - def __init__(self, tp_config): - super(ProxMplsTagUntagProfile, self).__init__(tp_config) - self.current_lower = self.lower_bound - self.current_upper = self.upper_bound - - @property - def delta(self): - return self.current_upper - self.current_lower - - @property - def mid_point(self): - return (self.current_lower + self.current_upper) / 2 - - 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_value) - logger.info("Testing with value %s", test_value) - - yield test_value - test_value = self.mid_point - - def run_test_with_pkt_size(self, traffic_gen, pkt_size, duration): - """Run the test for a single packet size. - - :param traffic_gen: traffic generator instance - :type traffic_gen: TrafficGen - :param pkt_size: The packet size to test with. - :type pkt_size: int - :param duration: The duration for each try. - :type duration: int - - """ - - LOG.info("Testing with packet size %d", pkt_size) - - # Binary search assumes the lower value of the interval is - # successful and the upper value is a failure. - # The first value that is tested, is the maximum value. If that - # succeeds, no more searching is needed. If it fails, a regular - # binary search is performed. - # - # The test_value used for the first iteration of binary search - # is adjusted so that the delta between this test_value and the - # upper bound is a power-of-2 multiple of precision. In the - # optimistic situation where this first test_value results in a - # success, the binary search will complete on an integer multiple - # of the precision, rather than on a fraction of it. - - # throughput and packet loss from the most recent successful test - successful_pkt_loss = 0.0 - for test_value in self.bounds_iterator(LOG): - result, port_samples = self._profile_helper.run_test(pkt_size, duration, - test_value, self.tolerated_loss) - - if result.success: - LOG.debug("Success! Increasing lower bound") - self.current_lower = test_value - successful_pkt_loss = result.pkt_loss - else: - LOG.debug("Failure... Decreasing upper bound") - self.current_upper = test_value - - samples = result.get_samples(pkt_size, successful_pkt_loss, port_samples) - self.queue.put(samples) diff --git a/yardstick/network_services/traffic_profile/rfc2544.py b/yardstick/network_services/traffic_profile/rfc2544.py index 16e809b65..b1ca8a345 100644 --- a/yardstick/network_services/traffic_profile/rfc2544.py +++ b/yardstick/network_services/traffic_profile/rfc2544.py @@ -62,7 +62,7 @@ class RFC2544Profile(TrexProfile): self.generator.rfc2544_helper.correlated_traffic: continue for intf in intfs: - port = self.generator.vnfd_helper.port_num(intf) + port = self.generator.port_num(intf) self.ports.append(port) self.generator.client.add_streams(self.get_streams(profile_data), ports=port) @@ -170,7 +170,7 @@ class RFC2544Profile(TrexProfile): self.generator.rfc2544_helper.correlated_traffic: continue for intf in intfs: - port = self.generator.vnfd_helper.port_num(intf) + port = self.generator.port_num(intf) self.ports.append(port) self.generator.client.add_streams(self.get_streams(profile_data), ports=port) diff --git a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py index 75ed6227d..ff81b5f5f 100644 --- a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py @@ -391,6 +391,10 @@ class ClientResourceHelper(ResourceHelper): self.vnfd_helper.port_nums(self.vnfd_helper.port_pairs.downlink_ports) self.all_ports = self.vnfd_helper.port_nums(self.vnfd_helper.port_pairs.all_ports) + def port_num(self, intf): + # by default return port num + return self.vnfd_helper.port_num(intf) + def get_stats(self, *args, **kwargs): try: return self.client.get_stats(*args, **kwargs) diff --git a/yardstick/network_services/vnf_generic/vnf/tg_trex.py b/yardstick/network_services/vnf_generic/vnf/tg_trex.py index 458f1b844..93ba8557a 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_trex.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_trex.py @@ -48,27 +48,38 @@ class TrexResourceHelper(ClientResourceHelper): ASYNC_PORT = 4500 SYNC_PORT = 4501 + def __init__(self, setup_helper): + super(TrexResourceHelper, self).__init__(setup_helper) + self.port_map = {} + self.dpdk_to_trex_port_map = {} + def generate_cfg(self): port_names = self.vnfd_helper.port_pairs.all_ports vpci_list = [] port_list = [] + self.port_map = {} + self.dpdk_to_trex_port_map = {} - port_nums = sorted(self.vnfd_helper.port_nums(port_names)) - for port_num in port_nums: - interface = self.vnfd_helper.find_interface_by_port(port_num) + sorted_ports = sorted((self.vnfd_helper.port_num(port_name), port_name) for port_name in + port_names) + for index, (port_num, port_name) in enumerate(sorted_ports): + interface = self.vnfd_helper.find_interface(name=port_name) virtual_interface = interface['virtual-interface'] dst_mac = virtual_interface["dst_mac"] - # why skip?, ordering is based on DPDK port number so we can't skip + # this is to check for unused ports, all ports in the topology + # will always have dst_mac if not dst_mac: continue - # TRex ports must be in DPDK port number, so order of append matters + # TRex ports are in logical order roughly based on DPDK port number sorting vpci_list.append(virtual_interface["vpci"]) local_mac = virtual_interface["local_mac"] port_list.append({ "src_mac": mac_address_to_hex_list(local_mac), "dest_mac": mac_address_to_hex_list(dst_mac), }) + self.port_map[port_name] = index + self.dpdk_to_trex_port_map[port_num] = index trex_cfg = { 'interfaces': vpci_list, 'port_info': port_list, @@ -80,6 +91,17 @@ class TrexResourceHelper(ClientResourceHelper): cfg_str = yaml.safe_dump(cfg_file, default_flow_style=False, explicit_start=True) self.ssh_helper.upload_config_file(os.path.basename(self.CONF_FILE), cfg_str) + def _build_ports(self): + super(TrexResourceHelper, self)._build_ports() + # override with TRex logic port number + self.uplink_ports = [self.dpdk_to_trex_port_map[p] for p in self.uplink_ports] + self.downlink_ports = [self.dpdk_to_trex_port_map[p] for p in self.downlink_ports] + self.all_ports = [self.dpdk_to_trex_port_map[p] for p in self.all_ports] + + def port_num(self, intf): + # return logical TRex port + return self.port_map[intf] + def check_status(self): status, _, _ = self.ssh_helper.execute("sudo lsof -i:%s" % self.SYNC_PORT) return status |