diff options
-rw-r--r-- | tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py | 236 | ||||
-rw-r--r-- | tests/vsperf/pvp_rfc2544_throughput_dpdk.yaml | 91 | ||||
-rwxr-xr-x | tools/vsperf-img-finalize.sh | 51 | ||||
-rwxr-xr-x | tools/vsperf-img-modify.sh | 74 | ||||
-rw-r--r-- | tools/vsperf_install.yml | 125 | ||||
-rw-r--r-- | yardstick/benchmark/scenarios/networking/testpmd_vsperf.bash | 60 | ||||
-rw-r--r-- | yardstick/benchmark/scenarios/networking/vsperf_dpdk.py | 347 |
7 files changed, 984 insertions, 0 deletions
diff --git a/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py b/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py new file mode 100644 index 000000000..3b9f99b08 --- /dev/null +++ b/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python + +# Copyright 2017 Nokia +# +# 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. + +# Unittest for yardstick.benchmark.scenarios.networking.vsperf.VsperfDPDK + +from __future__ import absolute_import +try: + from unittest import mock +except ImportError: + import mock +import unittest + +from yardstick.benchmark.scenarios.networking import vsperf_dpdk + + +@mock.patch('yardstick.benchmark.scenarios.networking.vsperf_dpdk.subprocess') +@mock.patch('yardstick.benchmark.scenarios.networking.vsperf_dpdk.ssh') +@mock.patch("yardstick.benchmark.scenarios.networking.vsperf_dpdk.open", + mock.mock_open()) +class VsperfDPDKTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + "host": { + "ip": "10.229.47.137", + "user": "ubuntu", + "password": "ubuntu", + }, + } + self.args = { + 'task_id': "1234-5678", + 'options': { + 'testname': 'pvp_tput', + 'traffic_type': 'rfc2544_throughput', + 'frame_size': '64', + 'test_params': 'TRAFFICGEN_DURATION=30;', + 'trafficgen_port1': 'ens4', + 'trafficgen_port2': 'ens5', + 'conf_file': 'vsperf-yardstick.conf', + 'setup_script': 'setup_yardstick.sh', + 'moongen_helper_file': '~/moongen.py', + 'moongen_host_ip': '10.5.201.151', + 'moongen_port1_mac': '8c:dc:d4:ae:7c:5c', + 'moongen_port2_mac': '8c:dc:d4:ae:7c:5d', + 'trafficgen_port1_nw': 'test2', + 'trafficgen_port2_nw': 'test3', + }, + 'sla': { + 'metrics': 'throughput_rx_fps', + 'throughput_rx_fps': 500000, + 'action': 'monitor', + } + } + + def test_vsperf_dpdk_setup(self, mock_ssh, mock_subprocess): + p = vsperf_dpdk.VsperfDPDK(self.args, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertEqual(p.setup_done, True) + + def test_vsperf_dpdk_teardown(self, mock_ssh, mock_subprocess): + p = vsperf_dpdk.VsperfDPDK(self.args, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertEqual(p.setup_done, True) + + p.teardown() + self.assertEqual(p.setup_done, False) + + def test_vsperf_dpdk_is_dpdk_setup_no(self, mock_ssh, mock_subprocess): + p = vsperf_dpdk.VsperfDPDK(self.args, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertEqual(p.setup_done, True) + + # is_dpdk_setup() specific mocks + mock_ssh.SSH.from_node().execute.return_value = (0, 'dummy', '') + + result = p._is_dpdk_setup() + self.assertEqual(result, False) + + def test_vsperf_dpdk_is_dpdk_setup_yes(self, mock_ssh, mock_subprocess): + p = vsperf_dpdk.VsperfDPDK(self.args, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertEqual(p.setup_done, True) + + # is_dpdk_setup() specific mocks + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + result = p._is_dpdk_setup() + self.assertEqual(result, True) + + def test_vsperf_dpdk_dpdk_setup_first(self, mock_ssh, mock_subprocess): + p = vsperf_dpdk.VsperfDPDK(self.args, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertEqual(p.setup_done, True) + + # is_dpdk_setup() specific mocks + mock_ssh.SSH.from_node().execute.return_value = (0, 'dummy', '') + + p.dpdk_setup() + self.assertEqual(p._is_dpdk_setup(), False) + self.assertEqual(p.dpdk_setup_done, True) + + def test_vsperf_dpdk_dpdk_setup_next(self, mock_ssh, mock_subprocess): + p = vsperf_dpdk.VsperfDPDK(self.args, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertEqual(p.setup_done, True) + + # dpdk_setup() specific mocks + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + p.dpdk_setup() + self.assertEqual(p._is_dpdk_setup(), True) + self.assertEqual(p.dpdk_setup_done, True) + + def test_vsperf_dpdk_dpdk_setup_fail(self, mock_ssh, mock_subprocess): + p = vsperf_dpdk.VsperfDPDK(self.args, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertEqual(p.setup_done, True) + + # dpdk_setup() specific mocks + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + self.assertRaises(RuntimeError, p.dpdk_setup) + + def test_vsperf_dpdk_run_ok(self, mock_ssh, mock_subprocess): + p = vsperf_dpdk.VsperfDPDK(self.args, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertEqual(p.setup_done, True) + + # run() specific mocks + mock_subprocess.call().execute.return_value = None + mock_subprocess.call().execute.return_value = None + mock_ssh.SSH.from_node().execute.return_value = ( + 0, 'throughput_rx_fps\r\n14797660.000\r\n', '') + + result = {} + p.run(result) + + self.assertEqual(result['throughput_rx_fps'], '14797660.000') + + def test_vsperf_dpdk_run_falied_vsperf_execution(self, mock_ssh, + mock_subprocess): + p = vsperf_dpdk.VsperfDPDK(self.args, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertEqual(p.setup_done, True) + + # run() specific mocks + mock_subprocess.call().execute.return_value = None + mock_subprocess.call().execute.return_value = None + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + result = {} + self.assertRaises(RuntimeError, p.run, result) + + def test_vsperf_dpdk_run_falied_csv_report(self, mock_ssh, mock_subprocess): + p = vsperf_dpdk.VsperfDPDK(self.args, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertEqual(p.setup_done, True) + + # run() specific mocks + mock_subprocess.call().execute.return_value = None + mock_subprocess.call().execute.return_value = None + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + result = {} + self.assertRaises(RuntimeError, p.run, result) + +def main(): + unittest.main() + + +if __name__ == '__main__': + main() diff --git a/tests/vsperf/pvp_rfc2544_throughput_dpdk.yaml b/tests/vsperf/pvp_rfc2544_throughput_dpdk.yaml new file mode 100644 index 000000000..0b3e5a876 --- /dev/null +++ b/tests/vsperf/pvp_rfc2544_throughput_dpdk.yaml @@ -0,0 +1,91 @@ +# Copyright 2017 Nokia +# +# 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. + +# VSPERF specific configuration file for execution of RFC2544 throughput +# traffic. Traffic executed by traffic generator is forwarded directly +# between interfaces connected to the traffic generator. So test will only +# benchmark the performance of OVS external bridge at controller node. +# Details about supported test options and test case execution can be +# found in VSPERF documentation: +# +# http://artifacts.opnfv.org/vswitchperf/docs/userguide/yardstick.html + +schema: "yardstick:task:0.1" + +scenarios: +{% for multistream in [1, 1000] %} +- + type: VsperfDPDK + options: + testname: 'pvp_tput' + traffic_type: 'rfc2544_throughput' + multistream: {{multistream}} + frame_size: 64 + test_params: 'TRAFFICGEN_DURATION=60;' + trafficgen_port1: 'ens4' + trafficgen_port2: 'ens5' + conf_file: '~/vsperf-yardstick.conf' + moongen_helper_file: '~/moongen.py' + moongen_host_ip: '10.5.201.151' + moongen_port1_mac: '8c:dc:d4:ae:7c:5c' + moongen_port2_mac: '8c:dc:d4:ae:7c:5d' + trafficgen_port1_nw: 'test2' + trafficgen_port2_nw: 'test3' + + host: vsperf.demo + + runner: + type: Sequence + scenario_option_name: frame_size + sequence: + - 64 + - 128 + - 256 + - 512 + - 1024 + - 1280 + - 1518 + + sla: + # The throughput SLA (or any other SLA) cannot be set to a meaningful + # value without knowledge of the server and networking environment, + # possibly including prior testing in that environment to establish + # a baseline SLA level under well-understood circumstances. + metrics: 'throughput_rx_fps' + throughput_rx_fps: 500000 + action: monitor +{% endfor %} + +context: + name: demo + image: yardstick-vsperf-server + flavor: vsperf-flavor + user: ubuntu + + placement_groups: + pgrp1: + policy: "availability" + + servers: + vsperf: + floating_ip: true + placement: "pgrp1" + + networks: + test: + cidr: '10.0.1.0/24' + test2: + cidr: '10.0.2.0/24' + test3: + cidr: '10.0.3.0/24' diff --git a/tools/vsperf-img-finalize.sh b/tools/vsperf-img-finalize.sh new file mode 100755 index 000000000..cf3677b84 --- /dev/null +++ b/tools/vsperf-img-finalize.sh @@ -0,0 +1,51 @@ +#!/bin/bash +############################################################################## +# Copyright (c) 2017 Nokia +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# PREREQUISITES +# modified image (yardstick-vsperf) must be uploaded to OpenStack +# must have a proper flavor (vsperf-flavor) for the image e.g. +# nova flavor-create vsperf-flavor auto 8192 80 6 +# nova flavor-key vsperf-flavor set hw:numa_nodes=1 +# nova flavor-key vsperf-flavor set hw:mem_page_size=1GB +# nova flavor-key vsperf-flavor set hw:cpu_policy=dedicated +# nova flavor-key vsperf-flavor set hw:vif_multiqueue_enabled=true + +stackname="vsperf-install-stack" +template=vsperf_install.yml +new_image_name="yardstick-vsperf-server" + +openstack stack create $stackname -f yaml -t $template +progress="WARMING_UP" + +while [ "$progress" != "CREATE_COMPLETE" ] +do + sleep 10 + echo "check stack status......." + show_output=$(openstack stack show $stackname) + progress=$(echo $show_output | sed 's/^.*stack_status . \([^ ]*\).*$/\1/') + echo "$progress" + if [ "$progress" == "CREATE_FAILED" ];then + echo "create $stackname failed" + exit 1 + fi +done + +# has to stop the instance before taking the snapshot +nova stop $stackname +sleep 10 + +status=$(nova image-create --poll $stackname $new_image_name) +if [[ "$status" =~ "Finished" ]];then + echo "$new_image_name finished" +fi + +nova delete $stackname +sleep 10 +openstack stack delete --yes $stackname diff --git a/tools/vsperf-img-modify.sh b/tools/vsperf-img-modify.sh new file mode 100755 index 000000000..3ba697c00 --- /dev/null +++ b/tools/vsperf-img-modify.sh @@ -0,0 +1,74 @@ +#!/bin/bash +############################################################################## +# Copyright (c) 2017 Nokia +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# installs required packages +# must be run from inside the image (either chrooted or running) + +set -ex + +if [ $# -eq 1 ]; then + nameserver_ip=$1 + + # /etc/resolv.conf is a symbolic link to /run, restore at end + rm /etc/resolv.conf + echo "nameserver $nameserver_ip" > /etc/resolv.conf + echo "nameserver 8.8.8.8" >> /etc/resolv.conf + echo "nameserver 8.8.4.4" >> /etc/resolv.conf +fi + +# Force apt to use ipv4 due to build problems on LF POD. +echo 'Acquire::ForceIPv4 "true";' > /etc/apt/apt.conf.d/99force-ipv4 + +echo 'GRUB_CMDLINE_LINUX="resume=/dev/sda1 default_hugepagesz=1G hugepagesz=1G hugepages=32 iommu=on iommu=pt intel_iommu=on"' >> /etc/default/grub + +# Add hostname to /etc/hosts. +# Allow console access via pwd +cat <<EOF >/etc/cloud/cloud.cfg.d/10_etc_hosts.cfg +manage_etc_hosts: True +password: ubuntu +chpasswd: { expire: False } +ssh_pwauth: True +EOF + +linuxheadersversion=`echo ls boot/vmlinuz* | cut -d- -f2-` + +apt-get update +apt-get install -y \ + linux-headers-$linuxheadersversion \ + screen \ + locate \ + sshpass \ + git + +cd /root +git clone -b stable/danube https://gerrit.opnfv.org/gerrit/vswitchperf + +# do not compile ovs and qemu +sed -i.bak -e 's/^\(SUBBUILDS\ =\ src_vanilla\)/#\1/' \ + -e 's/^\(SUBDIRS\ += ovs.*\)/#\1/' \ + -e 's/^\(SUBDIRS\ += qemu.*\)/#\1/' \ + vswitchperf/src/Makefile +# If these paths do not exist, vsperf wont start +mkdir -p /root/vswitchperf/src/ovs/ovs/ovsdb/ +touch /root/vswitchperf/src/ovs/ovs/ovsdb/ovsdb-tool +touch /root/vswitchperf/src/ovs/ovs/ovsdb/ovsdb-server +mkdir -p /root/vswitchperf/src/qemu/qemu/x86_64-softmmu/ +touch /root/vswitchperf/src/qemu/qemu/x86_64-softmmu/qemu-system-x86_64 +mkdir -p /root/vswitchperf/src/ovs/ovs/utilities/ +touch /root/vswitchperf/src/ovs/ovs/utilities/ovs-dpctl +touch /root/vswitchperf/src/ovs/ovs/utilities/ovs-vsctl +touch /root/vswitchperf/src/ovs/ovs/utilities/ovs-ofctl +touch /root/vswitchperf/src/ovs/ovs/utilities/ovs-appctl +mkdir -p /root/vswitchperf/src/ovs/ovs/vswitchd/ +touch /root/vswitchperf/src/ovs/ovs/vswitchd/vswitch.ovsschema +touch /root/vswitchperf/src/ovs/ovs/vswitchd/ovs-vswitchd + +# restore symlink +#ln -sf /run/resolvconf/resolv.conf /etc/resolv.conf diff --git a/tools/vsperf_install.yml b/tools/vsperf_install.yml new file mode 100644 index 000000000..3c78e0ca8 --- /dev/null +++ b/tools/vsperf_install.yml @@ -0,0 +1,125 @@ +############################################################################## +# Copyright (c) 2017 Nokia +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +heat_template_version: 2015-04-30 + +description: > + Used to run VMs with Vsperf + +parameters: + image: + type: string + description: Name of the image + default: yardstick-vsperf + + flavor: + type: string + default: vsperf-flavor + + timeout: + type: number + description: Timeout in seconds for WaitCondition, depends on your image and environment + default: 6000 + + external_net_name: + type: string + description: Name of the external network which management network will connect to + default: ext-net1 + +resources: + network: + type: OS::Neutron::Net + properties: + name: vsperf_net + + subnet: + type: OS::Neutron::Subnet + properties: + name: vsperf_subnet + ip_version: 4 + cidr: 192.168.0.0/24 + network: { get_resource: network } + + management_router: + type: OS::Neutron::Router + properties: + name: management_router + external_gateway_info: + network: { get_param: external_net_name } + + management_router_interface: + type: OS::Neutron::RouterInterface + properties: + router: { get_resource: management_router } + subnet: { get_resource: subnet } + + floating_ip: + type: OS::Neutron::FloatingIP + properties: + floating_network: { get_param: external_net_name } + + floating_ip_association: + type: OS::Nova::FloatingIPAssociation + properties: + floating_ip: { get_resource: floating_ip } + server_id: {get_resource: vsperf_vm} + + keypair: + type: OS::Nova::KeyPair + properties: + name: yardstick-key + public_key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD0RkXfW6pksd1cZmXuvXZF/Mlqqq3ahIGcGoULOC97XMpu0vdxMpcUwdjwGqMwEXTVyfHidu0l99bLqOCpSUKCmbWx3ONJ+1kqFx4HwsKEWLiyDYqsuMrDeZT1eFjC5avCoTcrIw2wq5NaBb00lDGagNZOeopaL5YIa4+PizEY23+cir24D67NU21Fg3JE92AIeGlNa4j66L3a+lL0hZq74Dilmp42wm4GsbplRO6KJfyaraowHb1X+TmhCjBgHk6M/OJ9yPAroZyJNcwjMAuuxhAYWRuT3SdbnoUR0RG2VhfDh0qNid7vOqLbhKPeaLLFmzkN+9w3WdCp6LbSYt87 yardstick@yardstick.opnfv.org + + wait_handle: + type: OS::Heat::WaitConditionHandle + + wait_condition: + type: OS::Heat::WaitCondition + properties: + handle: { get_resource: wait_handle } + count: 1 + timeout: { get_param: timeout } + + vsperf_vm: + type: OS::Nova::Server + depends_on: [subnet, keypair] + properties: + name: { get_param: "OS::stack_name" } + image: { get_param: image } + flavor: { get_param: flavor } + key_name: {get_resource: keypair} + networks: + - network: { get_resource: network } + config_drive: True + user_data_format : RAW + user_data: + str_replace: + template: | + #!/bin/bash + cat <<'CEOF' > /tmp/vsperf_post_build.sh + echo "Install vswitchperf" + mv /root/vswitchperf /home/ubuntu + chown -R ubuntu:ubuntu /home/ubuntu/vswitchperf + cd /home/ubuntu/vswitchperf/systems + sudo -H -u ubuntu ./build_base_machine.sh + echo "Set password less access to MoonGen server" + sudo -H -u ubuntu ssh-keygen -b 2048 -t rsa -f /home/ubuntu/.ssh/id_rsa -N '' + sudo -H -u ubuntu touch /home/ubuntu/.cloud-warnings.skip + echo "Enable 1GB huge pages" + update-grub + $NOTIFY --data-binary '{"status": "SUCCESS"}' + CEOF + chmod +x /tmp/vsperf_post_build.sh + nohup /tmp/vsperf_post_build.sh & + params: + $NOTIFY: { get_attr: ['wait_handle', 'curl_cli'] } + +outputs: + vm_uuid: + description: uuid of the VM + value: { get_attr: [ vsperf_vm, show,id ] } diff --git a/yardstick/benchmark/scenarios/networking/testpmd_vsperf.bash b/yardstick/benchmark/scenarios/networking/testpmd_vsperf.bash new file mode 100644 index 000000000..f4d55b2f8 --- /dev/null +++ b/yardstick/benchmark/scenarios/networking/testpmd_vsperf.bash @@ -0,0 +1,60 @@ +############################################################################## +# Copyright (c) 2017 Nokia +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +#!/bin/bash + +set -e + +# Commandline arguments +MOONGEN_PORT1_MAC=$1 # MAC address of the peer port +MOONGEN_PORT2_MAC=$2 # MAC address of the peer port + +DPDK_ROOT='/home/ubuntu/vswitchperf/src/dpdk/dpdk' + +load_modules() +{ + if ! lsmod | grep "uio" &> /dev/null; then + modprobe uio + fi + + if ! lsmod | grep "igb_uio" &> /dev/null; then + insmod ${DPDK_ROOT}/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko + fi + + if ! lsmod | grep "rte_kni" &> /dev/null; then + insmod ${DPDK_ROOT}/x86_64-native-linuxapp-gcc/kmod/rte_kni.ko + fi +} + +change_permissions() +{ + chmod 777 /sys/bus/pci/drivers/virtio-pci/* + chmod 777 /sys/bus/pci/drivers/igb_uio/* +} + +add_interface_to_dpdk(){ + interfaces=$(lspci |grep Eth |tail -n +2 |awk '{print $1}') + ${DPDK_ROOT}/tools/dpdk-devbind.py --bind=igb_uio $interfaces &> /dev/null +} + +run_testpmd() +{ + blacklist=$(lspci |grep Eth |awk '{print $1}'|head -1) + cd ${DPDK_ROOT} + sudo ./dpdk/bin/testpmd -c 0x3f -n 4 -b $blacklist -- -a --nb-cores=4 --coremask=0x3c --burst=64 --txd=4096 --rxd=4096 --rxq=2 --txq=2 --rss-udp --eth-peer=0,$MOONGEN_PORT1_MAC --eth-peer=1,$MOONGEN_PORT2_MAC --forward-mode=mac +} + +main() +{ + load_modules + change_permissions + add_interface_to_dpdk + run_testpmd +} + +main diff --git a/yardstick/benchmark/scenarios/networking/vsperf_dpdk.py b/yardstick/benchmark/scenarios/networking/vsperf_dpdk.py new file mode 100644 index 000000000..454587829 --- /dev/null +++ b/yardstick/benchmark/scenarios/networking/vsperf_dpdk.py @@ -0,0 +1,347 @@ +# Copyright 2016 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. +""" VsperfDPDK specific scenario definition """ + +from __future__ import absolute_import +import pkg_resources +import logging +import os +import subprocess +import csv +import time + +import yardstick.ssh as ssh +import yardstick.common.utils as utils +from yardstick.benchmark.scenarios import base + +LOG = logging.getLogger(__name__) + + +class VsperfDPDK(base.Scenario): + """Execute vsperf with defined parameters + + Parameters: + traffic_type - to specify the type of traffic executed by traffic generator + the valid values are "rfc2544", "continuous", "back2back" + type: string + default: "rfc2544" + frame_size - a frame size for which test should be executed; + Multiple frame sizes can be tested by modification of sequence runner + section inside TC YAML definition. + type: string + default: "64" + bidirectional - speficies if traffic will be uni (False) or bi-directional + (True) + type: string + default: False + iload - specifies frame rate + type: string + default: 100 + multistream - the number of simulated streams + type: string + default: 0 (disabled) + stream_type - specifies network layer used for multistream simulation + the valid values are "L4", "L3" and "L2" + type: string + default: "L4" + test_params - specifies a string with a list of vsperf configuration + parameters, which will be passed to the '--test-params' CLI argument; + Parameters should be stated in the form of 'param=value' and separated + by a semicolon. Please check VSPERF documentation for details about + available configuration parameters and their data types. + In case that both 'test_params' and 'conf_file' are specified, + then values from 'test_params' will override values defined + in the configuration file. + type: string + default: NA + conf_file - path to the vsperf configuration file, which will be uploaded + to the VM; + In case that both 'test_params' and 'conf_file' are specified, + then values from 'test_params' will override values defined + in configuration file. + type: string + default: NA + setup_script - path to the setup script, which will be executed during + setup and teardown phases + type: string + default: NA + trafficgen_port1 - specifies device name of 1st interface connected to + the trafficgen + type: string + default: NA + trafficgen_port2 - specifies device name of 2nd interface connected to + the trafficgen + type: string + default: NA + external_bridge - specifies name of external bridge configured in OVS + type: string + default: "br-ex" + + """ + __scenario_type__ = "VsperfDPDK" + + TESTPMD_SCRIPT = 'testpmd_vsperf.bash' + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + self.moongen_host_ip = \ + scenario_cfg['options'].get('moongen_host_ip', "127.0.0.1") + self.moongen_port1_mac = \ + scenario_cfg['options'].get('moongen_port1_mac', None) + self.moongen_port2_mac = \ + scenario_cfg['options'].get('moongen_port2_mac', None) + self.dpdk_setup_done = False + self.setup_done = False + self.client = None + self.tg_port1 = \ + self.scenario_cfg['options'].get('trafficgen_port1', None) + self.tg_port2 = \ + self.scenario_cfg['options'].get('trafficgen_port2', None) + self.tgen_port1_mac = None + self.tgen_port2_mac = None + self.br_ex = self.scenario_cfg['options'].get('external_bridge', + 'br-ex') + self.vsperf_conf = self.scenario_cfg['options'].get('conf_file', None) + if self.vsperf_conf: + self.vsperf_conf = os.path.expanduser(self.vsperf_conf) + + self.moongen_helper = \ + self.scenario_cfg['options'].get('moongen_helper_file', None) + if self.moongen_helper: + self.moongen_helper = os.path.expanduser(self.moongen_helper) + + self.setup_script = self.scenario_cfg['options'].get('setup_script', + None) + if self.setup_script: + self.setup_script = os.path.expanduser(self.setup_script) + + self.test_params = self.scenario_cfg['options'].get('test-params', + None) + + def setup(self): + """scenario setup""" + vsperf = self.context_cfg['host'] + + task_id = self.scenario_cfg['task_id'] + context_number = task_id.split('-')[0] + self.tg_port1_nw = vsperf.get('name', 'demo') + \ + "-" + context_number + "-" + \ + self.scenario_cfg['options'].get('trafficgen_port1_nw', 'test2') + self.tg_port2_nw = vsperf.get('name', 'demo') + \ + "-" + context_number + "-" + \ + self.scenario_cfg['options'].get('trafficgen_port2_nw', 'test3') + + # copy vsperf conf to VM + self.client = ssh.SSH.from_node(vsperf, defaults={ + "user": "ubuntu", "password": "ubuntu" + }) + # traffic generation could last long + self.client.wait(timeout=1800) + + # copy script to host + self.client._put_file_shell(self.vsperf_conf, '~/vsperf.conf') + + self.client._put_file_shell( + self.moongen_helper, + '~/vswitchperf/tools/pkt_gen/moongen/moongen.py') + + # execute external setup script + if self.setup_script: + cmd = "%s setup" % (self.setup_script) + LOG.info("Execute setup script \"%s\"", cmd) + subprocess.call(cmd, shell=True) + + self.setup_done = True + + def dpdk_setup(self): + """dpdk setup""" + + # setup dpdk loopback in VM + self.testpmd_script = pkg_resources.resource_filename( + 'yardstick.benchmark.scenarios.networking', + VsperfDPDK.TESTPMD_SCRIPT) + + self.client._put_file_shell(self.testpmd_script, + '~/testpmd_vsperf.sh') + + # disable Address Space Layout Randomization (ASLR) + cmd = "echo 0 | sudo tee /proc/sys/kernel/randomize_va_space" + self.client.send_command(cmd) + + if not self._is_dpdk_setup(): + self.tgen_port1_ip = \ + utils.get_port_ip(self.client, self.tg_port1) + self.tgen_port1_mac = \ + utils.get_port_mac(self.client, self.tg_port1) + self.client.run("tee ~/.testpmd.ipaddr.port1 > /dev/null", + stdin=self.tgen_port1_ip) + self.client.run("tee ~/.testpmd.macaddr.port1 > /dev/null", + stdin=self.tgen_port1_mac) + self.tgen_port2_ip = \ + utils.get_port_ip(self.client, self.tg_port2) + self.tgen_port2_mac = \ + utils.get_port_mac(self.client, self.tg_port2) + self.client.run("tee ~/.testpmd.ipaddr.port2 > /dev/null", + stdin=self.tgen_port2_ip) + self.client.run("tee ~/.testpmd.macaddr.port2 > /dev/null", + stdin=self.tgen_port2_mac) + cmd = "ip link set %s down" % (self.tg_port1) + LOG.debug("Executing command: %s", cmd) + self.client.send_command(cmd) + cmd = "ip link set %s down" % (self.tg_port2) + LOG.debug("Executing command: %s", cmd) + self.client.send_command(cmd) + else: + cmd = "cat ~/.testpmd.macaddr.port1" + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + self.tgen_port1_mac = stdout + cmd = "cat ~/.testpmd.macaddr.port2" + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + self.tgen_port2_mac = stdout + + cmd = "screen -d -m sudo -E bash ~/testpmd_vsperf.sh %s %s" % \ + (self.moongen_port1_mac, self.moongen_port2_mac) + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + + time.sleep(1) + + self.dpdk_setup_done = True + + def _is_dpdk_setup(self): + """Is dpdk already setup in the host?""" + is_run = True + cmd = "ip a | grep %s 2>/dev/null" % (self.tg_port1) + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.client.execute(cmd) + if stdout: + is_run = False + return is_run + + def run(self, result): + """ execute the vsperf benchmark and return test results + within result dictionary + """ + + if not self.setup_done: + self.setup() + + # remove results from previous tests + self.client.execute("rm -rf /tmp/results*") + + # get vsperf options + options = self.scenario_cfg['options'] + test_params = [] + traffic_type = self.scenario_cfg['options'].\ + get("traffic_type", "rfc2544_throughput") + multistream = self.scenario_cfg['options'].get("multistream", 1) + + if not self.dpdk_setup_done: + self.dpdk_setup() + + if 'frame_size' in options: + test_params.append("%s=(%s,)" % ('TRAFFICGEN_PKT_SIZES', + options['frame_size'])) + + cmd = "openstack network show %s | grep segmentation_id | " \ + "cut -d '|' -f 3" % (self.tg_port1_nw) + LOG.debug("Executing command: %s", cmd) + tg_port1_vlan = subprocess.check_output(cmd, shell=True) + + cmd = "openstack network show %s | grep segmentation_id | " \ + "cut -d '|' -f 3" % (self.tg_port2_nw) + LOG.debug("Executing command: %s", cmd) + tg_port2_vlan = subprocess.check_output(cmd, shell=True) + + additional_params = \ + 'TRAFFIC={"traffic_type":"%s", "multistream":%d, ' \ + '"l2":{"srcmac":"{\'%s\',\'%s\'}", "dstmac":"{\'%s\',\'%s\'}"}, ' \ + '"vlan":{"enabled":"True", "id":"{%d,%d}"}}' \ + % (traffic_type, multistream, + self.moongen_port1_mac, self.moongen_port2_mac, + self.tgen_port1_mac, self.tgen_port2_mac, + int(tg_port1_vlan), int(tg_port2_vlan)) + + if 'test_params' in options: + test_params.append(options['test_params'] + additional_params) + + # filter empty parameters and escape quotes and double quotes + test_params = [tp.replace('"', '\\"').replace("'", "\\'") + for tp in test_params if tp] + + # Set password less access to MoonGen + cmd = "sshpass -p yardstick ssh-copy-id -o StrictHostKeyChecking=no " \ + "root@%s -p 22" % (self.moongen_host_ip) + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + + # execute vsperf + cmd = "source ~/vsperfenv/bin/activate ; cd vswitchperf ; " + cmd += "./vsperf --mode trafficgen " + if self.vsperf_conf: + cmd += "--conf-file ~/vsperf.conf " + cmd += "--test-params=\"%s\"" % (';'.join(test_params)) + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.client.execute(cmd) + + if status: + raise RuntimeError(stderr) + + # get test results + cmd = "cat /tmp/results*/result.csv" + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.client.execute(cmd) + + if status: + raise RuntimeError(stderr) + + # convert result.csv to JSON format + reader = csv.DictReader(stdout.split('\r\n')) + result.update(next(reader)) + result['nrFlows'] = multistream + + # sla check; go through all defined SLAs and check if values measured + # by VSPERF are higher then those defined by SLAs + if 'sla' in self.scenario_cfg and \ + 'metrics' in self.scenario_cfg['sla']: + for metric in self.scenario_cfg['sla']['metrics'].split(','): + assert metric in result, \ + '%s is not collected by VSPERF' % (metric) + assert metric in self.scenario_cfg['sla'], \ + '%s is not defined in SLA' % (metric) + vs_res = float(result[metric]) + sla_res = float(self.scenario_cfg['sla'][metric]) + assert vs_res >= sla_res, \ + 'VSPERF_%s(%f) < SLA_%s(%f)' % \ + (metric, vs_res, metric, sla_res) + + def teardown(self): + """cleanup after the test execution""" + + # execute external setup script + if self.setup_script: + cmd = "%s teardown" % (self.setup_script) + LOG.info("Execute setup script \"%s\"", cmd) + subprocess.call(cmd, shell=True) + + self.setup_done = False |