diff options
11 files changed, 590 insertions, 0 deletions
diff --git a/samples/vnf_samples/nsut/cmts/cmts-tg-topology.yaml b/samples/vnf_samples/nsut/cmts/cmts-tg-topology.yaml new file mode 100644 index 000000000..81323e71c --- /dev/null +++ b/samples/vnf_samples/nsut/cmts/cmts-tg-topology.yaml @@ -0,0 +1,39 @@ +# Copyright (c) 2018 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: cmts-tg-topology + name: cmts-tg-topology + short-name: cmts-tg-topology + description: cmts-tg-topology + constituent-vnfd: + - member-vnf-index: '1' + vnfd-id-ref: tg__0 + VNF model: ../../vnf_descriptors/tg_pktgen.yaml + - member-vnf-index: '2' + vnfd-id-ref: vnf__0 + VNF model: ../../vnf_descriptors/tg_pktgen.yaml + + vld: [] +# - id: uplink +# name: tg__0 to vnf__0 link 1 +# type: ELAN +# vnfd-connection-point-ref: +# - member-vnf-index-ref: '1' +# vnfd-connection-point-ref: sriov01 +# vnfd-id-ref: tg__0 +# - member-vnf-index-ref: '2' +# vnfd-connection-point-ref: sriov01 +# vnfd-id-ref: vnf__0 diff --git a/samples/vnf_samples/nsut/cmts/tc_k8s_pktgen_01.yaml b/samples/vnf_samples/nsut/cmts/tc_k8s_pktgen_01.yaml new file mode 100644 index 000000000..cab8bb885 --- /dev/null +++ b/samples/vnf_samples/nsut/cmts/tc_k8s_pktgen_01.yaml @@ -0,0 +1,171 @@ +# Copyright (c) 2018 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/pktgen_throughput.yaml + topology: cmts-tg-topology.yaml + nodes: + tg__0: trafficgen-k8syardstick + vnf__0: vnf-k8syardstick + options: {} + runner: + type: IterationIPC + iterations: 10 + interval: 15 + timeout: 10000 +context: + name: k8syardstick + type: Kubernetes + + servers: + vnf: + containers: + - image: si-docker.ir.intel.com/vcmts-ubuntu/vcmts-pktgen-uepi + args: ["/opt/bin/cmk isolate --conf-dir=/etc/cmk --socket-id=0 --pool=dataplane /vcmts/setup.sh anga_mac_1_ds.pcap ds"] + env: + - name: LUA_PATH + value: "/vcmts/Pktgen.lua" + - name: CMK_PROC_FS + value: "/host/proc" + resources: + requests: + pod.alpha.kubernetes.io/opaque-int-resource-cmk: "1" + ports: + - containerPort: 22022 + volumeMounts: + - name: hugepages + mountPath: /dev/hugepages + - name: sysfs + mountPath: /sys + - name: sriov + mountPath: /sriov-cni + - name: host-proc + mountPath: /host/proc + readOnly: true + - name: cmk-install-dir + mountPath: /opt/bin + - name: cmk-conf-dir + mountPath: /etc/cmk + securityContext: + allowPrivilegeEscalation: true + privileged: true + + node_ports: + - name: lua # Lower case alphanumeric characters or '-' + port: 22022 + networks: + - flannel + - sriov01 + volumes: + - name: hugepages + hostPath: + path: /dev/hugepages + - name: sysfs + hostPath: + path: /sys + - name: sriov + hostPath: + path: /var/lib/cni/sriov + - name: cmk-install-dir + hostPath: + path: /opt/bin + - name: host-proc + hostPath: + path: /proc + - name: cmk-conf-dir + hostPath: + path: /etc/cmk + + trafficgen: + containers: + - image: si-docker.ir.intel.com/vcmts-ubuntu/vcmts-pktgen-uepi + args: ["/opt/bin/cmk isolate --conf-dir=/etc/cmk --socket-id=0 --pool=dataplane /vcmts/setup.sh anga_mac_1_ds.pcap ds"] + env: + - name: LUA_PATH + value: "/vcmts/Pktgen.lua" + - name: CMK_PROC_FS + value: "/host/proc" + resources: + requests: + pod.alpha.kubernetes.io/opaque-int-resource-cmk: "1" + ports: + - containerPort: 22022 + volumeMounts: + - name: hugepages + mountPath: /dev/hugepages + - name: sysfs + mountPath: /sys + - name: sriov + mountPath: /sriov-cni + - name: host-proc + mountPath: /host/proc + readOnly: true + - name: cmk-install-dir + mountPath: /opt/bin + - name: cmk-conf-dir + mountPath: /etc/cmk + securityContext: + allowPrivilegeEscalation: true + privileged: true + + node_ports: + - name: lua # Lower case alphanumeric characters or '-' + port: 22022 + networks: + - flannel + - sriov01 + volumes: + - name: hugepages + hostPath: + path: /dev/hugepages + - name: sysfs + hostPath: + path: /sys + - name: sriov + hostPath: + path: /var/lib/cni/sriov + - name: cmk-install-dir + hostPath: + path: /opt/bin + - name: host-proc + hostPath: + path: /proc + - name: cmk-conf-dir + hostPath: + path: /etc/cmk + + networks: + flannel: + args: '[{ "delegate": { "isDefaultGateway": true }}]' + plugin: flannel + sriov01: + plugin: sriov + args: '[{"if0": "ens802f0", + "if0name": "net0", + "dpdk": { + "kernel_driver": "i40evf", + "dpdk_driver": "igb_uio", + "dpdk_tool": "/opt/nsb_bin/dpdk-devbind.py"} + }]' + sriov02: + plugin: sriov + args: '[{"if0": "ens802f0", + "if0name": "net0", + "dpdk": { + "kernel_driver": "i40evf", + "dpdk_driver": "igb_uio", + "dpdk_tool": "/opt/nsb_bin/dpdk-devbind.py"} + }]' diff --git a/samples/vnf_samples/traffic_profiles/pktgen_throughput.yaml b/samples/vnf_samples/traffic_profiles/pktgen_throughput.yaml new file mode 100644 index 000000000..e222e1d8c --- /dev/null +++ b/samples/vnf_samples/traffic_profiles/pktgen_throughput.yaml @@ -0,0 +1,21 @@ +# Copyright (c) 2018 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: pktgen +description: Traffic profile to run throughput tests +traffic_profile: + traffic_type: PktgenTrafficProfile + duration: 15 diff --git a/samples/vnf_samples/vnf_descriptors/tg_pktgen.yaml b/samples/vnf_samples/vnf_descriptors/tg_pktgen.yaml new file mode 100644 index 000000000..17e631652 --- /dev/null +++ b/samples/vnf_samples/vnf_descriptors/tg_pktgen.yaml @@ -0,0 +1,47 @@ +# Copyright (c) 2018 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: PktgenTrafficGen # NSB class mapping + name: pktgen_tg + short-name: pktgen_tg + description: Pktgen DPDK traffic generator + mgmt-interface: + vdu-id: pktgen + {% if ip is defined %} + ip: '{{ ip }}' + {% endif %} + {% if service_ports is defined and service_ports %} + service_ports: + {% for port in service_ports %} + - port: "{{ port['port']|int }}" + node_port: "{{ port['node_port']|int }}" + target_port: "{{ port['target_port']|int }}" + {% endfor %} + {% endif %} + + vdu: + - id: pktgen_tg + name: pktgen_tg + description: Pktgen DPDK traffic generator + + benchmark: + kpi: + - rx_throughput_fps + - tx_throughput_fps + - tx_throughput_mbps + - rx_throughput_mbps + - in_packets + - out_packets diff --git a/yardstick/common/constants.py b/yardstick/common/constants.py index 4ed40f8af..3d775d48e 100644 --- a/yardstick/common/constants.py +++ b/yardstick/common/constants.py @@ -175,3 +175,4 @@ SCOPE_CLUSTER = 'Cluster' # VNF definition SSH_PORT = 22 +LUA_PORT = 22022 diff --git a/yardstick/common/exceptions.py b/yardstick/common/exceptions.py index 2e6cbdd7b..56b11b7aa 100644 --- a/yardstick/common/exceptions.py +++ b/yardstick/common/exceptions.py @@ -212,6 +212,10 @@ class WaitTimeout(YardstickException): message = 'Wait timeout while waiting for condition' +class PktgenActionError(YardstickException): + message = 'Error in "%(action)s" action' + + class KubernetesApiException(YardstickException): message = ('Kubernetes API errors. Action: %(action)s, ' 'resource: %(resource)s') diff --git a/yardstick/network_services/traffic_profile/__init__.py b/yardstick/network_services/traffic_profile/__init__.py index 356b36bd9..a1b26a24d 100644 --- a/yardstick/network_services/traffic_profile/__init__.py +++ b/yardstick/network_services/traffic_profile/__init__.py @@ -27,6 +27,7 @@ def register_modules(): 'yardstick.network_services.traffic_profile.prox_profile', 'yardstick.network_services.traffic_profile.prox_ramp', 'yardstick.network_services.traffic_profile.rfc2544', + 'yardstick.network_services.traffic_profile.pktgen', ] for module in modules: diff --git a/yardstick/network_services/traffic_profile/pktgen.py b/yardstick/network_services/traffic_profile/pktgen.py new file mode 100644 index 000000000..30f81b794 --- /dev/null +++ b/yardstick/network_services/traffic_profile/pktgen.py @@ -0,0 +1,61 @@ +# Copyright (c) 2018 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 yardstick.common import exceptions +from yardstick.common import utils +from yardstick.network_services.traffic_profile import base as tp_base + + +class PktgenTrafficProfile(tp_base.TrafficProfile): + """This class handles Pktgen Trex Traffic profile execution""" + + def __init__(self, tp_config): # pragma: no cover + super(PktgenTrafficProfile, self).__init__(tp_config) + self._host = None + self._port = None + + def init(self, host, port): # pragma: no cover + """Initialize control parameters + + :param host: (str) ip or host name + :param port: (int) TCP socket port number for Lua commands + """ + self._host = host + self._port = port + + def start(self): + if utils.send_socket_command(self._host, self._port, + 'pktgen.start("0")') != 0: + raise exceptions.PktgenActionError(action='start') + + def stop(self): + if utils.send_socket_command(self._host, self._port, + 'pktgen.stop("0")') != 0: + raise exceptions.PktgenActionError(action='stop') + + def rate(self, rate): + command = 'pktgen.set("0", "rate", ' + str(rate) + ')' + if utils.send_socket_command(self._host, self._port, command) != 0: + raise exceptions.PktgenActionError(action='rate') + + def clear_all_stats(self): + if utils.send_socket_command(self._host, self._port, 'clr') != 0: + raise exceptions.PktgenActionError(action='clear all stats') + + def help(self): + if utils.send_socket_command(self._host, self._port, 'help') != 0: + raise exceptions.PktgenActionError(action='help') + + def execute_traffic(self, *args, **kwargs): # pragma: no cover + pass diff --git a/yardstick/network_services/vnf_generic/vnf/tg_pktgen.py b/yardstick/network_services/vnf_generic/vnf/tg_pktgen.py new file mode 100644 index 000000000..9d452213f --- /dev/null +++ b/yardstick/network_services/vnf_generic/vnf/tg_pktgen.py @@ -0,0 +1,103 @@ +# Copyright (c) 2018 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 logging +import multiprocessing +import time +import uuid + +from yardstick.common import constants +from yardstick.common import exceptions +from yardstick.common import utils +from yardstick.network_services.vnf_generic.vnf import base as vnf_base + + +LOG = logging.getLogger(__name__) + + +class PktgenTrafficGen(vnf_base.GenericTrafficGen, + vnf_base.GenericVNFEndpoint): + """DPDK Pktgen traffic generator + + Website: http://pktgen-dpdk.readthedocs.io/en/latest/index.html + """ + + TIMEOUT = 30 + + def __init__(self, name, vnfd, task_id): + vnf_base.GenericTrafficGen.__init__(self, name, vnfd, task_id) + self.queue = multiprocessing.Queue() + self._id = uuid.uuid1().int + self._mq_producer = self._setup_mq_producer(self._id) + vnf_base.GenericVNFEndpoint.__init__(self, self._id, [task_id], + self.queue) + self._consumer = vnf_base.GenericVNFConsumer([task_id], self) + self._consumer.start_rpc_server() + self._traffic_profile = None + self._node_ip = vnfd['mgmt-interface'].get('ip') + self._lua_node_port = self._get_lua_node_port( + vnfd['mgmt-interface'].get('service_ports', [])) + self._rate = 1 + + def instantiate(self, scenario_cfg, context_cfg): # pragma: no cover + pass + + def run_traffic(self, traffic_profile): + self._traffic_profile = traffic_profile + self._traffic_profile.init(self._node_ip, self._lua_node_port) + utils.wait_until_true(self._is_running, timeout=self.TIMEOUT, + sleep=2) + + def terminate(self): # pragma: no cover + pass + + def collect_kpi(self): # pragma: no cover + pass + + def scale(self, flavor=''): # pragma: no cover + pass + + def wait_for_instantiate(self): # pragma: no cover + pass + + def runner_method_start_iteration(self, ctxt, **kwargs): + # pragma: no cover + LOG.debug('Start method') + # NOTE(ralonsoh): 'rate' should be modified between iterations. The + # current implementation is just for testing. + self._rate += 1 + self._traffic_profile.start() + self._traffic_profile.rate(self._rate) + time.sleep(4) + self._traffic_profile.stop() + self._mq_producer.tg_method_iteration(1, 1, {}) + + def runner_method_stop_iteration(self, ctxt, **kwargs): # pragma: no cover + # pragma: no cover + LOG.debug('Stop method') + + @staticmethod + def _get_lua_node_port(service_ports): + for port in (port for port in service_ports if + int(port['port']) == constants.LUA_PORT): + return int(port['node_port']) + # NOTE(ralonsoh): in case LUA port is not present, an exception should + # be raised. + + def _is_running(self): + try: + self._traffic_profile.help() + return True + except exceptions.PktgenActionError: + return False diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_pktgen.py b/yardstick/tests/unit/network_services/traffic_profile/test_pktgen.py new file mode 100644 index 000000000..08542b4f1 --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_pktgen.py @@ -0,0 +1,63 @@ +# Copyright (c) 2018 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 mock + +from yardstick.common import utils +from yardstick.network_services.traffic_profile import pktgen +from yardstick.tests.unit import base as ut_base + + +class TestIXIARFC2544Profile(ut_base.BaseUnitTestCase): + + def setUp(self): + self._tp_config = {'traffic_profile': {}} + self._host = 'localhost' + self._port = '12345' + self.tp = pktgen.PktgenTrafficProfile(self._tp_config) + self.tp.init(self._host, self._port) + self._mock_send_socket_command = mock.patch.object( + utils, 'send_socket_command', return_value=0) + self.mock_send_socket_command = self._mock_send_socket_command.start() + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_send_socket_command.stop() + + def test_start(self): + self.tp.start() + self.mock_send_socket_command.assert_called_once_with( + self._host, self._port, 'pktgen.start("0")') + + def test_stop(self): + self.tp.stop() + self.mock_send_socket_command.assert_called_once_with( + self._host, self._port, 'pktgen.stop("0")') + + def test_rate(self): + rate = 75 + self.tp.rate(rate) + command = 'pktgen.set("0", "rate", 75)' + self.mock_send_socket_command.assert_called_once_with( + self._host, self._port, command) + + def test_clear_all_stats(self): + self.tp.clear_all_stats() + self.mock_send_socket_command.assert_called_once_with( + self._host, self._port, 'clr') + + def test_help(self): + self.tp.help() + self.mock_send_socket_command.assert_called_once_with( + self._host, self._port, 'help') diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_pktgen.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_pktgen.py new file mode 100644 index 000000000..d341b970b --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_pktgen.py @@ -0,0 +1,79 @@ +# Copyright (c) 2018 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 uuid + +import mock + +from yardstick.common import constants +from yardstick.common import exceptions +from yardstick.network_services.vnf_generic.vnf import base as vnf_base +from yardstick.network_services.vnf_generic.vnf import tg_pktgen +from yardstick.tests.unit import base as ut_base + + +class PktgenTrafficGenTestCase(ut_base.BaseUnitTestCase): + + SERVICE_PORTS = [{'port': constants.LUA_PORT, + 'node_port': '34501'}] + VNFD = {'mgmt-interface': {'ip': '1.2.3.4', + 'service_ports': SERVICE_PORTS}, + 'vdu': [{'external-interface': 'interface'}], + 'benchmark': {'kpi': 'fake_kpi'} + } + + def setUp(self): + self._id = uuid.uuid1().int + self._mock_vnf_consumer = mock.patch.object(vnf_base, + 'GenericVNFConsumer') + self.mock_vnf_consumer = self._mock_vnf_consumer.start() + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_vnf_consumer.stop() + + def test__init(self): + tg = tg_pktgen.PktgenTrafficGen('name1', self.VNFD, self._id) + self.assertTrue(isinstance(tg, (vnf_base.GenericTrafficGen, + vnf_base.GenericVNFEndpoint))) + + def test_run_traffic(self): + tg = tg_pktgen.PktgenTrafficGen('name1', self.VNFD, self._id) + mock_tp = mock.Mock() + with mock.patch.object(tg, '_is_running', return_value=True): + tg.run_traffic(mock_tp) + + mock_tp.init.assert_called_once_with(tg._node_ip, tg._lua_node_port) + + def test__get_lua_node_port(self): + tg = tg_pktgen.PktgenTrafficGen('name1', self.VNFD, self._id) + service_ports = [{'port': constants.LUA_PORT, + 'node_port': '12345'}] + self.assertEqual(12345, tg._get_lua_node_port(service_ports)) + + def test__get_lua_node_port_no_lua_port(self): + tg = tg_pktgen.PktgenTrafficGen('name1', self.VNFD, self._id) + service_ports = [{'port': '333'}] + self.assertIsNone(tg._get_lua_node_port(service_ports)) + + def test__is_running(self): + tg = tg_pktgen.PktgenTrafficGen('name1', self.VNFD, self._id) + with mock.patch.object(tg, '_traffic_profile'): + self.assertTrue(tg._is_running()) + + def test__is_running_exception(self): + tg = tg_pktgen.PktgenTrafficGen('name1', self.VNFD, self._id) + with mock.patch.object(tg, '_traffic_profile') as mock_tp: + mock_tp.help.side_effect = exceptions.PktgenActionError() + self.assertFalse(tg._is_running()) |