summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--samples/vnf_samples/vnf_descriptors/tg_rfc2544_tpl.yaml69
-rw-r--r--tests/unit/network_services/vnf_generic/vnf/tc_baremetal_rfc2544_ipv4_1flow_64B.yaml41
-rw-r--r--tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_trex.py340
-rw-r--r--yardstick/network_services/vnf_generic/vnf/tg_rfc2544_trex.py285
4 files changed, 735 insertions, 0 deletions
diff --git a/samples/vnf_samples/vnf_descriptors/tg_rfc2544_tpl.yaml b/samples/vnf_samples/vnf_descriptors/tg_rfc2544_tpl.yaml
new file mode 100644
index 000000000..97feae5ba
--- /dev/null
+++ b/samples/vnf_samples/vnf_descriptors/tg_rfc2544_tpl.yaml
@@ -0,0 +1,69 @@
+# 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.
+
+vnfd:vnfd-catalog:
+ vnfd:
+ - id: TrexTrafficGenRFC # nsb class mapping
+ name: trexverifier
+ short-name: trexverifier
+ description: TRex stateless traffic verifier
+ mgmt-interface:
+ vdu-id: trexgen-baremetal
+ user: '{{user}}' # Value filled by vnfdgen
+ password: '{{password}}' # Value filled by vnfdgen
+ ip: '{{ip}}' # Value filled by vnfdgen
+ connection-point:
+ - name: xe0
+ type: VPORT
+ - name: xe1
+ type: VPORT
+ vdu:
+ - id: trexgen-baremetal
+ name: trexgen-baremetal
+ description: TRex stateless traffic verifier
+ external-interface:
+ - name: xe0
+ virtual-interface:
+ type: PCI-PASSTHROUGH
+ # Substitution variables MUST be quoted. Otherwise Python can misinterpet them.
+ vpci: '{{ interfaces.xe0.vpci }}' # Value filled by vnfdgen
+ local_iface_name: '{{ interfaces.xe0.local_iface_name }}' # Value filled by vnfdgen
+ local_ip: '{{ interfaces.xe0.local_ip }}' # Value filled by vnfdgen
+ driver: '{{ interfaces.xe0.driver}}' # Value filled by vnfdgen
+ dst_ip: '{{ interfaces.xe0.dst_ip }}' # Value filled by vnfdgen
+ local_mac: '{{ interfaces.xe0.local_mac }}' # Value filled by vnfdgen
+ dst_mac: '{{ interfaces.xe0.dst_mac }}' # Value filled by vnfdgen
+ bandwidth: 10 Gbps
+ vnfd-connection-point-ref: xe0
+ - name: xe1
+ virtual-interface:
+ type: PCI-PASSTHROUGH
+ vpci: '{{ interfaces.xe1.vpci }}' # Value filled by vnfdgen
+ local_iface_name: '{{ interfaces.xe1.local_iface_name }}' # Value filled by vnfdgen
+ local_ip: '{{ interfaces.xe1.local_ip }}' # Value filled by vnfdgen
+ driver: '{{ interfaces.xe1.driver}}' # Value filled by vnfdgen
+ dst_ip: '{{ interfaces.xe1.dst_ip }}' # Value filled by vnfdgen
+ local_mac: '{{ interfaces.xe1.local_mac }}' # Value filled by vnfdgen
+ dst_mac: '{{ interfaces.xe1.dst_mac }}' # Value filled by vnfdgen
+ bandwidth: 10 Gbps
+ vnfd-connection-point-ref: xe1
+
+ benchmark:
+ kpi:
+ - rx_throughput_fps
+ - tx_throughput_fps
+ - tx_throughput_mbps
+ - rx_throughput_mbps
+ - in_packets
+ - out_packets
diff --git a/tests/unit/network_services/vnf_generic/vnf/tc_baremetal_rfc2544_ipv4_1flow_64B.yaml b/tests/unit/network_services/vnf_generic/vnf/tc_baremetal_rfc2544_ipv4_1flow_64B.yaml
new file mode 100644
index 000000000..fb1be35c1
--- /dev/null
+++ b/tests/unit/network_services/vnf_generic/vnf/tc_baremetal_rfc2544_ipv4_1flow_64B.yaml
@@ -0,0 +1,41 @@
+# 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.
+
+---
+schema: yardstick:task:0.1
+scenarios:
+- type: NSPerf
+ traffic_profile: "../../traffic_profiles/ipv4_throughput_vpe.yaml"
+ topology: vpe_vnf_topology.yaml
+ nodes:
+ tg__1: trafficgen_1.yardstick
+ vnf__1: vnf.yardstick
+ tc_options:
+ rfc2544:
+ allowed_drop_rate: 0.8 - 1
+ vnf_options:
+ vpe:
+ cfg: vpe_config
+ runner:
+ type: Duration
+ duration: 400
+ interval: 35
+ traffic_options:
+ flow: "../../traffic_profiles/ipv4_1flow_Packets_vpe.yaml"
+ imix: "../../traffic_profiles/imix_voice.yaml"
+context:
+ type: Node
+ name: yardstick
+ nfvi_type: baremetal
+ file: /etc/yardstick/nodes/pod.yaml
diff --git a/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_trex.py b/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_trex.py
new file mode 100644
index 000000000..4ce5d81ab
--- /dev/null
+++ b/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_trex.py
@@ -0,0 +1,340 @@
+#!/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.
+#
+
+from __future__ import absolute_import
+import unittest
+import mock
+import os
+import multiprocessing
+from multiprocessing import Queue
+
+from stl.trex_stl_lib.trex_stl_client import STLClient
+from yardstick.network_services.vnf_generic.vnf.tg_rfc2544_trex import \
+ TrexTrafficGenRFC
+from yardstick.network_services.vnf_generic.vnf import tg_rfc2544_trex
+from yardstick.network_services.traffic_profile.base import TrafficProfile
+
+
+class TestTrexTrafficGenRFC(unittest.TestCase):
+ VNFD = {'vnfd:vnfd-catalog':
+ {'vnfd':
+ [{'short-name': 'VpeVnf',
+ 'vdu':
+ [{'routing_table':
+ [{'network': '152.16.100.20',
+ 'netmask': '255.255.255.0',
+ 'gateway': '152.16.100.20',
+ 'if': 'xe0'},
+ {'network': '152.16.40.20',
+ 'netmask': '255.255.255.0',
+ 'gateway': '152.16.40.20',
+ 'if': 'xe1'}],
+ 'description': 'VPE approximation using DPDK',
+ 'name': 'vpevnf-baremetal',
+ 'nd_route_tbl':
+ [{'network': '0064:ff9b:0:0:0:0:9810:6414',
+ 'netmask': '112',
+ 'gateway': '0064:ff9b:0:0:0:0:9810:6414',
+ 'if': 'xe0'},
+ {'network': '0064:ff9b:0:0:0:0:9810:2814',
+ 'netmask': '112',
+ 'gateway': '0064:ff9b:0:0:0:0:9810:2814',
+ 'if': 'xe1'}],
+ 'id': 'vpevnf-baremetal',
+ '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': '0',
+ 'bandwidth': '10 Gbps',
+ 'driver': "i40e",
+ 'dst_ip': '152.16.100.20',
+ 'local_iface_name': 'xe0',
+ 'local_mac': '00:00:00:00:00:01'},
+ 'vnfd-connection-point-ref': 'xe0',
+ 'name': 'xe0'},
+ {'virtual-interface':
+ {'dst_mac': '00:00:00:00:00:03',
+ 'vpci': '0000:05:00.1',
+ 'local_ip': '152.16.40.19',
+ 'type': 'PCI-PASSTHROUGH',
+ 'driver': "i40e",
+ 'netmask': '255.255.255.0',
+ 'dpdk_port_num': '1',
+ 'bandwidth': '10 Gbps',
+ 'dst_ip': '152.16.40.20',
+ 'local_iface_name': 'xe1',
+ 'local_mac': '00:00:00:00:00:02'},
+ 'vnfd-connection-point-ref': 'xe1',
+ 'name': 'xe1'}]}],
+ 'description': 'Vpe approximation using DPDK',
+ 'mgmt-interface':
+ {'vdu-id': 'vpevnf-baremetal',
+ 'host': '1.1.1.1',
+ 'password': 'r00t',
+ 'user': 'root',
+ 'ip': '1.1.1.1'},
+ 'benchmark':
+ {'kpi': ['packets_in', 'packets_fwd', 'packets_dropped']},
+ 'connection-point': [{'type': 'VPORT', 'name': 'xe0'},
+ {'type': 'VPORT', 'name': 'xe1'}],
+ 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh'}]}}
+
+ 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}}
+
+ TC_YAML = {'scenarios': [{'tc_options':
+ {'rfc2544': {'allowed_drop_rate': '0.8 - 1'}},
+ 'runner': {'duration': 400,
+ 'interval': 35, 'type': 'Duration'},
+ 'traffic_options':
+ {'flow': 'ipv4_1flow_Packets_vpe.yaml',
+ 'imix': 'imix_voice.yaml'},
+ 'vnf_options': {'vpe': {'cfg': 'vpe_config'}},
+ 'traffic_profile': 'ipv4_throughput_vpe.yaml',
+ 'type': 'NSPerf',
+ 'nodes': {'tg__1': 'trafficgen_1.yardstick',
+ 'vnf__1': 'vnf.yardstick'},
+ 'topology': 'vpe_vnf_topology.yaml'}],
+ 'context': {'nfvi_type': 'baremetal', 'type': 'Node',
+ 'name': 'yardstick',
+ 'file': '/etc/yardstick/nodes/pod.yaml'},
+ 'schema': 'yardstick:task:0.1'}
+
+ def test___init__(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ trex_traffic_gen = TrexTrafficGenRFC(vnfd)
+ self.assertIsNotNone(trex_traffic_gen._terminated)
+
+ def test_collect_kpi(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ trex_traffic_gen = TrexTrafficGenRFC(vnfd)
+ restult = trex_traffic_gen.collect_kpi()
+ self.assertEqual({}, restult)
+
+ def test_listen_traffic(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ trex_traffic_gen = TrexTrafficGenRFC(vnfd)
+ self.assertEqual(None, trex_traffic_gen.listen_traffic({}))
+
+ def test_instantiate(self):
+ mock_traffic_profile = mock.Mock(autospec=TrafficProfile)
+ mock_traffic_profile.get_traffic_definition.return_value = "64"
+ mock_traffic_profile.params = self.TRAFFIC_PROFILE
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh_mock.run = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ trex_traffic_gen = TrexTrafficGenRFC(vnfd)
+ trex_traffic_gen._start_server = mock.Mock(return_value=0)
+ scenario_cfg = {"tc": "tc_baremetal_rfc2544_ipv4_1flow_64B"}
+ self.assertEqual(0, trex_traffic_gen.instantiate(scenario_cfg, {}))
+
+ def test_instantiate_error(self):
+ mock_traffic_profile = mock.Mock(autospec=TrafficProfile)
+ mock_traffic_profile.get_traffic_definition.return_value = "64"
+ mock_traffic_profile.params = self.TRAFFIC_PROFILE
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(1, "", ""))
+ ssh_mock.run = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ trex_traffic_gen = TrexTrafficGenRFC(vnfd)
+ scenario_cfg = {"tc": "tc_baremetal_rfc2544_ipv4_1flow_64B"}
+ self.assertRaises(RuntimeError,
+ trex_traffic_gen.instantiate, scenario_cfg, {})
+
+ def test__get_rfc_tolerance(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh_mock.run = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ trex_traffic_gen = TrexTrafficGenRFC(vnfd)
+ self.assertEqual([0.8, 1.0],
+ trex_traffic_gen._get_rfc_tolerance(self.TC_YAML))
+ self.TC_YAML["scenarios"][0]["tc_options"]['rfc2544']['allowed_drop_rate'] = '0.8'
+ self.assertEqual([0.8, 0.8],
+ trex_traffic_gen._get_rfc_tolerance(self.TC_YAML))
+
+ def test__start_server(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh_mock.run = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ trex_traffic_gen = TrexTrafficGenRFC(vnfd)
+ self.assertEqual(None, trex_traffic_gen._start_server())
+
+ def _get_file_abspath(self, filename):
+ curr_path = os.path.dirname(os.path.abspath(__file__))
+ file_path = os.path.join(curr_path, filename)
+ return file_path
+
+ def test__traffic_runner(self):
+ mock_traffic_profile = mock.Mock(autospec=TrafficProfile)
+ mock_traffic_profile.get_traffic_definition.return_value = "64"
+ mock_traffic_profile.execute.return_value = "64"
+ mock_traffic_profile.get_drop_percentage.return_value = "64"
+ mock_traffic_profile.params = self.TRAFFIC_PROFILE
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh_mock.run = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ self.sut = TrexTrafficGenRFC(vnfd)
+ self.sut.connection = mock.Mock()
+ self.sut.connection.run = mock.Mock()
+ q = Queue()
+ client_started = multiprocessing.Value('i', 1)
+ self.sut._vpci_ascending = ["0000:05:00.0", "0000:05:00.1"]
+ self.sut._connect_client = mock.Mock(autospec=STLClient)
+ self.sut._connect_client.get_stats = mock.Mock(return_value="0")
+ self.sut.tc_file_name = \
+ self._get_file_abspath(
+ "tc_baremetal_rfc2544_ipv4_1flow_64B.yaml")
+ tg_rfc2544_trex.DURATION = 1
+ tg_rfc2544_trex.WAIT_TIME = 1
+ self.sut._traffic_runner(mock_traffic_profile, q, client_started,
+ self.sut._terminated)
+
+ def test__split_mac_address_into_list(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ trex_traffic_gen = TrexTrafficGenRFC(vnfd)
+ result = ['0x00', '0x00', '0x00', '0x00', '0x00', '0x01']
+ self.assertEqual(result,
+ trex_traffic_gen._split_mac_address_into_list(
+ "00:00:00:00:00:01"))
+
+ def test__generate_trex_cfg(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh_mock.run = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ trex_traffic_gen = TrexTrafficGenRFC(vnfd)
+ tg_rfc2544_trex.WAIT_TIME = 1
+ self.assertEqual(None, trex_traffic_gen._generate_trex_cfg(vnfd))
+
+ def test_run_traffic(self):
+ mock_traffic_profile = mock.Mock(autospec=TrafficProfile)
+ mock_traffic_profile.get_traffic_definition.return_value = "64"
+ mock_traffic_profile.params = self.TRAFFIC_PROFILE
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh_mock.run = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ self.sut = TrexTrafficGenRFC(vnfd)
+ self.sut.connection = mock.Mock()
+ self.sut.connection.run = mock.Mock()
+ self.sut._traffic_runner = mock.Mock(return_value=0)
+ client_started = multiprocessing.Value('i', 1)
+ result = self.sut.run_traffic(mock_traffic_profile, client_started)
+ self.sut._traffic_process.terminate()
+ self.assertEqual(True, result)
+
+ def test_scale(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh_mock.run = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ flavor = ""
+ trex_traffic_gen = TrexTrafficGenRFC(vnfd)
+ self.assertRaises(NotImplementedError,
+ trex_traffic_gen.scale, flavor)
+
+ def test_terminate(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ trex_traffic_gen = TrexTrafficGenRFC(vnfd)
+ self.assertEqual(None, trex_traffic_gen.terminate())
+
+ def test__connect_client(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ trex_traffic_gen = TrexTrafficGenRFC(vnfd)
+ client = mock.Mock(autospec=STLClient)
+ client.connect = mock.Mock(return_value=0)
+ self.assertIsNotNone(trex_traffic_gen._connect_client(client))
diff --git a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_trex.py b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_trex.py
new file mode 100644
index 000000000..ee7498cb8
--- /dev/null
+++ b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_trex.py
@@ -0,0 +1,285 @@
+# 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.
+""" Trex traffic generation definitions which implements rfc2544 """
+
+from __future__ import absolute_import
+from __future__ import print_function
+import multiprocessing
+import time
+import logging
+import os
+import yaml
+
+from yardstick import ssh
+from yardstick.network_services.vnf_generic.vnf.base import GenericTrafficGen
+from yardstick.network_services.utils import get_nsb_option
+from stl.trex_stl_lib.trex_stl_client import STLClient
+from stl.trex_stl_lib.trex_stl_client import LoggerApi
+from stl.trex_stl_lib.trex_stl_exceptions import STLError
+
+LOGGING = logging.getLogger(__name__)
+
+DURATION = 30
+WAIT_TIME = 3
+TREX_SYNC_PORT = 4500
+TREX_ASYNC_PORT = 4501
+
+
+class TrexTrafficGenRFC(GenericTrafficGen):
+ """
+ This class handles mapping traffic profile and generating
+ traffic for rfc2544 testcase.
+ """
+
+ def __init__(self, vnfd):
+ super(TrexTrafficGenRFC, self).__init__(vnfd)
+ self._result = {}
+ self._terminated = multiprocessing.Value('i', 0)
+ self._queue = multiprocessing.Queue()
+ self._terminated = multiprocessing.Value('i', 0)
+ self._traffic_process = None
+ self._vpci_ascending = None
+ self.tc_file_name = None
+ self.client = None
+ self.my_ports = None
+
+ mgmt_interface = self.vnfd["mgmt-interface"]
+ ssh_port = mgmt_interface.get("ssh_port", ssh.DEFAULT_PORT)
+ self.connection = ssh.SSH(mgmt_interface["user"], mgmt_interface["ip"],
+ password=mgmt_interface["password"],
+ port=ssh_port)
+ self.connection.wait()
+
+ @classmethod
+ def _split_mac_address_into_list(cls, mac):
+ octets = mac.split(':')
+ for i, elem in enumerate(octets):
+ octets[i] = "0x" + str(elem)
+ return octets
+
+ def _generate_trex_cfg(self, vnfd):
+ """
+
+ :param vnfd: vnfd.yaml
+ :return: trex_cfg.yaml file
+ """
+ trex_cfg = dict(
+ port_limit=0,
+ version='2',
+ interfaces=[],
+ port_info=list(dict(
+ ))
+ )
+ trex_cfg["port_limit"] = len(vnfd["vdu"][0]["external-interface"])
+ trex_cfg["version"] = '2'
+
+ cfg_file = []
+ vpci = []
+ port = {}
+
+ ext_intf = vnfd["vdu"][0]["external-interface"]
+ for interface in ext_intf:
+ virt_intf = interface["virtual-interface"]
+ vpci.append(virt_intf["vpci"])
+
+ port["src_mac"] = \
+ self._split_mac_address_into_list(virt_intf["local_mac"])
+
+ time.sleep(WAIT_TIME)
+ port["dest_mac"] = \
+ self._split_mac_address_into_list(virt_intf["dst_mac"])
+ if virt_intf["dst_mac"]:
+ trex_cfg["port_info"].append(port.copy())
+
+ trex_cfg["interfaces"] = vpci
+ cfg_file.append(trex_cfg)
+
+ with open('/tmp/trex_cfg.yaml', 'w') as outfile:
+ outfile.write(yaml.safe_dump(cfg_file, default_flow_style=False))
+ self.connection.put('/tmp/trex_cfg.yaml', '/etc')
+
+ self._vpci_ascending = sorted(vpci)
+
+ def scale(self, flavor=""):
+ ''' scale vnfbased on flavor input '''
+ super(TrexTrafficGenRFC, self).scale(flavor)
+
+ def instantiate(self, scenario_cfg, context_cfg):
+ self._generate_trex_cfg(self.vnfd)
+ self.tc_file_name = '{0}.yaml'.format(scenario_cfg['tc'])
+ trex = os.path.join(self.bin_path, "trex")
+ err, _, _ = \
+ self.connection.execute("ls {} >/dev/null 2>&1".format(trex))
+ if err != 0:
+ self.connection.put(trex, trex, True)
+
+ LOGGING.debug("Starting TRex server...")
+ _tg_server = \
+ multiprocessing.Process(target=self._start_server)
+ _tg_server.start()
+ while True:
+ LOGGING.info("Waiting for TG Server to start.. ")
+ time.sleep(1)
+
+ status = \
+ self.connection.execute("lsof -i:%s" % TREX_SYNC_PORT)[0]
+ if status == 0:
+ LOGGING.info("TG server is up and running.")
+ return _tg_server.exitcode
+ if not _tg_server.is_alive():
+ raise RuntimeError("Traffic Generator process died.")
+
+ def listen_traffic(self, traffic_profile):
+ pass
+
+ def _get_logical_if_name(self, vpci):
+ ext_intf = self.vnfd["vdu"][0]["external-interface"]
+ for interface in range(len(self.vnfd["vdu"][0]["external-interface"])):
+ virtual_intf = ext_intf[interface]["virtual-interface"]
+ if virtual_intf["vpci"] == vpci:
+ return ext_intf[interface]["name"]
+
+ def run_traffic(self, traffic_profile,
+ client_started=multiprocessing.Value('i', 0)):
+
+ self._traffic_process = \
+ multiprocessing.Process(target=self._traffic_runner,
+ args=(traffic_profile, self._queue,
+ client_started, self._terminated))
+ self._traffic_process.start()
+ # Wait for traffic process to start
+ while client_started.value == 0:
+ time.sleep(1)
+
+ return self._traffic_process.is_alive()
+
+ def _start_server(self):
+ mgmt_interface = self.vnfd["mgmt-interface"]
+ ssh_port = mgmt_interface.get("ssh_port", ssh.DEFAULT_PORT)
+ _server = ssh.SSH(mgmt_interface["user"], mgmt_interface["ip"],
+ password=mgmt_interface["password"],
+ port=ssh_port)
+ _server.wait()
+
+ _server.execute("fuser -n tcp %s %s -k > /dev/null 2>&1" %
+ (TREX_SYNC_PORT, TREX_ASYNC_PORT))
+ _server.execute("pkill -9 rex > /dev/null 2>&1")
+
+ trex_path = os.path.join(self.bin_path, "trex/scripts")
+ path = get_nsb_option("trex_path", trex_path)
+ trex_cmd = "cd " + path + "; sudo ./t-rex-64 -i > /dev/null 2>&1"
+
+ _server.execute(trex_cmd)
+
+ def _connect_client(self, client=None):
+ if client is None:
+ client = STLClient(username=self.vnfd["mgmt-interface"]["user"],
+ server=self.vnfd["mgmt-interface"]["ip"],
+ verbose_level=LoggerApi.VERBOSE_QUIET)
+ for idx in range(6):
+ try:
+ client.connect()
+ break
+ except STLError:
+ LOGGING.info("Unable to connect to Trex. Attempt %s", idx)
+ time.sleep(WAIT_TIME)
+ return client
+
+ @classmethod
+ def _get_rfc_tolerance(cls, tc_yaml):
+ tolerance = '0.8 - 1.0'
+ if 'tc_options' in tc_yaml['scenarios'][0]:
+ tc_options = tc_yaml['scenarios'][0]['tc_options']
+ if 'rfc2544' in tc_options:
+ tolerance = \
+ tc_options['rfc2544'].get('allowed_drop_rate', '0.8 - 1.0')
+
+ tolerance = tolerance.split('-')
+ min_tol = float(tolerance[0])
+ if len(tolerance) == 2:
+ max_tol = float(tolerance[1])
+ else:
+ max_tol = float(tolerance[0])
+
+ return [min_tol, max_tol]
+
+ def _traffic_runner(self, traffic_profile, queue,
+ client_started, terminated):
+ LOGGING.info("Starting TRex client...")
+ tc_yaml = {}
+
+ with open(self.tc_file_name) as tc_file:
+ tc_yaml = yaml.load(tc_file.read())
+
+ tolerance = self._get_rfc_tolerance(tc_yaml)
+
+ # fixme: fix passing correct trex config file,
+ # instead of searching the default path
+ self.my_ports = [0, 1]
+ self.client = self._connect_client()
+ self.client.reset(ports=self.my_ports)
+ self.client.remove_all_streams(self.my_ports) # remove all streams
+ while not terminated.value:
+ traffic_profile.execute(self)
+ client_started.value = 1
+ time.sleep(DURATION)
+ self.client.stop(self.my_ports)
+ time.sleep(WAIT_TIME)
+ last_res = self.client.get_stats(self.my_ports)
+ samples = {}
+ for vpci_idx in range(len(self._vpci_ascending)):
+ name = \
+ self._get_logical_if_name(self._vpci_ascending[vpci_idx])
+ # fixme: VNFDs KPIs values needs to be mapped to TRex structure
+ if not isinstance(last_res, dict):
+ terminated.value = 1
+ last_res = {}
+
+ samples[name] = \
+ {"rx_throughput_fps":
+ float(last_res.get(vpci_idx, {}).get("rx_pps", 0.0)),
+ "tx_throughput_fps":
+ float(last_res.get(vpci_idx, {}).get("tx_pps", 0.0)),
+ "rx_throughput_mbps":
+ float(last_res.get(vpci_idx, {}).get("rx_bps", 0.0)),
+ "tx_throughput_mbps":
+ float(last_res.get(vpci_idx, {}).get("tx_bps", 0.0)),
+ "in_packets":
+ last_res.get(vpci_idx, {}).get("ipackets", 0),
+ "out_packets":
+ last_res.get(vpci_idx, {}).get("opackets", 0)}
+
+ samples = \
+ traffic_profile.get_drop_percentage(self, samples,
+ tolerance[0], tolerance[1])
+ queue.put(samples)
+ self.client.stop(self.my_ports)
+ self.client.disconnect()
+ queue.put(samples)
+
+ def collect_kpi(self):
+ if not self._queue.empty():
+ result = self._queue.get()
+ self._result.update(result)
+ LOGGING.debug("trex collect Kpis %s", self._result)
+ return self._result
+
+ def terminate(self):
+ self._terminated.value = 1 # stop Trex clinet
+
+ self.connection.execute("fuser -n tcp %s %s -k > /dev/null 2>&1" %
+ (TREX_SYNC_PORT, TREX_ASYNC_PORT))
+
+ if self._traffic_process:
+ self._traffic_process.terminate()