diff options
18 files changed, 1032 insertions, 96 deletions
diff --git a/api/resources/v1/env.py b/api/resources/v1/env.py index 4632f15fe..8943db3d1 100644 --- a/api/resources/v1/env.py +++ b/api/resources/v1/env.py @@ -65,16 +65,20 @@ class V1Env(ApiResource): client.pull(consts.GRAFANA_IMAGE, consts.GRAFANA_TAG) LOG.info('Createing grafana container') - self._create_grafana_container(client) + container = self._create_grafana_container(client) LOG.info('Grafana container is created') time.sleep(5) + container = client.inspect_container(container['Id']) + ip = container['NetworkSettings']['Networks']['bridge']['IPAddress'] + LOG.debug('container ip is: %s', ip) + LOG.info('Creating data source for grafana') - self._create_data_source() + self._create_data_source(ip) LOG.info('Creating dashboard for grafana') - self._create_dashboard() + self._create_dashboard(ip) self._update_task_status(task_id) LOG.info('Finished') @@ -82,8 +86,8 @@ class V1Env(ApiResource): self._update_task_error(task_id, str(e)) LOG.exception('Create grafana failed') - def _create_dashboard(self): - url = 'http://admin:admin@%s:3000/api/dashboards/db' % consts.GRAFANA_IP + def _create_dashboard(self, ip): + url = 'http://admin:admin@{}:{}/api/dashboards/db'.format(ip, consts.GRAFANA_PORT) path = os.path.join(consts.REPOS_DIR, 'dashboard', '*dashboard.json') for i in sorted(glob.iglob(path)): @@ -95,13 +99,21 @@ class V1Env(ApiResource): LOG.exception('Create dashboard %s failed', i) raise - def _create_data_source(self): - url = 'http://admin:admin@%s:3000/api/datasources' % consts.GRAFANA_IP + def _create_data_source(self, ip): + url = 'http://admin:admin@{}:{}/api/datasources'.format(ip, consts.GRAFANA_PORT) + influx_conf = utils.parse_ini_file(consts.CONF_FILE) + + try: + influx_url = influx_conf['dispatcher_influxdb']['target'] + except KeyError: + LOG.exception('influxdb url not set in yardstick.conf') + raise + data = { "name": "yardstick", "type": "influxdb", "access": "proxy", - "url": "http://%s:8086" % consts.INFLUXDB_IP, + "url": influx_url, "password": "root", "user": "root", "database": "yardstick", @@ -117,8 +129,8 @@ class V1Env(ApiResource): raise def _create_grafana_container(self, client): - ports = [3000] - port_bindings = {k: k for k in ports} + ports = [consts.GRAFANA_PORT] + port_bindings = {consts.GRAFANA_PORT: consts.GRAFANA_MAPPING_PORT} restart_policy = {"MaximumRetryCount": 0, "Name": "always"} host_config = client.create_host_config(port_bindings=port_bindings, restart_policy=restart_policy) @@ -133,6 +145,7 @@ class V1Env(ApiResource): host_config=host_config) LOG.info('Starting container') client.start(container) + return container def _check_image_exist(self, client, t): return any(t in a['RepoTags'][0] @@ -152,9 +165,6 @@ class V1Env(ApiResource): client = Client(base_url=consts.DOCKER_URL) try: - LOG.info('Changing output to influxdb') - self._change_output_to_influxdb() - LOG.info('Checking if influxdb image exist') if not self._check_image_exist(client, '%s:%s' % (consts.INFLUXDB_IMAGE, @@ -163,11 +173,18 @@ class V1Env(ApiResource): client.pull(consts.INFLUXDB_IMAGE, tag=consts.INFLUXDB_TAG) LOG.info('Createing influxdb container') - self._create_influxdb_container(client) + container = self._create_influxdb_container(client) LOG.info('Influxdb container is created') time.sleep(5) + container = client.inspect_container(container['Id']) + ip = container['NetworkSettings']['Networks']['bridge']['IPAddress'] + LOG.debug('container ip is: %s', ip) + + LOG.info('Changing output to influxdb') + self._change_output_to_influxdb(ip) + LOG.info('Config influxdb') self._config_influxdb() @@ -180,7 +197,7 @@ class V1Env(ApiResource): def _create_influxdb_container(self, client): - ports = [8083, 8086] + ports = [consts.INFLUXDB_DASHBOARD_PORT, consts.INFLUXDB_PORT] port_bindings = {k: k for k in ports} restart_policy = {"MaximumRetryCount": 0, "Name": "always"} host_config = client.create_host_config(port_bindings=port_bindings, @@ -196,6 +213,7 @@ class V1Env(ApiResource): host_config=host_config) LOG.info('Starting container') client.start(container) + return container def _config_influxdb(self): try: @@ -208,7 +226,7 @@ class V1Env(ApiResource): except Exception: LOG.exception('Config influxdb failed') - def _change_output_to_influxdb(self): + def _change_output_to_influxdb(self, ip): utils.makedirs(consts.CONF_DIR) parser = configparser.ConfigParser() @@ -218,7 +236,7 @@ class V1Env(ApiResource): LOG.info('Set dispatcher to influxdb') parser.set('DEFAULT', 'dispatcher', 'influxdb') parser.set('dispatcher_influxdb', 'target', - 'http://%s:8086' % consts.INFLUXDB_IP) + 'http://{}:{}'.format(ip, consts.INFLUXDB_PORT)) LOG.info('Writing to %s', consts.CONF_FILE) with open(consts.CONF_FILE, 'w') as f: diff --git a/tests/ci/scp_storperf_files.sh b/tests/ci/scp_storperf_files.sh index ffcc710cb..71306eb80 100644 --- a/tests/ci/scp_storperf_files.sh +++ b/tests/ci/scp_storperf_files.sh @@ -13,23 +13,25 @@ ssh_options="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" +scp_files(){ + export JUMP_HOST_IP + sshpass -p root scp 2>/dev/null $ssh_options ~/storperf_admin-rc \ + root@${JUMP_HOST_IP}:/root/ &> /dev/null + sshpass -p root scp 2>/dev/null $ssh_options /home/opnfv/repos/storperf/docker-compose/docker-compose.yaml \ + root@${JUMP_HOST_IP}:/root/ &> /dev/null +} + case "$NODE_NAME" in "huawei-pod1") JUMP_HOST_IP='192.168.10.6' + scp_files ;; "huawei-pod2") JUMP_HOST_IP='192.168.11.2' + scp_files ;; *) # no node name, exit - exit 1 + echo "storperf test case will not run on this pod, skipping scp files..." ;; esac -export JUMP_HOST_IP - -sshpass -p root scp 2>/dev/null $ssh_options ~/storperf_admin-rc \ - root@${JUMP_HOST_IP}:/root/ &> /dev/null -sshpass -p root scp 2>/dev/null $ssh_options /home/opnfv/repos/storperf/docker-compose/docker-compose.yaml \ - root@${JUMP_HOST_IP}:/root/ &> /dev/null -sshpass -p root scp 2>/dev/null $ssh_options /home/opnfv/repos/storperf/docker-compose/nginx.conf \ - root@${JUMP_HOST_IP}:/root/ &> /dev/null diff --git a/tests/ci/yardstick-verify b/tests/ci/yardstick-verify index 751cf65f3..149eef429 100755 --- a/tests/ci/yardstick-verify +++ b/tests/ci/yardstick-verify @@ -99,8 +99,8 @@ set -o pipefail install_storperf() { - # Install Storper on huawei-pod1 - if [ "$INSTALLER_TYPE" == "compass" ]; then + # Install Storper on huawei-pod1 and huawei-pod2 + if [ "$NODE_NAME" == "huawei-pod1" -o "$NODE_NAME" == "huawei-pod2" ]; then echo echo "========== Installing storperf ==========" @@ -114,8 +114,8 @@ install_storperf() remove_storperf() { - # remove Storper from huawei-pod1 - if [ "$INSTALLER_TYPE" == "compass" ]; then + # remove Storper from huawei-pod1 and huawei-pod2 + if [ "$NODE_NAME" == "huawei-pod1" -o "$NODE_NAME" == "huawei-pod2" ]; then echo echo "========== Removing storperf ==========" diff --git a/tests/opnfv/test_cases/opnfv_yardstick_tc008.yaml b/tests/opnfv/test_cases/opnfv_yardstick_tc008.yaml index 4c7fdab90..f5ccb255a 100644 --- a/tests/opnfv/test_cases/opnfv_yardstick_tc008.yaml +++ b/tests/opnfv/test_cases/opnfv_yardstick_tc008.yaml @@ -29,12 +29,21 @@ scenarios: packetsize: {{pkt_size}} number_of_ports: {{num_ports}} duration: 20 + # choose vnic name: default to eth0 + # vnic_name: 'ens3' + # turn on multiqueue inside VM + # multiqueue: True + # choose starting pps: default 1M; + # works with binary search runner Dynamictp to find max throughput per sla + # pps: 3000000 host: demeter.yardstick-TC008 target: poseidon.yardstick-TC008 runner: type: Iteration + # binary search runner + # type: Dynamictp iterations: 10 interval: 1 diff --git a/tests/unit/benchmark/scenarios/availability/test_util.py b/tests/unit/benchmark/scenarios/availability/test_util.py index bb0e6bc79..2e4fff417 100644 --- a/tests/unit/benchmark/scenarios/availability/test_util.py +++ b/tests/unit/benchmark/scenarios/availability/test_util.py @@ -19,6 +19,25 @@ from yardstick.benchmark.scenarios.availability import util @mock.patch('yardstick.benchmark.scenarios.availability.util.subprocess') class ExecuteShellTestCase(unittest.TestCase): + def setUp(self): + self.param_config = {'serviceName': '$serviceName', 'value': 1} + self.intermediate_variables = {'$serviceName': 'nova-api'} + self.std_output = '| id | 1 |' + self.cmd_config = {'cmd':'ls','param':'-a'} + + def test_util_build_command_shell(self,mock_subprocess): + result = util.build_shell_command(self.param_config, True, + self.intermediate_variables) + self.assertEqual("nova-api" in result, True) + + def test_read_stdout_item(self,mock_subprocess): + result = util.read_stdout_item(self.std_output,'id') + self.assertEquals('1',result) + + def test_buildshellparams(self,mock_subprocess): + result = util.buildshellparams(self.cmd_config,True) + self.assertEquals('/bin/bash -s {0} {1}', result) + def test__fun_execute_shell_command_successful(self, mock_subprocess): cmd = "env" mock_subprocess.check_output.return_value = (0, 'unittest') diff --git a/tests/unit/benchmark/scenarios/networking/test_pktgen.py b/tests/unit/benchmark/scenarios/networking/test_pktgen.py index d4eb1246f..2914c8e02 100644 --- a/tests/unit/benchmark/scenarios/networking/test_pktgen.py +++ b/tests/unit/benchmark/scenarios/networking/test_pktgen.py @@ -138,6 +138,7 @@ class PktgenTestCase(unittest.TestCase): p.run(result) expected_result = jsonutils.loads(sample_output) expected_result["packets_received"] = 149300 + expected_result["packetsize"] = 60 self.assertEqual(result, expected_result) def test_pktgen_successful_sla(self, mock_ssh): @@ -164,6 +165,7 @@ class PktgenTestCase(unittest.TestCase): p.run(result) expected_result = jsonutils.loads(sample_output) expected_result["packets_received"] = 149300 + expected_result["packetsize"] = 60 self.assertEqual(result, expected_result) def test_pktgen_unsuccessful_sla(self, mock_ssh): @@ -204,6 +206,538 @@ class PktgenTestCase(unittest.TestCase): mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') self.assertRaises(RuntimeError, p.run, result) + def test_pktgen_get_vnic_driver_name(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, 'ixgbevf', '') + + vnic_driver_name = p._get_vnic_driver_name() + self.assertEqual(vnic_driver_name, 'ixgbevf') + + def test_pktgen_unsuccessful_get_vnic_driver_name(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + self.assertRaises(RuntimeError, p._get_vnic_driver_name) + + def test_pktgen_get_sriov_queue_number(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '2', '') + + p.queue_number = p._get_sriov_queue_number() + self.assertEqual(p.queue_number, 2) + + def test_pktgen_unsuccessful_get_sriov_queue_number(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + self.assertRaises(RuntimeError, p._get_sriov_queue_number) + + def test_pktgen_get_available_queue_number(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '4', '') + + p._get_available_queue_number() + + mock_ssh.SSH.from_node().execute.assert_called_with( + "sudo ethtool -l eth0 | grep Combined | head -1 |" \ + "awk '{printf $2}'") + + def test_pktgen_unsuccessful_get_available_queue_number(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + self.assertRaises(RuntimeError, p._get_available_queue_number) + + def test_pktgen_get_usable_queue_number(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '1', '') + + p._get_usable_queue_number() + + mock_ssh.SSH.from_node().execute.assert_called_with( + "sudo ethtool -l eth0 | grep Combined | tail -1 |" \ + "awk '{printf $2}'") + + def test_pktgen_unsuccessful_get_usable_queue_number(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + self.assertRaises(RuntimeError, p._get_usable_queue_number) + + def test_pktgen_enable_ovs_multiqueue(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '4', '') + + mock_result1 = mock.Mock() + mock_result1.return_value = 1 + p._get_usable_queue_number = mock_result1 + + mock_result2 = mock.Mock() + mock_result2.return_value = 4 + p._get_available_queue_number = mock_result2 + + p.queue_number = p._enable_ovs_multiqueue() + self.assertEqual(p.queue_number, 4) + + def test_pktgen_enable_ovs_multiqueue_1q(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '1', '') + + mock_result1 = mock.Mock() + mock_result1.return_value = 1 + p._get_usable_queue_number = mock_result1 + + mock_result2 = mock.Mock() + mock_result2.return_value = 1 + p._get_available_queue_number = mock_result2 + + p.queue_number = p._enable_ovs_multiqueue() + self.assertEqual(p.queue_number, 1) + + def test_pktgen_unsuccessful_enable_ovs_multiqueue(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + mock_result1 = mock.Mock() + mock_result1.return_value = 1 + p._get_usable_queue_number = mock_result1 + + mock_result2 = mock.Mock() + mock_result2.return_value = 4 + p._get_available_queue_number = mock_result2 + + self.assertRaises(RuntimeError, p._enable_ovs_multiqueue) + + def test_pktgen_setup_irqmapping_ovs(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '10', '') + + p._setup_irqmapping_ovs(4) + + mock_ssh.SSH.from_node().execute.assert_called_with( + "echo 8 | sudo tee /proc/irq/10/smp_affinity") + + def test_pktgen_setup_irqmapping_ovs_1q(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '10', '') + + p._setup_irqmapping_ovs(1) + + mock_ssh.SSH.from_node().execute.assert_called_with( + "echo 1 | sudo tee /proc/irq/10/smp_affinity") + + def test_pktgen_unsuccessful_setup_irqmapping_ovs(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + self.assertRaises(RuntimeError, p._setup_irqmapping_ovs, 4) + + def test_pktgen_unsuccessful_setup_irqmapping_ovs_1q(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + self.assertRaises(RuntimeError, p._setup_irqmapping_ovs, 1) + + def test_pktgen_setup_irqmapping_sriov(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '10', '') + + p._setup_irqmapping_sriov(2) + + mock_ssh.SSH.from_node().execute.assert_called_with( + "echo 2 | sudo tee /proc/irq/10/smp_affinity") + + def test_pktgen_setup_irqmapping_sriov_1q(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '10', '') + + p._setup_irqmapping_sriov(1) + + mock_ssh.SSH.from_node().execute.assert_called_with( + "echo 1 | sudo tee /proc/irq/10/smp_affinity") + + def test_pktgen_unsuccessful_setup_irqmapping_sriov(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + self.assertRaises(RuntimeError, p._setup_irqmapping_sriov, 2) + + def test_pktgen_unsuccessful_setup_irqmapping_sriov_1q(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + self.assertRaises(RuntimeError, p._setup_irqmapping_sriov, 1) + + def test_pktgen_is_irqbalance_disabled(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + p._is_irqbalance_disabled() + + mock_ssh.SSH.from_node().execute.assert_called_with( + "grep ENABLED /etc/default/irqbalance") + + def test_pktgen_unsuccessful_is_irqbalance_disabled(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + self.assertRaises(RuntimeError, p._is_irqbalance_disabled) + + def test_pktgen_disable_irqbalance(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + p._disable_irqbalance() + + mock_ssh.SSH.from_node().execute.assert_called_with( + "sudo service irqbalance disable") + + def test_pktgen_unsuccessful_disable_irqbalance(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + self.assertRaises(RuntimeError, p._disable_irqbalance) + + def test_pktgen_multiqueue_setup_ovs(self, mock_ssh): + args = { + 'options': {'packetsize': 60, 'multiqueue': True}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '4', '') + + mock_result1 = mock.Mock() + mock_result1.return_value = False + p._is_irqbalance_disabled = mock_result1 + + mock_result2 = mock.Mock() + mock_result2.return_value = "virtio_net" + p._get_vnic_driver_name = mock_result2 + + mock_result3 = mock.Mock() + mock_result3.return_value = 1 + p._get_usable_queue_number = mock_result3 + + mock_result4 = mock.Mock() + mock_result4.return_value = 4 + p._get_available_queue_number = mock_result4 + + p.multiqueue_setup() + + self.assertEqual(p.queue_number, 4) + + def test_pktgen_multiqueue_setup_ovs_1q(self, mock_ssh): + args = { + 'options': {'packetsize': 60, 'multiqueue': True}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '1', '') + + mock_result1 = mock.Mock() + mock_result1.return_value = False + p._is_irqbalance_disabled = mock_result1 + + mock_result2 = mock.Mock() + mock_result2.return_value = "virtio_net" + p._get_vnic_driver_name = mock_result2 + + mock_result3 = mock.Mock() + mock_result3.return_value = 1 + p._get_usable_queue_number = mock_result3 + + mock_result4 = mock.Mock() + mock_result4.return_value = 1 + p._get_available_queue_number = mock_result4 + + p.multiqueue_setup() + + self.assertEqual(p.queue_number, 1) + + def test_pktgen_multiqueue_setup_sriov(self, mock_ssh): + args = { + 'options': {'packetsize': 60, 'multiqueue': True}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '2', '') + + mock_result1 = mock.Mock() + mock_result1.return_value = False + p._is_irqbalance_disabled = mock_result1 + + mock_result2 = mock.Mock() + mock_result2.return_value = "ixgbevf" + p._get_vnic_driver_name = mock_result2 + + p.multiqueue_setup() + + self.assertEqual(p.queue_number, 2) + + def test_pktgen_multiqueue_setup_sriov_1q(self, mock_ssh): + args = { + 'options': {'packetsize': 60, 'multiqueue': True}, + } + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (0, '1', '') + + mock_result1 = mock.Mock() + mock_result1.return_value = False + p._is_irqbalance_disabled = mock_result1 + + mock_result2 = mock.Mock() + mock_result2.return_value = "ixgbevf" + p._get_vnic_driver_name = mock_result2 + + p.multiqueue_setup() + + self.assertEqual(p.queue_number, 1) + + def test_pktgen_run_with_setup_done(self, mock_ssh): + args = { + 'options': {'packetsize': 60, 'number_of_ports': 10, 'duration': 20, 'multiqueue': True}, + 'sla': {'max_ppm': 1} + } + result = {} + p = pktgen.Pktgen(args, self.ctx) + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + p.setup_done = True + p.multiqueue_setup_done = True + + mock_iptables_result = mock.Mock() + mock_iptables_result.return_value = 149300 + p._iptables_get_result = mock_iptables_result + + sample_output = '{"packets_per_second": 9753, "errors": 0, \ + "packets_sent": 149300, "flows": 110}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + p.run(result) + expected_result = jsonutils.loads(sample_output) + expected_result["packets_received"] = 149300 + expected_result["packetsize"] = 60 + self.assertEqual(result, expected_result) + + def test_pktgen_run_with_ovs_multiqueque(self, mock_ssh): + args = { + 'options': {'packetsize': 60, 'number_of_ports': 10, 'duration': 20, 'multiqueue': True}, + 'sla': {'max_ppm': 1} + } + result = {} + + p = pktgen.Pktgen(args, self.ctx) + + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_result = mock.Mock() + mock_result.return_value = "virtio_net" + p._get_vnic_driver_name = mock_result + + mock_result1 = mock.Mock() + mock_result1.return_value = 1 + p._get_usable_queue_number = mock_result1 + + mock_result2 = mock.Mock() + mock_result2.return_value = 4 + p._get_available_queue_number = mock_result2 + + mock_result3 = mock.Mock() + mock_result3.return_value = 4 + p._enable_ovs_multiqueue = mock_result3 + + mock_result4 = mock.Mock() + p._setup_irqmapping_ovs = mock_result4 + + mock_iptables_result = mock.Mock() + mock_iptables_result.return_value = 149300 + p._iptables_get_result = mock_iptables_result + + sample_output = '{"packets_per_second": 9753, "errors": 0, \ + "packets_sent": 149300, "flows": 110}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + p.run(result) + expected_result = jsonutils.loads(sample_output) + expected_result["packets_received"] = 149300 + expected_result["packetsize"] = 60 + self.assertEqual(result, expected_result) + + def test_pktgen_run_with_sriov_multiqueque(self, mock_ssh): + args = { + 'options': {'packetsize': 60, 'number_of_ports': 10, 'duration': 20, 'multiqueue': True}, + 'sla': {'max_ppm': 1} + } + result = {} + + p = pktgen.Pktgen(args, self.ctx) + + p.server = mock_ssh.SSH.from_node() + p.client = mock_ssh.SSH.from_node() + + mock_result1 = mock.Mock() + mock_result1.return_value = "ixgbevf" + p._get_vnic_driver_name = mock_result1 + + mock_result2 = mock.Mock() + mock_result2.return_value = 2 + p._get_sriov_queue_number = mock_result2 + + mock_result3 = mock.Mock() + p._setup_irqmapping_sriov = mock_result3 + + mock_iptables_result = mock.Mock() + mock_iptables_result.return_value = 149300 + p._iptables_get_result = mock_iptables_result + + sample_output = '{"packets_per_second": 9753, "errors": 0, \ + "packets_sent": 149300, "flows": 110}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + p.run(result) + expected_result = jsonutils.loads(sample_output) + expected_result["packets_received"] = 149300 + expected_result["packetsize"] = 60 + self.assertEqual(result, expected_result) def main(): unittest.main() diff --git a/yardstick/benchmark/core/task.py b/yardstick/benchmark/core/task.py index b53d6446e..ede14b1c0 100644 --- a/yardstick/benchmark/core/task.py +++ b/yardstick/benchmark/core/task.py @@ -58,7 +58,12 @@ class Task(object): # pragma: no cover check_environment() - output_config = utils.parse_ini_file(config_file) + try: + output_config = utils.parse_ini_file(config_file) + except Exception: + # all error will be ignore, the default value is {} + output_config = {} + self._init_output_config(output_config) self._set_output_config(output_config, args.output_file) LOG.debug('Output configuration is: %s', output_config) diff --git a/yardstick/benchmark/scenarios/availability/actionplayers.py b/yardstick/benchmark/scenarios/availability/actionplayers.py index 420626413..c5e199ba6 100644 --- a/yardstick/benchmark/scenarios/availability/actionplayers.py +++ b/yardstick/benchmark/scenarios/availability/actionplayers.py @@ -29,8 +29,10 @@ class AttackerPlayer(ActionPlayer): class OperationPlayer(ActionPlayer): - def __init__(self, operation): + def __init__(self, operation, intermediate_variables): self.underlyingOperation = operation + self.underlyingOperation.intermediate_variables \ + = intermediate_variables def action(self): self.underlyingOperation.run() diff --git a/yardstick/benchmark/scenarios/availability/director.py b/yardstick/benchmark/scenarios/availability/director.py index e0d05ebf5..c9187c34d 100644 --- a/yardstick/benchmark/scenarios/availability/director.py +++ b/yardstick/benchmark/scenarios/availability/director.py @@ -65,7 +65,9 @@ class Director(object): self.resultCheckerMgr = baseresultchecker.ResultCheckerMgr() self.resultCheckerMgr.init_ResultChecker(result_check_cfgs, nodes) - def createActionPlayer(self, type, key): + def createActionPlayer(self, type, key, intermediate_variables=None): + if intermediate_variables is None: + intermediate_variables = {} LOG.debug( "the type of current action is %s, the key is %s", type, key) if type == ActionType.ATTACKER: @@ -76,7 +78,8 @@ class Director(object): return actionplayers.ResultCheckerPlayer( self.resultCheckerMgr[key]) if type == ActionType.OPERATION: - return actionplayers.OperationPlayer(self.operationMgr[key]) + return actionplayers.OperationPlayer(self.operationMgr[key], + intermediate_variables) LOG.debug("something run when creatactionplayer") def createActionRollbacker(self, type, key): diff --git a/yardstick/benchmark/scenarios/availability/operation/baseoperation.py b/yardstick/benchmark/scenarios/availability/operation/baseoperation.py index be286b8fd..88ca9e2bb 100644 --- a/yardstick/benchmark/scenarios/availability/operation/baseoperation.py +++ b/yardstick/benchmark/scenarios/availability/operation/baseoperation.py @@ -58,6 +58,7 @@ class BaseOperation(object): self.key = '' self._config = config self._context = context + self.intermediate_variables = {} @staticmethod def get_operation_cls(type): diff --git a/yardstick/benchmark/scenarios/availability/operation/operation_general.py b/yardstick/benchmark/scenarios/availability/operation/operation_general.py index 8fd387e47..af1ae7469 100644 --- a/yardstick/benchmark/scenarios/availability/operation/operation_general.py +++ b/yardstick/benchmark/scenarios/availability/operation/operation_general.py @@ -15,7 +15,8 @@ from yardstick.benchmark.scenarios.availability.operation.baseoperation \ import yardstick.ssh as ssh from yardstick.benchmark.scenarios.availability.util \ - import buildshellparams, execute_shell_command + import buildshellparams, execute_shell_command, \ + read_stdout_item, build_shell_command LOG = logging.getLogger(__name__) @@ -39,11 +40,7 @@ class GeneralOperaion(BaseOperation): self.operation_key = self._config['operation_key'] if "action_parameter" in self._config: - actionParameter = self._config['action_parameter'] - str = buildshellparams( - actionParameter, True if self.connection else False) - l = list(item for item in actionParameter.values()) - self.action_param = str.format(*l) + self.actionParameter_config = self._config['action_parameter'] if "rollback_parameter" in self._config: rollbackParameter = self._config['rollback_parameter'] @@ -61,6 +58,11 @@ class GeneralOperaion(BaseOperation): def run(self): if "action_parameter" in self._config: + self.action_param = \ + build_shell_command( + self.actionParameter_config, + True if self.connection else False, + self.intermediate_variables) if self.connection: with open(self.action_script, "r") as stdin_file: exit_status, stdout, stderr = self.connection.execute( @@ -83,6 +85,12 @@ class GeneralOperaion(BaseOperation): if exit_status == 0: LOG.debug("success,the operation's output is: %s", stdout) + if "return_parameter" in self._config: + returnParameter = self._config['return_parameter'] + for key, item in returnParameter.items(): + value = read_stdout_item(stdout, key) + LOG.debug("intermediate variables %s: %s", item, value) + self.intermediate_variables[item] = value else: LOG.error( "the operation's error, stdout:%s, stderr:%s", diff --git a/yardstick/benchmark/scenarios/availability/scenario_general.py b/yardstick/benchmark/scenarios/availability/scenario_general.py index 28bec8aff..17ad79f29 100644 --- a/yardstick/benchmark/scenarios/availability/scenario_general.py +++ b/yardstick/benchmark/scenarios/availability/scenario_general.py @@ -25,6 +25,7 @@ class ScenarioGeneral(base.Scenario): "scenario_cfg:%s context_cfg:%s", scenario_cfg, context_cfg) self.scenario_cfg = scenario_cfg self.context_cfg = context_cfg + self.intermediate_variables = {} def setup(self): self.director = Director(self.scenario_cfg, self.context_cfg) @@ -38,7 +39,8 @@ class ScenarioGeneral(base.Scenario): orderedSteps.index(step) + 1) try: actionPlayer = self.director.createActionPlayer( - step['actionType'], step['actionKey']) + step['actionType'], step['actionKey'], + self.intermediate_variables) actionPlayer.action() actionRollbacker = self.director.createActionRollbacker( step['actionType'], step['actionKey']) diff --git a/yardstick/benchmark/scenarios/availability/util.py b/yardstick/benchmark/scenarios/availability/util.py index eadbfa53b..6fef622bd 100644 --- a/yardstick/benchmark/scenarios/availability/util.py +++ b/yardstick/benchmark/scenarios/availability/util.py @@ -14,13 +14,8 @@ LOG = logging.getLogger(__name__) def buildshellparams(param, remote=True): - i = 0 - values = [] result = '/bin/bash -s' if remote else '' - for key in param.keys(): - values.append(param[key]) - result += " {%d}" % i - i = i + 1 + result += "".join(" {%d}" % i for i in range(len(param))) return result @@ -36,5 +31,29 @@ def execute_shell_command(command): output = traceback.format_exc() LOG.error("exec command '%s' error:\n ", command) LOG.error(traceback.format_exc()) - return exitcode, output + +PREFIX = '$' + + +def build_shell_command(param_config, remote=True, intermediate_variables=None): + param_template = '/bin/bash -s' if remote else '' + if intermediate_variables: + for key, val in param_config.items(): + if str(val).startswith(PREFIX): + try: + param_config[key] = intermediate_variables[val] + except KeyError: + pass + result = param_template + "".join(" {}".format(v) for v in param_config.values()) + LOG.debug("THE RESULT OF build_shell_command IS: %s", result) + return result + + +def read_stdout_item(stdout, key): + for item in stdout.splitlines(): + if key in item: + attributes = item.split("|") + if attributes[1].lstrip().startswith(key): + return attributes[2].strip() + return None diff --git a/yardstick/benchmark/scenarios/networking/iperf3.py b/yardstick/benchmark/scenarios/networking/iperf3.py index 3135af9bd..a3d273750 100644 --- a/yardstick/benchmark/scenarios/networking/iperf3.py +++ b/yardstick/benchmark/scenarios/networking/iperf3.py @@ -50,6 +50,17 @@ For more info see http://software.es.net/iperf type: int unit: bytes default: - + length - length of buffer to read or write, + (default 128 KB for TCP, 8 KB for UDP) + type: int + unit: k + default: - + window - set window size / socket buffer size + set TCP windows size. for UDP way to test, this will set to accept UDP + packet buffer size, limit the max size of acceptable data packet. + type: int + unit: k + default: - """ __scenario_type__ = "Iperf3" @@ -122,6 +133,12 @@ For more info see http://software.es.net/iperf elif "blockcount" in options: cmd += " --blockcount %d" % options["blockcount"] + if "length" in options: + cmd += " --length %s" % options["length"] + + if "window" in options: + cmd += " --window %s" % options["window"] + LOG.debug("Executing command: %s", cmd) status, stdout, stderr = self.host.execute(cmd) diff --git a/yardstick/benchmark/scenarios/networking/pktgen.py b/yardstick/benchmark/scenarios/networking/pktgen.py index e6aa7e5fb..8ca1ca60e 100644 --- a/yardstick/benchmark/scenarios/networking/pktgen.py +++ b/yardstick/benchmark/scenarios/networking/pktgen.py @@ -9,6 +9,7 @@ from __future__ import absolute_import from __future__ import print_function +import os import logging import pkg_resources @@ -19,6 +20,9 @@ from yardstick.benchmark.scenarios import base LOG = logging.getLogger(__name__) +VNIC_TYPE_LIST = ["ovs", "sriov"] +SRIOV_DRIVER_LIST = ["ixgbevf", "i40evf"] + class Pktgen(base.Scenario): """Execute pktgen between two hosts @@ -44,7 +48,11 @@ class Pktgen(base.Scenario): def __init__(self, scenario_cfg, context_cfg): self.scenario_cfg = scenario_cfg self.context_cfg = context_cfg + self.vnic_name = "eth0" + self.vnic_type = "ovs" + self.queue_number = 1 self.setup_done = False + self.multiqueue_setup_done = False def setup(self): """scenario setup""" @@ -67,6 +75,212 @@ class Pktgen(base.Scenario): self.setup_done = True + def multiqueue_setup(self): + # one time setup stuff + cmd = "sudo sysctl -w net.core.netdev_budget=3000" + self.server.send_command(cmd) + self.client.send_command(cmd) + + cmd = "sudo sysctl -w net.core.netdev_max_backlog=100000" + self.server.send_command(cmd) + self.client.send_command(cmd) + + """multiqueue setup""" + if not self._is_irqbalance_disabled(): + self._disable_irqbalance() + + vnic_driver_name = self._get_vnic_driver_name() + if vnic_driver_name in SRIOV_DRIVER_LIST: + self.vnic_type = "sriov" + + # one time setup stuff + cmd = "sudo ethtool -G %s rx 4096 tx 4096" % self.vnic_name + self.server.send_command(cmd) + self.client.send_command(cmd) + + self.queue_number = self._get_sriov_queue_number() + self._setup_irqmapping_sriov(self.queue_number) + else: + self.vnic_type = "ovs" + self.queue_number = self._enable_ovs_multiqueue() + self._setup_irqmapping_ovs(self.queue_number) + + self.multiqueue_setup_done = True + + def _get_vnic_driver_name(self): + cmd = "readlink /sys/class/net/%s/device/driver" % self.vnic_name + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.server.execute(cmd) + if status: + raise RuntimeError(stderr) + return os.path.basename(stdout.strip()) + + def _is_irqbalance_disabled(self): + """Did we disable irqbalance already in the guest?""" + is_disabled = False + cmd = "grep ENABLED /etc/default/irqbalance" + status, stdout, stderr = self.server.execute(cmd) + if status: + raise RuntimeError(stderr) + if "0" in stdout: + is_disabled = True + + return is_disabled + + def _disable_irqbalance(self): + cmd = "sudo sed -i -e 's/ENABLED=\"1\"/ENABLED=\"0\"/g' " \ + "/etc/default/irqbalance" + status, stdout, stderr = self.server.execute(cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + + cmd = "sudo service irqbalance stop" + status, stdout, stderr = self.server.execute(cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + + cmd = "sudo service irqbalance disable" + status, stdout, stderr = self.server.execute(cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + + def _setup_irqmapping_ovs(self, queue_number): + cmd = "grep 'virtio0-input.0' /proc/interrupts |" \ + "awk '{match($0,/ +[0-9]+/)} " \ + "{print substr($1,RSTART,RLENGTH-1)}'" + status, stdout, stderr = self.server.execute(cmd) + if status: + raise RuntimeError(stderr) + + cmd = "echo 1 | sudo tee /proc/irq/%s/smp_affinity" % (int(stdout)) + status, stdout, stderr = self.server.execute(cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + + cmd = "grep 'virtio0-output.0' /proc/interrupts |" \ + "awk '{match($0,/ +[0-9]+/)} " \ + "{print substr($1,RSTART,RLENGTH-1)}'" + status, stdout, stderr = self.server.execute(cmd) + if status: + raise RuntimeError(stderr) + + cmd = "echo 1 | sudo tee /proc/irq/%s/smp_affinity" % (int(stdout)) + status, stdout, stderr = self.server.execute(cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + + if queue_number == 1: + return + + for i in range(1, queue_number): + cmd = "grep 'virtio0-input.%s' /proc/interrupts |" \ + "awk '{match($0,/ +[0-9]+/)} " \ + "{print substr($1,RSTART,RLENGTH-1)}'" % (i) + status, stdout, stderr = self.server.execute(cmd) + if status: + raise RuntimeError(stderr) + + cmd = "echo %s | sudo tee /proc/irq/%s/smp_affinity" \ + % (1 << i, int(stdout)) + status, stdout, stderr = self.server.execute(cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + + cmd = "grep 'virtio0-output.%s' /proc/interrupts |" \ + "awk '{match($0,/ +[0-9]+/)} " \ + "{print substr($1,RSTART,RLENGTH-1)}'" % (i) + status, stdout, stderr = self.server.execute(cmd) + if status: + raise RuntimeError(stderr) + + cmd = "echo %s | sudo tee /proc/irq/%s/smp_affinity" \ + % (1 << i, int(stdout)) + status, stdout, stderr = self.server.execute(cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + + def _setup_irqmapping_sriov(self, queue_number): + cmd = "grep '%s-TxRx-0' /proc/interrupts |" \ + "awk '{match($0,/ +[0-9]+/)} " \ + "{print substr($1,RSTART,RLENGTH-1)}'" % self.vnic_name + status, stdout, stderr = self.server.execute(cmd) + if status: + raise RuntimeError(stderr) + + cmd = "echo 1 | sudo tee /proc/irq/%s/smp_affinity" % (int(stdout)) + status, stdout, stderr = self.server.execute(cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + + if queue_number == 1: + return + + for i in range(1, queue_number): + cmd = "grep '%s-TxRx-%s' /proc/interrupts |" \ + "awk '{match($0,/ +[0-9]+/)} " \ + "{print substr($1,RSTART,RLENGTH-1)}'" % (self.vnic_name, i) + status, stdout, stderr = self.server.execute(cmd) + if status: + raise RuntimeError(stderr) + + cmd = "echo %s | sudo tee /proc/irq/%s/smp_affinity" \ + % (1 << i, int(stdout)) + status, stdout, stderr = self.server.execute(cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + + def _get_sriov_queue_number(self): + """Get queue number from server as both VMs are the same""" + cmd = "grep %s-TxRx- /proc/interrupts | wc -l" % self.vnic_name + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.server.execute(cmd) + if status: + raise RuntimeError(stderr) + return int(stdout) + + def _get_available_queue_number(self): + """Get queue number from client as both VMs are the same""" + cmd = "sudo ethtool -l %s | grep Combined | head -1 |" \ + "awk '{printf $2}'" % self.vnic_name + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.server.execute(cmd) + if status: + raise RuntimeError(stderr) + return int(stdout) + + def _get_usable_queue_number(self): + """Get queue number from client as both VMs are the same""" + cmd = "sudo ethtool -l %s | grep Combined | tail -1 |" \ + "awk '{printf $2}'" % self.vnic_name + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.server.execute(cmd) + if status: + raise RuntimeError(stderr) + return int(stdout) + + def _enable_ovs_multiqueue(self): + available_queue_number = self._get_available_queue_number() + usable_queue_number = self._get_usable_queue_number() + if available_queue_number > 1 and \ + available_queue_number != usable_queue_number: + cmd = "sudo ethtool -L %s combined %s" % \ + (self.vnic_name, available_queue_number) + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.server.execute(cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + return available_queue_number + def _iptables_setup(self): """Setup iptables on server to monitor for received packets""" cmd = "sudo iptables -F; " \ @@ -99,6 +313,14 @@ class Pktgen(base.Scenario): options = self.scenario_cfg['options'] packetsize = options.get("packetsize", 60) self.number_of_ports = options.get("number_of_ports", 10) + self.vnic_name = options.get("vnic_name", "eth0") + ovs_dpdk = options.get("ovs_dpdk", False) + pps = options.get("pps", 1000000) + multiqueue = options.get("multiqueue", False) + + if multiqueue and not self.multiqueue_setup_done: + self.multiqueue_setup() + # if run by a duration runner duration_time = self.scenario_cfg["runner"].get("duration", None) \ if "runner" in self.scenario_cfg else None @@ -114,8 +336,18 @@ class Pktgen(base.Scenario): self._iptables_setup() - cmd = "sudo bash pktgen.sh %s %s %s %s" \ - % (ipaddr, self.number_of_ports, packetsize, duration) + queue_number = self.queue_number + + # For native OVS, half of vCPUs are used by vhost kernel threads + # hence set the queue_number to half number of vCPUs + # e.g. set queue_number to 2 if there are 4 vCPUs + if self.vnic_type == "ovs" and not ovs_dpdk and self.queue_number > 1: + queue_number = self.queue_number / 2 + + cmd = "sudo bash pktgen.sh %s %s %s %s %s %s" \ + % (ipaddr, self.number_of_ports, packetsize, + duration, queue_number, pps) + LOG.debug("Executing command: %s", cmd) status, stdout, stderr = self.client.execute(cmd) @@ -131,12 +363,15 @@ class Pktgen(base.Scenario): sent = result['packets_sent'] received = result['packets_received'] ppm = 1000000 * (sent - received) / sent + # if ppm is 1, then 11 out of 10 million is no pass + ppm += (sent - received) % sent > 0 + LOG.debug("Lost packets %d - Lost ppm %d", (sent - received), ppm) sla_max_ppm = int(self.scenario_cfg["sla"]["max_ppm"]) assert ppm <= sla_max_ppm, "ppm %d > sla_max_ppm %d; " \ % (ppm, sla_max_ppm) -def _test(): +def _test(): # pragma: no cover """internal test function""" key_filename = pkg_resources.resource_filename('yardstick.resources', 'files/yardstick_key') @@ -165,6 +400,5 @@ def _test(): p.run(result) print(result) - if __name__ == '__main__': _test() diff --git a/yardstick/benchmark/scenarios/networking/pktgen_benchmark.bash b/yardstick/benchmark/scenarios/networking/pktgen_benchmark.bash index 4224c5abf..e338a1b09 100644 --- a/yardstick/benchmark/scenarios/networking/pktgen_benchmark.bash +++ b/yardstick/benchmark/scenarios/networking/pktgen_benchmark.bash @@ -16,6 +16,8 @@ DST_IP=$1 # destination IP address NUM_PORTS=$2 # number of source ports PKT_SIZE=$3 # packet size DURATION=$4 # test duration (seconds) +TRXQUEUE=$5 # number of RX/TX queues to use +PPS=$6 # packets per second to send # Configuration UDP_SRC_MIN=1000 # UDP source port min @@ -37,62 +39,100 @@ pgset() fi } +# remove all devices from thread +pgclean() +{ + COUNTER=0 + while [ ${COUNTER} -lt ${TRXQUEUE} ]; do + # + # Thread commands + # + + PGDEV=/proc/net/pktgen/kpktgend_${COUNTER} + + # Remove all devices from this thread + pgset "rem_device_all" + let COUNTER=COUNTER+1 + done +} + # configure pktgen (see pktgen doc for details) pgconfig() { - # - # Thread commands - # + pps=$(( PPS / TRXQUEUE )) + COUNTER=0 + while [ ${COUNTER} -lt ${TRXQUEUE} ]; do + # + # Thread commands + # - PGDEV=/proc/net/pktgen/kpktgend_0 + PGDEV=/proc/net/pktgen/kpktgend_${COUNTER} - # Remove all devices from this thread - pgset "rem_device_all" + # Add device to thread + pgset "add_device $DEV@${COUNTER}" - # Add device to thread - pgset "add_device $DEV" + # + # Device commands + # - # - # Device commands - # + PGDEV=/proc/net/pktgen/$DEV@${COUNTER} - PGDEV=/proc/net/pktgen/$DEV + # 0 means continious sends untill explicitly stopped + pgset "count 0" - # 0 means continious sends untill explicitly stopped - pgset "count 0" + # set pps count to test with an explicit number. if 0 will try with bandwidth + if [ ${pps} -gt 0 ] + then + pgset "ratep ${pps}" + fi - # use single SKB for all transmits - pgset "clone_skb 0" + pgset "clone_skb 10" - # packet size, NIC adds 4 bytes CRC - pgset "pkt_size $PKT_SIZE" + # use different queue per thread + pgset "queue_map_min ${COUNTER}" + pgset "queue_map_max ${COUNTER}" - # random address within the min-max range - pgset "flag IPDST_RND UDPSRC_RND UDPDST_RND" + # packet size, NIC adds 4 bytes CRC + pgset "pkt_size $PKT_SIZE" - # destination IP - pgset "dst_min $DST_IP" - pgset "dst_max $DST_IP" + # random address within the min-max range + pgset "flag UDPDST_RND" + pgset "flag UDPSRC_RND" + pgset "flag IPDST_RND" - # destination MAC address - pgset "dst_mac $MAC" + # destination IP + pgset "dst_min $DST_IP" + pgset "dst_max $DST_IP" + + # destination MAC address + pgset "dst_mac $MAC" + + # source UDP port range + pgset "udp_src_min $UDP_SRC_MIN" + pgset "udp_src_max $UDP_SRC_MAX" - # source UDP port range - pgset "udp_src_min $UDP_SRC_MIN" - pgset "udp_src_max $UDP_SRC_MAX" + # destination UDP port range + pgset "udp_dst_min $UDP_DST_MIN" + pgset "udp_dst_max $UDP_DST_MAX" - # destination UDP port range - pgset "udp_dst_min $UDP_DST_MIN" - pgset "udp_dst_max $UDP_DST_MAX" + let COUNTER=COUNTER+1 + + done } # run pktgen pgrun() { - # Time to run, result can be vieved in /proc/net/pktgen/$DEV + # Time to run, result can be viewed in /proc/net/pktgen/$DEV PGDEV=/proc/net/pktgen/pgctrl # Will hang, Ctrl-C or SIGINT to stop pgset "start" start + + COUNTER=0 + while [ ${COUNTER} -lt ${TRXQUEUE} ]; do + taskset -c ${COUNTER} kpktgend_${COUNTER} + let COUNTER=COUNTER+1 + done } # run pktgen for ${DURATION} seconds @@ -111,19 +151,28 @@ run_test() # write the result to stdout in json format output_json() { - sent=$(awk '/^Result:/{print $5}' <$PGDEV) - pps=$(awk 'match($0,/'\([0-9]+\)pps'/, a) {print a[1]}' <$PGDEV) - errors=$(awk '/errors:/{print $5}' <$PGDEV) + sent=0 + result_pps=0 + errors=0 + PGDEV=/proc/net/pktgen/$DEV@ + COUNTER=0 + while [ ${COUNTER} -lt ${TRXQUEUE} ]; do + sent=$(($sent + $(awk '/^Result:/{print $5}' <$PGDEV${COUNTER}))) + result_pps=$(($result_pps + $(awk 'match($0,/'\([0-9]+\)pps'/, a) {print a[1]}' <$PGDEV${COUNTER}))) + errors=$(($errors + $(awk '/errors:/{print $5}' <$PGDEV${COUNTER}))) + let COUNTER=COUNTER+1 + done flows=$(( NUM_PORTS * (NUM_PORTS + 1) )) - echo { '"packets_sent"':$sent , '"packets_per_second"':$pps, '"flows"':$flows, '"errors"':$errors } + echo '{ "packets_sent"':${sent} , '"packets_per_second"':${result_pps}, '"flows"':${flows}, '"errors"':${errors} '}' } # main entry main() { modprobe pktgen + pgclean ping -c 3 $DST_IP >/dev/null @@ -137,16 +186,20 @@ main() pgconfig # run the test - run_test >/dev/null + run_test - PGDEV=/proc/net/pktgen/$DEV + PGDEV=/proc/net/pktgen/$DEV@ # check result - result=$(cat $PGDEV | fgrep "Result: OK:") - if [ "$result" = "" ]; then - cat $PGDEV | fgrep Result: >/dev/stderr - exit 1 - fi + COUNTER=0 + while [ ${COUNTER} -lt ${TRXQUEUE} ]; do + result=$(cat $PGDEV${COUNTER} | fgrep "Result: OK:") + if [ "$result" = "" ]; then + cat $PGDEV${COUNTER} | fgrep Result: >/dev/stderr + exit 1 + fi + let COUNTER=COUNTER+1 + done # output result output_json diff --git a/yardstick/common/constants.py b/yardstick/common/constants.py index 69485a4e4..8e8114fbb 100644 --- a/yardstick/common/constants.py +++ b/yardstick/common/constants.py @@ -77,6 +77,7 @@ INFLUXDB_PASS = get_param('influxdb.password', 'root') INFLUXDB_DB_NAME = get_param('influxdb.db_name', 'yardstick') INFLUXDB_IMAGE = get_param('influxdb.image', 'tutum/influxdb') INFLUXDB_TAG = get_param('influxdb.tag', '0.13') +INFLUXDB_DASHBOARD_PORT = 8083 # grafana GRAFANA_IP = get_param('grafana.ip', SERVER_IP) @@ -85,6 +86,7 @@ GRAFANA_USER = get_param('grafana.username', 'admin') GRAFANA_PASS = get_param('grafana.password', 'admin') GRAFANA_IMAGE = get_param('grafana.image', 'grafana/grafana') GRAFANA_TAG = get_param('grafana.tag', '3.1.1') +GRAFANA_MAPPING_PORT = 1948 # api API_PORT = 5000 diff --git a/yardstick/common/utils.py b/yardstick/common/utils.py index 92bb7b7d3..7a64b8ca2 100644 --- a/yardstick/common/utils.py +++ b/yardstick/common/utils.py @@ -172,7 +172,15 @@ def write_file(path, data, mode='w'): def parse_ini_file(path): parser = configparser.ConfigParser() - parser.read(path) + + try: + files = parser.read(path) + except configparser.MissingSectionHeaderError: + logger.exception('invalid file type') + raise + else: + if not files: + raise RuntimeError('file not exist') try: default = {k: v for k, v in parser.items('DEFAULT')} |