aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDeepak S <deepak.s@linux.intel.com>2016-12-30 09:24:52 -0800
committerDeepak S <deepak.s@linux.intel.com>2017-01-19 08:29:35 +0530
commit8d7b81187071891317245a7e47a10dcfe70b3809 (patch)
tree6905348146fc77e6cfaa4d81d42ae901336432fb
parent06432a93864b7c742e3194cc1296cbdcb68d5cfc (diff)
Adding trex trafficgen example.
This patch uses trex trafficgen example to define dynamic traffic profiles and how it can be mapped to real world traffic. JIRA: YARDSTICK-492 Change-Id: Ica24957ebf43315a8d81adabd4745c27d3c7c36a Signed-off-by: Deepak S <deepak.s@linux.intel.com>
-rw-r--r--samples/vnf_samples/nsut/2trex/tc_trex_baremetal_context.yaml43
-rw-r--r--samples/vnf_samples/nsut/2trex/trex_tg_topology.yaml50
-rw-r--r--samples/vnf_samples/traffic_profiles/example.yaml198
-rw-r--r--samples/vnf_samples/traffic_profiles/fixed.yaml24
-rw-r--r--samples/vnf_samples/vnf_descriptors/tg_trex_tpl.yaml75
-rw-r--r--tests/unit/benchmark/scenarios/networking/test_vnf_generic.py6
-rw-r--r--tests/unit/network_services/vnf_generic/vnf/test_tg_trex.py302
-rw-r--r--yardstick/network_services/vnf_generic/vnf/tg_trex.py278
8 files changed, 973 insertions, 3 deletions
diff --git a/samples/vnf_samples/nsut/2trex/tc_trex_baremetal_context.yaml b/samples/vnf_samples/nsut/2trex/tc_trex_baremetal_context.yaml
new file mode 100644
index 000000000..e0ba6d96f
--- /dev/null
+++ b/samples/vnf_samples/nsut/2trex/tc_trex_baremetal_context.yaml
@@ -0,0 +1,43 @@
+# 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/fixed.yaml
+ topology: trex_tg_topology.yaml # TODO: look in relative path where the tc.yaml is found
+
+ nodes: # This section is copied from pod.xml or resolved via Heat
+ trexgen__1: trafficgen_1.yardstick
+ trexvnf__1: vnf.yardstick
+
+ vnf_options:
+ trexgen__1:
+ target_ip: trexvnf__1.xe0.local_ip # TODO: resolve to config vars
+ trexvnf__1:
+ target_ip: trexgen__1.xe1.local_ip # TODO: resolve to config vars
+ runner:
+ type: Duration
+ duration: 10
+
+context:
+ type: Node # Heat, Node(baremetal), Node-ovs, Node-sriov
+ name: yardstick
+ #file: pod.yaml
+ # image: yardstick-baremetal # only important for heat context
+ nfvi_type: baremetal # options: baremetal, sriov, ovs-virtio TODO: what about mixed scenarios?
+ file: /etc/yardstick/nodes/pod.yaml
diff --git a/samples/vnf_samples/nsut/2trex/trex_tg_topology.yaml b/samples/vnf_samples/nsut/2trex/trex_tg_topology.yaml
new file mode 100644
index 000000000..1a9b50c84
--- /dev/null
+++ b/samples/vnf_samples/nsut/2trex/trex_tg_topology.yaml
@@ -0,0 +1,50 @@
+# 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.
+
+nsd:nsd-catalog:
+ nsd:
+ - id: trex-tg-topology
+ name: trex-tg-topology
+ short-name: trex-tg-topology
+ description: trex-tg-topology
+ constituent-vnfd:
+ - member-vnf-index: '1'
+ vnfd-id-ref: trexgen__1
+ VNF model: ../../vnf_descriptors/tg_trex_tpl.yaml #VNF type
+ - member-vnf-index: '2'
+ vnfd-id-ref: trexvnf__1
+ VNF model: ../../vnf_descriptors/tg_trex_tpl.yaml #VNF type
+
+ vld:
+ - id: private
+ name: trexgen__1 to trexvnf__1 link 1
+ type: ELAN
+ vnfd-connection-point-ref:
+ - member-vnf-index-ref: '1'
+ vnfd-connection-point-ref: xe0
+ vnfd-id-ref: trexgen
+ - member-vnf-index-ref: '2'
+ vnfd-connection-point-ref: xe0
+ vnfd-id-ref: trexgen
+
+ - id: public
+ name: trexvnf__1 to trexgen__1 link 2
+ type: ELAN
+ vnfd-connection-point-ref:
+ - member-vnf-index-ref: '1'
+ vnfd-connection-point-ref: xe1
+ vnfd-id-ref: trexgen
+ - member-vnf-index-ref: '2'
+ vnfd-connection-point-ref: xe1
+ vnfd-id-ref: trexgen
diff --git a/samples/vnf_samples/traffic_profiles/example.yaml b/samples/vnf_samples/traffic_profiles/example.yaml
new file mode 100644
index 000000000..71371995e
--- /dev/null
+++ b/samples/vnf_samples/traffic_profiles/example.yaml
@@ -0,0 +1,198 @@
+# 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: "nsb:traffic_profile:0.1"
+
+# This file is a template, it will be filled with values from tc.yaml before passing to the traffic generator
+
+name: rfc2544
+description: Traffic profile to run RFC2544 latency
+traffic_profile:
+ traffic_type : rfc2544 # defines traffic behavior - constant or look for highest possible throughput
+ frame_rate : 100 # pc of linerate
+ flow_number: {{ flow.number }} #This decides how many frames should be generated. Each frame should have a random of the field
+ # that specifies a range (e.g. ipv4 address, port)
+
+private:
+ - ipv4_1:
+ outer_l2:
+ framesize:
+ 64B: {{ get(imix_small, 12) }}
+ 128B: {{get(imix_128B, 12) }}
+ 256B: {{get(imix_256B, 12) }}
+ 373b: {{ get(imix_373B, 12) }}
+ 570B: {{get(imix_570B, 12) }}
+ 1400B: {{get(imix_1400B, 12) }}
+ 1518b: {{get(imix_1500B, 12) }}
+ srcmac: {{src_mac}} # xe0.local_mac
+ dstmac: {{dst_mac}} # xe0.dst_mac
+ QinQ:
+ S-VLAN:
+ id: {{ get(flow.s_vlan_range, "0") }}
+ priority: 0
+ cfi: 0
+ C-VLAN:
+ id: {{ get(flow.c_vlan_range, "0") }}
+ priority: 0
+ cfi: 0
+ mpls:
+ label: {{ get(flow.mpls_label, "0") }}
+
+ outer_l3v4:
+ proto: {{ get(flow.outer_proto, "udp") }},
+ srcip4: {{ get(flow.outer_srcip4, "1.1.1.1") }}
+ dstip4: {{ get(flow.outer_dstip4, "90.90.90.90") }}
+ ttl: 32
+ dscp: {{ get(flow.tos, 0) }}
+
+ outer_l4:
+ srcport: {{ get(flow.outer_srcport, "3002") }}
+ dstport: {{ get(flow.outer_dstport, "3001") }}
+ vni: {{ get(flow.vxlan_vni, "2345") }} #optional
+ inner_l2: #optional
+ srcmac: {{ get(flow.inner_srcmac, "00:00:00:00:00:00") }}
+ dstmac: {{ get(flow.inner_dstmac, "00:00:00:00:00:00") }}
+
+ inner_l3v4: #optional
+ proto: {{ get(flow.inner_proto, "udp") }},
+ srcip4: {{ get(flow.inner_srcip4, "1.1.1.1") }}
+ dstip4: {{ get(flow.inner_dstip4, "90.90.90.90") }}
+ ttl: 32
+ dscp: {{ get(flow.inner_tos, 0) }}
+
+ inner_l3v6:
+ proto: {{ get(flow.inner_proto, "udp") }},
+ srcip6: {{ get(flow.inner_srcip6, "2001::1") }}
+ dstip6: {{ get(flow.outer_dstip6, "2001::11") }}
+ ttl: 32
+ tc: {{ get(flow.inner_tos, 0) }}
+
+ inner_l4:
+ srcport: {{ get(flow.inner_srcport, "3002") }}
+ dstport: {{ get(flow.inner_dstport, "3001") }}
+
+ - ipv6_2:
+ outer_l2:
+ framesize:
+ 64B: {{ get(imix_small, 12) }}
+ 128B: {{get(imix_128B, 12) }}
+ 256B: {{get(imix_256B, 12) }}
+ 373b: {{ get(imix_373B, 12) }}
+ 570B: {{get(imix_570B, 12) }}
+ 1400B: {{get(imix_1400B, 12) }}
+ 1518b: {{get(imix_1500B, 12) }}
+ srcmac: {{src_mac}} # xe0.local_mac
+ dstmac: {{dst_mac}} # xe0.dst_mac
+ QinQ:
+ S-VLAN:
+ id: {{ get(flow.s_vlan_range, "0") }}
+ priority: 0
+ cfi: 0
+ C-VLAN:
+ id: {{ get(flow.c_vlan_range, "0") }}
+ priority: 0
+ cfi: 0
+ mpls:
+ label: {{ get(flow.mpls_label, "0") }}
+
+ outer_l3v6:
+ proto: {{ get(flow.outer_proto, "udp") }},
+ srcip6: {{ get(flow.outer_srcip6, "2001::1") }}
+ dstip6: {{ get(flow.outer_dstip6, "2001::11") }}
+ ttl: 32
+ tc: {{ get(flow.outer_tos, 0) }}
+
+ outer_l4:
+ srcport: {{ get(flow.outer_srcport, "3002") }}
+ dstport: {{ get(flow.outer_dstport, "3001") }}
+ vni: {{ get(flow.vxlan_vni, "2345") }} #optional
+ inner_l2: #optional
+ srcmac: {{ get(flow.inner_srcmac, "00:00:00:00:00:00") }}
+ dstmac: {{ get(flow.inner_dstmac, "00:00:00:00:00:00") }}
+
+ inner_l3v4: #optional
+ proto: {{ get(flow.inner_proto, "udp") }},
+ srcip4: {{ get(flow.inner_srcip4, "1.1.1.1") }}
+ dstip4: {{ get(flow.inner_dstip4, "90.90.90.90") }}
+ ttl: 32
+ dscp: {{ get(flow.inner_tos, 0) }}
+
+ inner_l3v6:
+ proto: {{ get(flow.inner_proto, "udp") }},
+ srcip6: {{ get(flow.inner_srcip6, "2001::1") }}
+ dstip6: {{ get(flow.outer_dstip6, "2001::11") }}
+ ttl: 32
+ tc: {{ get(flow.inner_tos, 0) }}
+
+ inner_l4:
+ srcport: {{ get(flow.inner_srcport, "3002") }}
+ dstport: {{ get(flow.inner_dstport, "3001") }}
+
+public:
+ - ipv4_2:
+ outer_l2:
+ framesize:
+ 64B: {{ get(imix_small, 12) }}
+ 128B: {{get(imix_128B, 12) }}
+ 256B: {{get(imix_256B, 12) }}
+ 373b: {{ get(imix_373B, 12) }}
+ 570B: {{get(imix_570B, 12) }}
+ 1400B: {{get(imix_1400B, 12) }}
+ 1518b: {{get(imix_1500B, 12) }}
+ srcmac: {{get(private.ipv4_1.dst_mac}} # xe0.local_mac
+ dstmac: {{get)private.ipv4_1.src_mac}} # xe0.dst_mac
+ QinQ:
+ S-VLAN:
+ id: {{ get(flow.s_vlan_range, "0") }}
+ priority: 0
+ cfi: 0
+ C-VLAN:
+ id: {{ get(flow.c_vlan_range, "0") }}
+ priority: 0
+ cfi: 0
+ mpls:
+ label: {{ get(flow.mpls_label, "0") }}
+
+ outer_l3v4:
+ proto: {{ get(flow.outer_proto, "udp") }},
+ srcip4: {{ get(private.ipv4_1.outer_l3v4.dstip4) }}
+ dstip4: {{ get(private.ipv4_1.outer_l3v4.srcip4) }}
+ ttl: 32
+ dscp: {{ get(flow.tos, 0) }}
+
+ outer_l4:
+ srcport: {{ get(private.ipv4_1.outer_l4.dstport) }}
+ dstport: {{ get(private.ipv4_1.outer_l4.srcport) }}
+ vni: {{ get(flow.vxlan_vni, "2345") }} #optional
+ inner_l2: #optional
+ srcmac: {{ get(private.ipv4_1.outer_l4.inner_l2.dstmac) }}
+ dstmac: {{ get(private.ipv4_1.outer_l4.inner_l2.srcmac) }}
+
+ inner_l3v4: #optional
+ proto: {{ get(flow.inner_proto, "udp") }},
+ srcip4: {{ get(private.ipv4_1.inner_l3v4.dstip4) }}
+ dstip4: {{ get(private.ipv4_1.inner_l3v4.srcip4) }}
+ ttl: 32
+ dscp: {{ get(flow.inner_tos, 0) }}
+
+ inner_l3v6:
+ proto: {{ get(flow.inner_proto, "udp") }},
+ srcip6: {{ get(private.ipv4_1.inner_l3v6.dstip6) }}
+ dstip6: {{ get(private.ipv4_1.inner_l3v6.srcip6) }}
+ ttl: 32
+ tc: {{ get(flow.inner_tos, 0) }}
+
+ inner_l4:
+ srcport: {{ get(private.ipv4_1.inner_l4.dstport) }}
+ dstport: {{ get(private.ipv4_1.inner_l4.srcport) }}
diff --git a/samples/vnf_samples/traffic_profiles/fixed.yaml b/samples/vnf_samples/traffic_profiles/fixed.yaml
new file mode 100644
index 000000000..afccd6fd4
--- /dev/null
+++ b/samples/vnf_samples/traffic_profiles/fixed.yaml
@@ -0,0 +1,24 @@
+# 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: "nsb:traffic_profile:0.1"
+
+name: fixed
+description: Fixed traffic profile to run UDP traffic
+
+traffic_profile:
+ traffic_type: FixedProfile
+ frame_rate: 100 # pps
+ flow_number: 10
+ frame_size: 64
diff --git a/samples/vnf_samples/vnf_descriptors/tg_trex_tpl.yaml b/samples/vnf_samples/vnf_descriptors/tg_trex_tpl.yaml
new file mode 100644
index 000000000..b1641836b
--- /dev/null
+++ b/samples/vnf_samples/vnf_descriptors/tg_trex_tpl.yaml
@@ -0,0 +1,75 @@
+# 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: TrexTrafficGen # NSPerf class mapping
+ name: trexgen
+ short-name: trexgen
+ description: TRex stateless traffic generator for tests
+ vm-flavor:
+ vcpu-count: '4'
+ memory-mb: '4096'
+ 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 generator for tests
+ 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_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
+ vld_id: '{{ interfaces.xe0.vld_id }}' # 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_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
+ vld_id: '{{ interfaces.xe1.vld_id }}' # 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
+ - tx_throughput_pc_linerate
+ - rx_throughput_pc_linerate
+ - min_latency
+ - max_latency
+ - avg_latency
diff --git a/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py b/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py
index 540d7f214..40b07b1ca 100644
--- a/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py
+++ b/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py
@@ -262,9 +262,9 @@ class TestNetworkServiceTestCase(unittest.TestCase):
"ipv4_1flow_Packets_vpe.yaml1"
self.assertEqual({}, self.s._get_traffic_flow(self.scenario_cfg))
- def test_get_vnf_impl_invalid(self):
- self.assertRaises(IncorrectConfig, self.s.get_vnf_impl,
- COMPLETE_TREX_VNFD['vnfd:vnfd-catalog']['vnfd'][0])
+ def test_get_vnf_imp(self):
+ vnfd = COMPLETE_TREX_VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ self.assertIsNotNone(self.s.get_vnf_impl(vnfd))
def test_load_vnf_models_invalid(self):
self.context_cfg["nodes"]['trexgen__1']['VNF model'] = \
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
new file mode 100644
index 000000000..a7f4d8870
--- /dev/null
+++ b/tests/unit/network_services/vnf_generic/vnf/test_tg_trex.py
@@ -0,0 +1,302 @@
+#!/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 multiprocessing
+from multiprocessing import Queue
+
+from stl.trex_stl_lib.trex_stl_client import STLClient
+from yardstick.network_services.vnf_generic.vnf.tg_trex import TrexTrafficGen
+from yardstick.network_services.traffic_profile.base import TrafficProfile
+
+
+class TestTrexTrafficGen(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:02'},
+ '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:01'},
+ '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}}
+
+ 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 = TrexTrafficGen(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 = TrexTrafficGen(vnfd)
+ trex_traffic_gen._queue.put({})
+ 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 = TrexTrafficGen(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 = TrexTrafficGen(vnfd)
+ self.assertEqual(0, trex_traffic_gen.instantiate({}, {}))
+
+ 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 = TrexTrafficGen(vnfd)
+ self.assertRaises(RuntimeError,
+ trex_traffic_gen.instantiate, {}, {})
+
+ 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 = TrexTrafficGen(vnfd)
+ self.assertEqual(None, trex_traffic_gen._start_server())
+
+ 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.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 = TrexTrafficGen(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._traffic_runner(mock_traffic_profile, q, client_started,
+ self.sut._terminated)
+
+ 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 = TrexTrafficGen(vnfd)
+ self.assertEqual(None, trex_traffic_gen._generate_trex_cfg(vnfd))
+
+ 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_mock.run = \
+ mock.Mock(return_value=(0, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ trex_traffic_gen = TrexTrafficGen(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_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 = TrexTrafficGen(vnfd)
+ self.sut.connection = mock.Mock()
+ self.sut.connection.run = mock.Mock()
+ self.sut._traffic_runner = mock.Mock(return_value=0)
+ self.sut.client_started.value = 1
+ result = self.sut.run_traffic(mock_traffic_profile)
+ self.sut._traffic_process.terminate()
+ self.assertEqual(True, result)
+
+ def test_scale(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=(1, "", ""))
+ ssh.return_value = ssh_mock
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ flavor = ""
+ trex_traffic_gen = TrexTrafficGen(vnfd)
+ self.assertRaises(NotImplementedError,
+ trex_traffic_gen.scale, flavor)
+
+ def test_setup_vnf_environment(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=(1, "", ""))
+ ssh.return_value = ssh_mock
+ trex_traffic_gen = TrexTrafficGen(vnfd)
+ self.assertEqual(
+ None, trex_traffic_gen.setup_vnf_environment(ssh_mock))
+
+ 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 = TrexTrafficGen(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
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ trex_traffic_gen = TrexTrafficGen(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_trex.py b/yardstick/network_services/vnf_generic/vnf/tg_trex.py
new file mode 100644
index 000000000..2731476e0
--- /dev/null
+++ b/yardstick/network_services/vnf_generic/vnf/tg_trex.py
@@ -0,0 +1,278 @@
+# 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 acts as traffic generation and vnf definitions based on IETS Spec """
+
+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 yardstick.network_services.utils import provision_tool
+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
+
+LOG = logging.getLogger(__name__)
+DURATION = 30
+TREX_SYNC_PORT = 4500
+TREX_ASYNC_PORT = 4501
+
+
+class TrexTrafficGen(GenericTrafficGen):
+ """
+ This class handles mapping traffic profile and generating
+ traffic for given testcase
+ """
+
+ def __init__(self, vnfd):
+ super(TrexTrafficGen, self).__init__(vnfd)
+ self._result = {}
+ self._queue = multiprocessing.Queue()
+ self._terminated = multiprocessing.Value('i', 0)
+ self._traffic_process = None
+ self._vpci_ascending = None
+ self.client = None
+ self.my_ports = None
+ self.client_started = multiprocessing.Value('i', 0)
+
+ 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 = {}
+
+ for interface in range(len(vnfd["vdu"][0]["external-interface"])):
+ ext_intrf = vnfd["vdu"][0]["external-interface"]
+ virtual_interface = ext_intrf[interface]["virtual-interface"]
+ vpci.append(virtual_interface["vpci"])
+
+ port["src_mac"] = self._split_mac_address_into_list(
+ virtual_interface["local_mac"])
+ port["dest_mac"] = self._split_mac_address_into_list(
+ virtual_interface["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)
+
+ @classmethod
+ def __setup_hugepages(cls, connection):
+ hugepages = \
+ connection.execute(
+ "awk '/Hugepagesize/ { print $2$3 }' < /proc/meminfo")[1]
+ hugepages = hugepages.rstrip()
+
+ memory_path = \
+ '/sys/kernel/mm/hugepages/hugepages-%s/nr_hugepages' % hugepages
+ connection.execute("awk -F: '{ print $1 }' < %s" % memory_path)
+
+ pages = 16384 if hugepages.rstrip() == "2048kB" else 16
+ connection.execute("echo %s > %s" % (pages, memory_path))
+
+ def setup_vnf_environment(self, connection):
+ ''' setup dpdk environment needed for vnf to run '''
+
+ self.__setup_hugepages(connection)
+ connection.execute("modprobe uio && modprobe igb_uio")
+
+ exit_status = connection.execute("lsmod | grep -i igb_uio")[0]
+ if exit_status == 0:
+ return
+
+ dpdk = os.path.join(self.bin_path, "dpdk-16.07")
+ dpdk_setup = \
+ provision_tool(self.connection,
+ os.path.join(self.bin_path, "nsb_setup.sh"))
+ status = connection.execute("ls {} >/dev/null 2>&1".format(dpdk))[0]
+ if status:
+ connection.execute("bash %s dpdk >/dev/null 2>&1" % dpdk_setup)
+
+ def scale(self, flavor=""):
+ ''' scale vnfbased on flavor input '''
+ super(TrexTrafficGen, self).scale(flavor)
+
+ def instantiate(self, scenario_cfg, context_cfg):
+ self._generate_trex_cfg(self.vnfd)
+ self.setup_vnf_environment(self.connection)
+
+ trex = os.path.join(self.bin_path, "trex")
+ err = \
+ self.connection.execute("ls {} >/dev/null 2>&1".format(trex))[0]
+ if err != 0:
+ LOG.info("Copying trex to destination...")
+ self.connection.put("/root/.bash_profile", "/root/.bash_profile")
+ self.connection.put(trex, trex, True)
+ ko_src = os.path.join(trex, "scripts/ko/src/")
+ self.connection.execute("cd %s && make && make install" % ko_src)
+
+ LOG.info("Starting TRex server...")
+ _tg_process = \
+ multiprocessing.Process(target=self._start_server)
+ _tg_process.start()
+ while True:
+ if not _tg_process.is_alive():
+ raise RuntimeError("Traffic Generator process died.")
+ LOG.info("Waiting for TG Server to start.. ")
+ time.sleep(1)
+ status = \
+ self.connection.execute("lsof -i:%s" % TREX_SYNC_PORT)[0]
+ if status == 0:
+ LOG.info("TG server is up and running.")
+ return _tg_process.exitcode
+
+ 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):
+ self._traffic_process = \
+ multiprocessing.Process(target=self._traffic_runner,
+ args=(traffic_profile, self._queue,
+ self.client_started,
+ self._terminated))
+ self._traffic_process.start()
+ # Wait for traffic process to start
+ while self.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))
+
+ 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)
+ # try to connect with 5s intervals, 30s max
+ for idx in range(6):
+ try:
+ client.connect()
+ break
+ except STLError:
+ LOG.info("Unable to connect to Trex Server.. Attempt %s", idx)
+ time.sleep(5)
+ return client
+
+ def _traffic_runner(self, traffic_profile, queue,
+ client_started, terminated):
+ LOG.info("Starting TRex client...")
+
+ 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
+ last_res = self.client.get_stats(self.my_ports)
+ if not isinstance(last_res, dict): # added for mock unit test
+ terminated.value = 1
+ last_res = {}
+
+ 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
+ xe_value = last_res.get(vpci_idx, {})
+ samples[name] = \
+ {"rx_throughput_fps": float(xe_value.get("rx_pps", 0.0)),
+ "tx_throughput_fps": float(xe_value.get("tx_pps", 0.0)),
+ "rx_throughput_mbps": float(xe_value.get("rx_bps", 0.0)),
+ "tx_throughput_mbps": float(xe_value.get("tx_bps", 0.0)),
+ "in_packets": xe_value.get("ipackets", 0),
+ "out_packets": xe_value.get("opackets", 0)}
+ queue.put(samples)
+
+ self.client.disconnect()
+ terminated.value = 0
+
+ def collect_kpi(self):
+ if not self._queue.empty():
+ self._result.update(self._queue.get())
+ LOG.debug("trex collect Kpis %s", self._result)
+ return self._result
+
+ def terminate(self):
+ self.connection.execute("fuser -n tcp %s %s -k > /dev/null 2>&1" %
+ (TREX_SYNC_PORT, TREX_ASYNC_PORT))
+ self.traffic_finished = True
+ if self._traffic_process:
+ self._traffic_process.terminate()