aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ansible/nsb_setup.yml1
-rw-r--r--docs/testing/user/userguide/04-installation.rst138
-rwxr-xr-xtests/ci/yardstick-verify4
-rw-r--r--tests/opnfv/test_cases/opnfv_yardstick_tc011.yaml12
-rw-r--r--tests/opnfv/test_cases/opnfv_yardstick_tc063.yaml4
-rw-r--r--tests/opnfv/test_suites/opnfv_k8-ovn-lb-noha_daily.yaml18
-rw-r--r--tests/unit/benchmark/contexts/test_heat.py38
-rw-r--r--tests/unit/benchmark/core/test_task.py52
-rw-r--r--tests/unit/network_services/vnf_generic/vnf/test_iniparser.py52
-rw-r--r--tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py14
-rw-r--r--tests/unit/test_ssh.py2
-rw-r--r--yardstick/benchmark/contexts/heat.py25
-rw-r--r--yardstick/benchmark/contexts/model.py15
-rw-r--r--yardstick/benchmark/core/task.py10
-rw-r--r--yardstick/benchmark/scenarios/compute/qemu_migrate.py2
-rw-r--r--yardstick/benchmark/scenarios/compute/qemu_migrate_benchmark.bash6
-rw-r--r--yardstick/benchmark/scenarios/networking/vnf_generic.py29
-rw-r--r--yardstick/network_services/vnf_generic/vnf/iniparser.py299
-rw-r--r--yardstick/ssh.py3
19 files changed, 499 insertions, 225 deletions
diff --git a/ansible/nsb_setup.yml b/ansible/nsb_setup.yml
index e79ccabea..78cf87ec7 100644
--- a/ansible/nsb_setup.yml
+++ b/ansible/nsb_setup.yml
@@ -48,7 +48,6 @@
recreate: yes
state: started
restart_policy: always
- network_mode: host
privileged: True
interactive: True
volumes:
diff --git a/docs/testing/user/userguide/04-installation.rst b/docs/testing/user/userguide/04-installation.rst
index 37e4ba599..cb4f31434 100644
--- a/docs/testing/user/userguide/04-installation.rst
+++ b/docs/testing/user/userguide/04-installation.rst
@@ -443,6 +443,141 @@ Deploy InfluxDB and Grafana directly in Ubuntu (**Todo**)
-----------------------------------------------------------
+Yardstick common CLI
+--------------------
+
+list test cases
+>>>>>>>>>>>>>>>
+**yardstick testcase list**
+
+This command line would list all test cases in yardstick.
+It would show like below::
+
+ +---------------------------------------------------------------------------------------
+ | Testcase Name | Description
+ +---------------------------------------------------------------------------------------
+ | opnfv_yardstick_tc001 | Measure network throughput using pktgen
+ | opnfv_yardstick_tc002 | measure network latency using ping
+ | opnfv_yardstick_tc005 | Measure Storage IOPS, throughput and latency using fio.
+ | opnfv_yardstick_tc006 | Measure volume storage IOPS, throughput and latency using fio.
+ | opnfv_yardstick_tc008 | Measure network throughput and packet loss using Pktgen
+ | opnfv_yardstick_tc009 | Measure network throughput and packet loss using pktgen
+ | opnfv_yardstick_tc010 | measure memory read latency using lmbench.
+ | opnfv_yardstick_tc011 | Measure packet delay variation (jitter) using iperf3.
+ | opnfv_yardstick_tc012 | Measure memory read and write bandwidth using lmbench.
+ | opnfv_yardstick_tc014 | Measure Processing speed using unixbench.
+ | opnfv_yardstick_tc019 | Sample test case for the HA of controller node service.
+ ...
+ +---------------------------------------------------------------------------------------
+show a test case config file
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+Take opnfv_yardstick_tc002 for an example. This test case measure network latency.
+You just need to type in **yardstick testcase show opnfv_yardstick_tc002**, and the console
+would show the config yaml of this test case::
+ ##############################################################################
+ # Copyright (c) 2017 kristian.hunt@gmail.com and others.
+ #
+ # 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
+ ##############################################################################
+ ---
+
+ schema: "yardstick:task:0.1"
+ description: >
+ Yardstick TC002 config file;
+ measure network latency using ping;
+
+ {% set image = image or "cirros-0.3.5" %}
+
+ {% set provider = provider or none %}
+ {% set physical_network = physical_network or 'physnet1' %}
+ {% set segmentation_id = segmentation_id or none %}
+ {% set packetsize = packetsize or 100 %}
+
+ scenarios:
+ {% for i in range(2) %}
+ -
+ type: Ping
+ options:
+ packetsize: {{packetsize}}
+ host: athena.demo
+ target: ares.demo
+
+ runner:
+ type: Duration
+ duration: 60
+ interval: 10
+
+ sla:
+ max_rtt: 10
+ action: monitor
+ {% endfor %}
+
+ context:
+ name: demo
+ image: {{image}}
+ flavor: yardstick-flavor
+ user: cirros
+
+ placement_groups:
+ pgrp1:
+ policy: "availability"
+
+ servers:
+ athena:
+ floating_ip: true
+ placement: "pgrp1"
+ ares:
+ placement: "pgrp1"
+
+ networks:
+ test:
+ cidr: '10.0.1.0/24'
+ {% if provider == "vlan" %}
+ provider: {{provider}}
+ physical_network: {{physical_network}}å
+ {% if segmentation_id %}
+ segmentation_id: {{segmentation_id}}
+ {% endif %}
+ {% endif %}
+
+start a task to run yardstick test case
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+If you want run a test case, then you need to use **yardstick task start <test_case_path>**
+this command support some parameters as below:
+
++---------------------+--------------------------------------------------+
+| Parameters | Detail |
++=====================+==================================================+
+| -d | show debug log of yardstick running |
+| | |
++---------------------+--------------------------------------------------+
+| --task-args | If you want to customize test case parameters, |
+| | use "--task-args" to pass the value. The format |
+| | is a json string with parameter key-value pair. |
+| | |
++---------------------+--------------------------------------------------+
+| --task-args-file | If you want to use yardstick |
+| | env prepare command(or |
+| | related API) to load the |
++---------------------+--------------------------------------------------+
+| --parse-only | |
+| | |
+| | |
++---------------------+--------------------------------------------------+
+| --output-file \ | Specify where to output the log. if not pass, |
+| OUTPUT_FILE_PATH | the default value is |
+| | "/tmp/yardstick/yardstick.log" |
+| | |
++---------------------+--------------------------------------------------+
+| --suite \ | run a test suite, TEST_SUITE_PATH speciy where |
+| TEST_SUITE_PATH | the test suite locates |
+| | |
++---------------------+--------------------------------------------------+
+
+
Run Yardstick in a local environment
------------------------------------
@@ -512,5 +647,4 @@ yaml file and add test cases, constraint or task arguments if necessary.
Proxy Support (**Todo**)
----------------------------
-
+--------------------------- \ No newline at end of file
diff --git a/tests/ci/yardstick-verify b/tests/ci/yardstick-verify
index ca8a0b27a..f3e7a49e3 100755
--- a/tests/ci/yardstick-verify
+++ b/tests/ci/yardstick-verify
@@ -87,7 +87,9 @@ error_exit()
exitcode=$rc
fi
- cleanup
+ if [[ "${DEPLOY_SCENARIO:0:2}" == 'os' ]];then
+ source "${YARDSTICK_REPO_DIR}/tests/ci/clean_images.sh"
+ fi
echo "Exiting with RC=$exitcode"
diff --git a/tests/opnfv/test_cases/opnfv_yardstick_tc011.yaml b/tests/opnfv/test_cases/opnfv_yardstick_tc011.yaml
index eef1a7a62..b2077d59e 100644
--- a/tests/opnfv/test_cases/opnfv_yardstick_tc011.yaml
+++ b/tests/opnfv/test_cases/opnfv_yardstick_tc011.yaml
@@ -13,6 +13,10 @@ description: >
Yardstick TC011 config file;
Measure packet delay variation (jitter) using iperf3.
+{% set udp = udp or "udp" %}
+{% set bandwidth = bandwidth or "20m" %}
+{% set length = length or "8K" %}
+{% set window = window or 29200 %}
{% set provider = provider or none %}
{% set physical_network = physical_network or 'physnet1' %}
{% set segmentation_id = segmentation_id or none %}
@@ -21,10 +25,10 @@ scenarios:
-
type: Iperf3
options:
- udp: udp
- bandwidth: 20m
- length: 8K
- window: 29200
+ udp: {{udp}}
+ bandwidth: {{bandwidth}}
+ length: {{length}}
+ window: {{window}}
host: zeus.demo
target: hera.demo
diff --git a/tests/opnfv/test_cases/opnfv_yardstick_tc063.yaml b/tests/opnfv/test_cases/opnfv_yardstick_tc063.yaml
index 48288ef62..254c214d2 100644
--- a/tests/opnfv/test_cases/opnfv_yardstick_tc063.yaml
+++ b/tests/opnfv/test_cases/opnfv_yardstick_tc063.yaml
@@ -13,8 +13,8 @@ description: >
Yardstick TC063 config file;
Measure disk size, block size and disk utilization using fdisk and iostat.
-{% set host = host or "node5.yardstick-TC063" %}
{% set file = file or '/etc/yardstick/pod.yaml' %}
+{% set node = node or "node5" %}
scenarios:
-
@@ -22,7 +22,7 @@ scenarios:
options:
test_type: "disk_size"
- host: {{host}}
+ host: {{node}}.yardstick-TC063
runner:
type: Iteration
diff --git a/tests/opnfv/test_suites/opnfv_k8-ovn-lb-noha_daily.yaml b/tests/opnfv/test_suites/opnfv_k8-ovn-lb-noha_daily.yaml
new file mode 100644
index 000000000..207030073
--- /dev/null
+++ b/tests/opnfv/test_suites/opnfv_k8-ovn-lb-noha_daily.yaml
@@ -0,0 +1,18 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others.
+#
+# 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
+##############################################################################
+---
+# k8 ovn lb noha daily task suite
+
+schema: "yardstick:suite:0.1"
+
+name: "k8-ovn-lb-noha"
+test_cases_dir: "tests/opnfv/test_cases/"
+test_cases:
+-
+ file_name: opnfv_yardstick_tc080.yaml
diff --git a/tests/unit/benchmark/contexts/test_heat.py b/tests/unit/benchmark/contexts/test_heat.py
index cc0c7bc8e..582d9ab99 100644
--- a/tests/unit/benchmark/contexts/test_heat.py
+++ b/tests/unit/benchmark/contexts/test_heat.py
@@ -13,7 +13,6 @@
from __future__ import absolute_import
-import ipaddress
import logging
import os
import unittest
@@ -147,30 +146,6 @@ class HeatContextTestCase(unittest.TestCase):
self.test_context.user = 'foo'
@mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
- @mock.patch('yardstick.benchmark.contexts.heat.get_neutron_client')
- def test_attrs_get(self, mock_neutron, mock_template):
- image, flavor, user = expected_tuple = 'foo1', 'foo2', 'foo3'
- self.assertNotEqual(self.test_context.image, image)
- self.assertNotEqual(self.test_context.flavor, flavor)
- self.assertNotEqual(self.test_context.user, user)
- self.test_context._image = image
- self.test_context._flavor = flavor
- self.test_context._user = user
- attr_tuple = self.test_context.image, self.test_context.flavor, self.test_context.user
- self.assertEqual(attr_tuple, expected_tuple)
-
- @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
- def test_attrs_set_negative(self, mock_template):
- with self.assertRaises(AttributeError):
- self.test_context.image = 'foo'
-
- with self.assertRaises(AttributeError):
- self.test_context.flavor = 'foo'
-
- with self.assertRaises(AttributeError):
- self.test_context.user = 'foo'
-
- @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
def test_deploy(self, mock_template):
self.test_context.name = 'foo'
self.test_context.template_file = '/bar/baz/some-heat-file'
@@ -210,8 +185,8 @@ class HeatContextTestCase(unittest.TestCase):
}
server = mock.MagicMock()
server.ports = OrderedDict([
- ('a', {'stack_name': 'b'}),
- ('c', {'stack_name': 'd'}),
+ ('a', {'stack_name': 'b', 'port': 'port_a'}),
+ ('c', {'stack_name': 'd', 'port': 'port_c'}),
])
expected = {
@@ -231,7 +206,7 @@ class HeatContextTestCase(unittest.TestCase):
self.test_context.add_server_port(server)
self.assertEqual(server.private_ip, '10.20.30.45')
self.assertEqual(len(server.interfaces), 2)
- self.assertDictEqual(server.interfaces['a'], expected)
+ self.assertDictEqual(server.interfaces['port_a'], expected)
@mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
def test_undeploy(self, mock_template):
@@ -472,7 +447,6 @@ class HeatContextTestCase(unittest.TestCase):
network2 = mock.MagicMock()
network2.name = 'net_2'
- network2.vld_id = 'vld999'
network2.segmentation_id = 'seg45'
network2.network_type = 'type_b'
network2.physical_network = 'virt'
@@ -488,16 +462,15 @@ class HeatContextTestCase(unittest.TestCase):
attr_name = {}
self.assertIsNone(self.test_context._get_network(attr_name))
- attr_name = {'vld_id': 'vld777'}
+ attr_name = {'network_type': 'nosuch'}
self.assertIsNone(self.test_context._get_network(attr_name))
attr_name = 'vld777'
self.assertIsNone(self.test_context._get_network(attr_name))
- attr_name = {'vld_id': 'vld999'}
+ attr_name = {'segmentation_id': 'seg45'}
expected = {
"name": 'net_2',
- "vld_id": 'vld999',
"segmentation_id": 'seg45',
"network_type": 'type_b',
"physical_network": 'virt',
@@ -508,7 +481,6 @@ class HeatContextTestCase(unittest.TestCase):
attr_name = 'a'
expected = {
"name": 'net_1',
- "vld_id": 'vld111',
"segmentation_id": 'seg54',
"network_type": 'type_a',
"physical_network": 'phys',
diff --git a/tests/unit/benchmark/core/test_task.py b/tests/unit/benchmark/core/test_task.py
index 14027e43c..e3917b5b2 100644
--- a/tests/unit/benchmark/core/test_task.py
+++ b/tests/unit/benchmark/core/test_task.py
@@ -66,31 +66,27 @@ class TaskTestCase(unittest.TestCase):
nodes = {
'node1': {
'interfaces': {
- 'eth0': {
- 'name': 'mgmt',
+ 'mgmt': {
+ 'network_name': 'mgmt',
},
- 'eth1': {
- 'name': 'external',
- 'vld_id': '23',
+ 'xe0': {
+ 'network_name': 'private_0',
},
- 'eth10': {
- 'name': 'internal',
- 'vld_id': '55',
+ 'xe1': {
+ 'network_name': 'public_0',
},
},
},
'node2': {
'interfaces': {
- 'eth4': {
- 'name': 'mgmt',
+ 'mgmt': {
+ 'network_name': 'mgmt',
},
- 'eth2': {
- 'name': 'external',
- 'vld_id': '32',
+ 'private_0': {
+ 'network_name': 'private_0',
},
- 'eth11': {
- 'name': 'internal',
- 'vld_id': '55',
+ 'public_0': {
+ 'network_name': 'public_0',
},
},
},
@@ -99,30 +95,30 @@ class TaskTestCase(unittest.TestCase):
mock_context.get_network.side_effect = iter([
None,
{
- 'name': 'a',
- 'network_type': 'private',
+ 'name': 'mgmt',
+ 'network_type': 'flat',
},
{},
{
- 'name': 'b',
- 'vld_id': 'y',
+ 'name': 'private_0',
'subnet_cidr': '10.20.0.0/16',
},
{
- 'name': 'c',
- 'vld_id': 'x',
+ 'name': 'public_0',
+ 'segmentation_id': '1001',
},
{
- 'name': 'd',
- 'vld_id': 'w',
+ 'name': 'private_1',
},
])
- # once for each vld_id in the nodes dict
- expected_get_network_calls = 4
+ # one for each interface
+ expected_get_network_calls = 6
expected = {
- 'a': {'name': 'a', 'network_type': 'private'},
- 'b': {'name': 'b', 'vld_id': 'y', 'subnet_cidr': '10.20.0.0/16'},
+ 'mgmt': {'name': 'mgmt', 'network_type': 'flat'},
+ 'private_0': {'name': 'private_0', 'subnet_cidr': '10.20.0.0/16'},
+ 'private_1': {'name': 'private_1'},
+ 'public_0': {'name': 'public_0', 'segmentation_id': '1001'},
}
networks = task.get_networks_from_nodes(nodes)
diff --git a/tests/unit/network_services/vnf_generic/vnf/test_iniparser.py b/tests/unit/network_services/vnf_generic/vnf/test_iniparser.py
index 15d6adea1..1ad8df9c6 100644
--- a/tests/unit/network_services/vnf_generic/vnf/test_iniparser.py
+++ b/tests/unit/network_services/vnf_generic/vnf/test_iniparser.py
@@ -28,6 +28,7 @@ stl_patch.start()
if stl_patch:
from yardstick.network_services.vnf_generic.vnf.iniparser import ParseError
+ from yardstick.network_services.vnf_generic.vnf.iniparser import LineParser
from yardstick.network_services.vnf_generic.vnf.iniparser import BaseParser
from yardstick.network_services.vnf_generic.vnf.iniparser import ConfigParser
@@ -38,16 +39,18 @@ key1=value1
list1: value2
value3
value4
-key2="double quote value"
key3='single quote value' ; comment here
key4=
-[section2]
+[section2] ; comment with #2 other symbol
# here is a comment line
list2: value5
-key with no value
+key with no value # mixed comment ; symbols
; another comment line
key5=
+
+[section1] ; reopen a section!
+key2="double quote value"
"""
PARSE_TEXT_2 = """\
@@ -86,6 +89,17 @@ class TestParseError(unittest.TestCase):
self.assertEqual(str(error), "at line 2, a: 'c'")
+class TestLineParser(unittest.TestCase):
+
+ def test___repr__(self):
+ line_parser = LineParser('', 101)
+ self.assertIsNotNone(repr(line_parser))
+
+ def test_error_invalid_assignment(self):
+ line_parser = LineParser('', 101)
+ self.assertIsNotNone(line_parser.error_invalid_assignment())
+
+
class TestBaseParser(unittest.TestCase):
@staticmethod
@@ -96,23 +110,26 @@ class TestBaseParser(unittest.TestCase):
return internal_open
- @mock.patch('yardstick.network_services.vnf_generic.vnf.iniparser.open')
- def test_parse_none(self, mock_open):
- mock_open.side_effect = self.make_open('')
-
+ def test_parse(self):
parser = BaseParser()
+ parser.parse()
- parser.parse([])
+ def test_parse_empty_string(self):
+ parser = BaseParser()
+ self.assertIsNone(parser.parse(''))
def test_not_implemented_methods(self):
parser = BaseParser()
with self.assertRaises(NotImplementedError):
- parser.assignment('key', 'value')
+ parser.assignment('key', 'value', LineParser('', 100))
with self.assertRaises(NotImplementedError):
parser.new_section('section')
+ with self.assertRaises(NotImplementedError):
+ parser.comment('comment')
+
class TestConfigParser(unittest.TestCase):
@@ -128,18 +145,25 @@ class TestConfigParser(unittest.TestCase):
def test_parse(self, mock_open):
mock_open.side_effect = self.make_open(PARSE_TEXT_1)
- config_parser = ConfigParser('my_file', [])
+ existing_data = [['section0', [['key0', 'value0']]]]
+ config_parser = ConfigParser('my_file', existing_data)
config_parser.parse()
expected = [
[
+ 'section0',
+ [
+ ['key0', 'value0'],
+ ],
+ ],
+ [
'section1',
[
['key1', 'value1'],
['list1', 'value2\nvalue3\nvalue4'],
- ['key2', 'double quote value'],
['key3', 'single quote value'],
['key4', ''],
+ ['key2', 'double quote value'],
],
],
[
@@ -153,12 +177,16 @@ class TestConfigParser(unittest.TestCase):
]
self.assertEqual(config_parser.sections, expected)
+ self.assertIsNotNone(config_parser.find_section('section1'))
+ self.assertIsNone(config_parser.find_section('section3'))
+ self.assertEqual(config_parser.find_section_index('section1'), 1)
+ self.assertEqual(config_parser.find_section_index('section3'), -1)
@mock.patch('yardstick.network_services.vnf_generic.vnf.iniparser.open')
def test_parse_2(self, mock_open):
mock_open.side_effect = self.make_open(PARSE_TEXT_2)
- config_parser = ConfigParser('my_file', [])
+ config_parser = ConfigParser('my_file')
config_parser.parse()
expected = [
diff --git a/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py b/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py
index 0264facf5..983c21e61 100644
--- a/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py
+++ b/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py
@@ -186,6 +186,7 @@ class TestVnfSshHelper(unittest.TestCase):
@mock.patch('yardstick.ssh.paramiko')
def test_upload_config_file(self, mock_paramiko):
ssh_helper = VnfSshHelper(self.VNFD_0['mgmt-interface'], 'my/bin/path')
+ ssh_helper._run = mock.MagicMock()
self.assertFalse(ssh_helper.is_connected)
cfg_file = ssh_helper.upload_config_file('my/prefix', 'my content')
@@ -227,6 +228,7 @@ class TestVnfSshHelper(unittest.TestCase):
@mock.patch('yardstick.ssh.provision_tool')
def test_provision_tool(self, mock_provision_tool, mock_paramiko):
ssh_helper = VnfSshHelper(self.VNFD_0['mgmt-interface'], 'my/bin/path')
+ ssh_helper._run = mock.MagicMock()
self.assertFalse(ssh_helper.is_connected)
ssh_helper.provision_tool()
@@ -412,6 +414,7 @@ class TestDpdkVnfSetupEnvHelper(unittest.TestCase):
'virtual-interface': {
'dst_mac': '00:00:00:00:00:03',
'vpci': '0000:05:00.0',
+ 'dpdk_port_num': '0',
'driver': 'i40e',
'local_ip': '152.16.100.19',
'type': 'PCI-PASSTHROUGH',
@@ -419,7 +422,9 @@ class TestDpdkVnfSetupEnvHelper(unittest.TestCase):
'dpdk_port_num': '0',
'bandwidth': '10 Gbps',
'dst_ip': '152.16.100.20',
- 'local_mac': '00:00:00:00:00:01'
+ 'local_mac': '00:00:00:00:00:01',
+ 'vld_id': 'private_0',
+ 'ifname': 'xe0',
},
'vnfd-connection-point-ref': 'xe0',
'name': 'xe0'
@@ -428,6 +433,7 @@ class TestDpdkVnfSetupEnvHelper(unittest.TestCase):
'virtual-interface': {
'dst_mac': '00:00:00:00:00:04',
'vpci': '0000:05:00.1',
+ 'dpdk_port_num': '1',
'driver': 'ixgbe',
'local_ip': '152.16.40.19',
'type': 'PCI-PASSTHROUGH',
@@ -435,7 +441,9 @@ class TestDpdkVnfSetupEnvHelper(unittest.TestCase):
'dpdk_port_num': '1',
'bandwidth': '10 Gbps',
'dst_ip': '152.16.40.20',
- 'local_mac': '00:00:00:00:00:02'
+ 'local_mac': '00:00:00:00:00:02',
+ 'vld_id': 'public_0',
+ 'ifname': 'xe1',
},
'vnfd-connection-point-ref': 'xe1',
'name': 'xe1'
@@ -600,8 +608,8 @@ class TestDpdkVnfSetupEnvHelper(unittest.TestCase):
dpdk_setup_helper = DpdkVnfSetupEnvHelper(vnfd_helper, ssh_helper, scenario_helper)
dpdk_setup_helper.CFG_CONFIG = 'config'
dpdk_setup_helper.CFG_SCRIPT = 'script'
- dpdk_setup_helper.all_ports = [3, 4, 5]
dpdk_setup_helper.pipeline_kwargs = {}
+ dpdk_setup_helper.all_ports = [0, 1, 2]
expected = {
'cfg_file': 'config',
diff --git a/tests/unit/test_ssh.py b/tests/unit/test_ssh.py
index 27ed68c7b..b298c745b 100644
--- a/tests/unit/test_ssh.py
+++ b/tests/unit/test_ssh.py
@@ -527,7 +527,7 @@ class TestAutoConnectSSH(unittest.TestCase):
'key_filename': None,
'password': None,
'name': None,
- 'wait': False,
+ 'wait': True,
}
result = auto_connect_ssh._make_dict()
self.assertDictEqual(result, expected)
diff --git a/yardstick/benchmark/contexts/heat.py b/yardstick/benchmark/contexts/heat.py
index 0802c9a64..d2309cc10 100644
--- a/yardstick/benchmark/contexts/heat.py
+++ b/yardstick/benchmark/contexts/heat.py
@@ -329,19 +329,24 @@ class HeatContext(Context):
def add_server_port(self, server):
# TODO(hafe) can only handle one internal network for now
- port = next(iter(server.ports.values()))
- server.private_ip = self.stack.outputs[port["stack_name"]]
+ # use private ip from first port
+ private_port = next(iter(server.ports.values()))
+ server.private_ip = self.stack.outputs[private_port["stack_name"]]
server.interfaces = {}
for network_name, port in server.ports.items():
- server.interfaces[network_name] = self.make_interface_dict(
- network_name, port['stack_name'], self.stack.outputs)
+ # port['port'] is either port name from mapping or default network_name
+ server.interfaces[port['port']] = self.make_interface_dict(network_name, port['port'],
+ port['stack_name'],
+ self.stack.outputs)
- def make_interface_dict(self, network_name, stack_name, outputs):
+ def make_interface_dict(self, network_name, port, stack_name, outputs):
private_ip = outputs[stack_name]
mac_address = outputs[h_join(stack_name, "mac_address")]
+ # these are attributes of the network, not the port
output_subnet_cidr = outputs[h_join(self.name, network_name,
'subnet', 'cidr')]
+ # these are attributes of the network, not the port
output_subnet_gateway = outputs[h_join(self.name, network_name,
'subnet', 'gateway_ip')]
@@ -355,6 +360,7 @@ class HeatContext(Context):
"mac_address": mac_address,
"device_id": outputs[h_join(stack_name, "device_id")],
"network_id": outputs[h_join(stack_name, "network_id")],
+ # this should be == vld_id for NSB tests
"network_name": network_name,
# to match vnf_generic
"local_mac": mac_address,
@@ -438,9 +444,11 @@ class HeatContext(Context):
network = self.networks.get(attr_name, None)
else:
- # Don't generalize too much Just support vld_id
- vld_id = attr_name.get('vld_id', {})
- network_iter = (n for n in self.networks.values() if n.vld_id == vld_id)
+ # Only take the first key, value
+ key, value = next(iter(attr_name.items()), (None, None))
+ if key is None:
+ return None
+ network_iter = (n for n in self.networks.values() if getattr(n, key) == value)
network = next(network_iter, None)
if network is None:
@@ -448,7 +456,6 @@ class HeatContext(Context):
result = {
"name": network.name,
- "vld_id": network.vld_id,
"segmentation_id": network.segmentation_id,
"network_type": network.network_type,
"physical_network": network.physical_network,
diff --git a/yardstick/benchmark/contexts/model.py b/yardstick/benchmark/contexts/model.py
index 0b8197ce9..da2b74e1c 100644
--- a/yardstick/benchmark/contexts/model.py
+++ b/yardstick/benchmark/contexts/model.py
@@ -208,6 +208,7 @@ class Server(Object): # pragma: no cover
self.instances = attrs["instances"]
# dict with key network name, each item is a dict with port name and ip
+ self.network_ports = attrs.get("network_ports", {})
self.ports = {}
self.floating_ip = None
@@ -253,8 +254,18 @@ class Server(Object): # pragma: no cover
"""adds to the template one server and corresponding resources"""
port_name_list = []
for network in networks:
- port_name = server_name + "-" + network.name + "-port"
- self.ports[network.name] = {"stack_name": port_name}
+ # if explicit mapping skip unused networks
+ if self.network_ports:
+ try:
+ port = self.network_ports[network.name]
+ except KeyError:
+ # no port for this network
+ continue
+ # otherwise add a port for every network with port name as network name
+ else:
+ port = network.name
+ port_name = "{0}-{1}-port".format(server_name, port)
+ self.ports[network.name] = {"stack_name": port_name, "port": port}
# we can't use secgroups if port_security_enabled is False
if network.port_security_enabled is False:
sec_group_id = None
diff --git a/yardstick/benchmark/core/task.py b/yardstick/benchmark/core/task.py
index 9fc097d42..a8447e2cf 100644
--- a/yardstick/benchmark/core/task.py
+++ b/yardstick/benchmark/core/task.py
@@ -376,8 +376,6 @@ class Task(object): # pragma: no cover
target_attr: either a name for a server created by yardstick or a dict
with attribute name mapping when using external heat templates
"""
- host = None
- target = None
for context in self.contexts:
if context.__context_type__ != "Heat":
continue
@@ -626,11 +624,11 @@ def get_networks_from_nodes(nodes):
continue
interfaces = node.get('interfaces', {})
for interface in interfaces.values():
- vld_id = interface.get('vld_id')
- # mgmt network doesn't have vld_id
- if not vld_id:
+ # vld_id is network_name
+ network_name = interface.get('network_name')
+ if not network_name:
continue
- network = Context.get_network({"vld_id": vld_id})
+ network = Context.get_network(network_name)
if network:
networks[network['name']] = network
return networks
diff --git a/yardstick/benchmark/scenarios/compute/qemu_migrate.py b/yardstick/benchmark/scenarios/compute/qemu_migrate.py
index 6c0446bb7..6cfedc17a 100644
--- a/yardstick/benchmark/scenarios/compute/qemu_migrate.py
+++ b/yardstick/benchmark/scenarios/compute/qemu_migrate.py
@@ -106,7 +106,7 @@ class QemuMigrate(base.Scenario):
cmd_args = " %s %s %s %s %s %s" %\
(smp, qmp_sock_src, qmp_sock_dst, incoming_ip,
migrate_to_port, max_down_time)
- cmd = "bash migrate_benchmark.sh %s" % (cmd_args)
+ cmd = "bash qemu_migrate_benchmark.sh %s" % (cmd_args)
LOG.debug("Executing command: %s", cmd)
status, stdout, stderr = self.host.execute(cmd)
if status:
diff --git a/yardstick/benchmark/scenarios/compute/qemu_migrate_benchmark.bash b/yardstick/benchmark/scenarios/compute/qemu_migrate_benchmark.bash
index 552098103..d9a440c89 100644
--- a/yardstick/benchmark/scenarios/compute/qemu_migrate_benchmark.bash
+++ b/yardstick/benchmark/scenarios/compute/qemu_migrate_benchmark.bash
@@ -14,6 +14,7 @@ set -e
# Commandline arguments
src=$2
+dst=$3
dst_ip=$4
migrate_to_port=$5
max_down_time=$6
@@ -22,7 +23,6 @@ OUTPUT_FILE=/tmp/output-qemu.log
do_migrate()
{
-# local src=`echo $OPTIONS | cut -d ':' -f 2 | cut -d ',' -f 1`
echo "info status" | nc -U $src
# with no speed limit
echo "migrate_set_speed 0" |nc -U $src
@@ -45,7 +45,9 @@ output_qemu()
# print detail information
echo "info migrate" | nc -U $src
echo "quit" | nc -U $src
+ echo "quit" | nc -u $dst
sleep 5
+ echo "Migration executed successfully"
} > $OUTPUT_FILE
@@ -64,5 +66,7 @@ echo -e "{ \
main()
{
do_migrate
+ output_qemu
+ output_json
}
main
diff --git a/yardstick/benchmark/scenarios/networking/vnf_generic.py b/yardstick/benchmark/scenarios/networking/vnf_generic.py
index f7b2915a2..0e6ceab6e 100644
--- a/yardstick/benchmark/scenarios/networking/vnf_generic.py
+++ b/yardstick/benchmark/scenarios/networking/vnf_generic.py
@@ -216,7 +216,26 @@ class NetworkServiceTestCase(base.Scenario):
@staticmethod
def get_vld_networks(networks):
- return {n['vld_id']: n for n in networks.values()}
+ # network name is vld_id
+ vld_map = {}
+ for name, n in networks.items():
+ try:
+ vld_map[n['vld_id']] = n
+ except KeyError:
+ vld_map[name] = n
+ return vld_map
+
+ @staticmethod
+ def find_node_if(nodes, name, if_name, vld_id):
+ try:
+ # check for xe0, xe1
+ intf = nodes[name]["interfaces"][if_name]
+ except KeyError:
+ # if not xe0, then maybe vld_id, private_0, public_0
+ # pop it and re-insert with the correct name from topology
+ intf = nodes[name]["interfaces"].pop(vld_id)
+ nodes[name]["interfaces"][if_name] = intf
+ return intf
def _resolve_topology(self):
for vld in self.topology["vld"]:
@@ -234,8 +253,8 @@ class NetworkServiceTestCase(base.Scenario):
try:
nodes = self.context_cfg["nodes"]
- node0_if = nodes[node0_name]["interfaces"][node0_if_name]
- node1_if = nodes[node1_name]["interfaces"][node1_if_name]
+ node0_if = self.find_node_if(nodes, node0_name, node0_if_name, vld["id"])
+ node1_if = self.find_node_if(nodes, node1_name, node1_if_name, vld["id"])
# names so we can do reverse lookups
node0_if["ifname"] = node0_if_name
@@ -285,8 +304,8 @@ class NetworkServiceTestCase(base.Scenario):
node1_if_name = node1_data["vnfd-connection-point-ref"]
nodes = self.context_cfg["nodes"]
- node0_if = nodes[node0_name]["interfaces"][node0_if_name]
- node1_if = nodes[node1_name]["interfaces"][node1_if_name]
+ node0_if = self.find_node_if(nodes, node0_name, node0_if_name, vld["id"])
+ node1_if = self.find_node_if(nodes, node1_name, node1_if_name, vld["id"])
# add peer interface dict, but remove circular link
# TODO: don't waste memory
diff --git a/yardstick/network_services/vnf_generic/vnf/iniparser.py b/yardstick/network_services/vnf_generic/vnf/iniparser.py
index 70e24de5b..98256e08a 100644
--- a/yardstick/network_services/vnf_generic/vnf/iniparser.py
+++ b/yardstick/network_services/vnf_generic/vnf/iniparser.py
@@ -14,163 +14,236 @@
class ParseError(Exception):
- def __init__(self, message, lineno, line):
+
+ def __init__(self, message, line_no, line):
self.msg = message
self.line = line
- self.lineno = lineno
+ self.line_no = line_no
def __str__(self):
- return 'at line %d, %s: %r' % (self.lineno, self.msg, self.line)
+ return 'at line %d, %s: %r' % (self.line_no, self.msg, self.line)
+
+
+class SectionParseError(ParseError):
+
+ pass
+
+
+class LineParser(object):
+
+ PARSE_EXC = ParseError
+
+ @staticmethod
+ def strip_key_value(key, value):
+ key = key.strip()
+ value = value.strip()
+ if value and value[0] == value[-1] and value.startswith(('"', "'")):
+ value = value[1:-1]
+ return key, [value]
+
+ def __init__(self, line, line_no):
+ super(LineParser, self).__init__()
+ self.line = line
+ self.line_no = line_no
+ self.continuation = line != line.lstrip()
+ semi_active, _, semi_comment = line.partition(';')
+ pound_active, _, pound_comment = line.partition('#')
+ if not semi_comment and not pound_comment:
+ self.active = line.strip()
+ self.comment = ''
+ elif len(semi_comment) > len(pound_comment):
+ self.active = semi_active.strip()
+ self.comment = semi_comment.strip()
+ else:
+ self.active = pound_active.strip()
+ self.comment = pound_comment.strip()
+ self._section_name = None
+
+ def __repr__(self):
+ template = "line %d: active '%s' comment '%s'\n%s"
+ return template % (self.line_no, self.active, self.comment, self.line)
+
+ @property
+ def section_name(self):
+ if self._section_name is None:
+ if not self.active.startswith('['):
+ raise self.error_no_section_start_bracket()
+ if not self.active.endswith(']'):
+ raise self.error_no_section_end_bracket()
+ self._section_name = ''
+ if self.active:
+ self._section_name = self.active[1:-1]
+ if not self._section_name:
+ raise self.error_no_section_name()
+ return self._section_name
+
+ def is_active_line(self):
+ return bool(self.active)
+
+ def is_continuation(self):
+ return self.continuation
+
+ def split_key_value(self):
+ for sep in ['=', ':']:
+ words = self.active.split(sep, 1)
+ try:
+ return self.strip_key_value(*words)
+ except TypeError:
+ pass
+
+ return self.active.rstrip(), '@'
+
+ def error_invalid_assignment(self):
+ return self.PARSE_EXC("No ':' or '=' found in assignment", self.line_no, self.line)
+
+ def error_empty_key(self):
+ return self.PARSE_EXC('Key cannot be empty', self.line_no, self.line)
+
+ def error_unexpected_continuation(self):
+ return self.PARSE_EXC('Unexpected continuation line', self.line_no, self.line)
+
+ def error_no_section_start_bracket(self):
+ return SectionParseError('Invalid section (must start with [)', self.line_no, self.line)
+
+ def error_no_section_end_bracket(self):
+ return self.PARSE_EXC('Invalid section (must end with ])', self.line_no, self.line)
+
+ def error_no_section_name(self):
+ return self.PARSE_EXC('Empty section name', self.line_no, self.line)
class BaseParser(object):
- lineno = 0
- parse_exc = ParseError
- def _assignment(self, key, value):
- self.assignment(key, value)
- return None, []
+ def parse(self, data=None):
+ if data is not None:
+ return self._parse(data.splitlines())
- def _get_section(self, line):
- if not line.endswith(']'):
- return self.error_no_section_end_bracket(line)
- if len(line) <= 2:
- return self.error_no_section_name(line)
+ def _next_key_value(self, line_parser, key, value):
+ self.comment(line_parser)
- return line[1:-1]
+ if not line_parser.is_active_line():
+ # Blank line, ends multi-line values
+ if key:
+ key, value = self.assignment(key, value, line_parser)
+ return key, value
- def _split_key_value(self, line):
- colon = line.find(':')
- equal = line.find('=')
- if colon < 0 and equal < 0:
- return line.strip(), '@'
+ if line_parser.is_continuation():
+ # Continuation of previous assignment
+ if key is None:
+ raise line_parser.error_unexpected_continuation()
- if colon < 0 or (equal >= 0 and equal < colon):
- key, value = line[:equal], line[equal + 1:]
+ value.append(line_parser.active.lstrip())
+ return key, value
+
+ if key:
+ # Flush previous assignment, if any
+ key, value = self.assignment(key, value, line_parser)
+
+ try:
+ # Section start
+ self.new_section(line_parser)
+ except SectionParseError:
+ pass
else:
- key, value = line[:colon], line[colon + 1:]
+ return key, value
- value = value.strip()
- if value and value[0] == value[-1] and value.startswith(("\"", "'")):
- value = value[1:-1]
- return key.strip(), [value]
+ key, value = line_parser.split_key_value()
+ if not key:
+ raise line_parser.error_empty_key()
+ return key, value
- def parse(self, lineiter):
+ def _parse(self, line_iter):
key = None
value = []
- for line in lineiter:
- self.lineno += 1
-
- line = line.rstrip()
- lines = line.split(';')
- line = lines[0]
- if not line:
- # Blank line, ends multi-line values
- if key:
- key, value = self._assignment(key, value)
- continue
- elif line.startswith((' ', '\t')):
- # Continuation of previous assignment
- if key is None:
- self.error_unexpected_continuation(line)
- else:
- value.append(line.lstrip())
- continue
-
- if key:
- # Flush previous assignment, if any
- key, value = self._assignment(key, value)
-
- if line.startswith('['):
- # Section start
- section = self._get_section(line)
- if section:
- self.new_section(section)
- elif line.startswith(('#', ';')):
- self.comment(line[1:].lstrip())
- else:
- key, value = self._split_key_value(line)
- if not key:
- return self.error_empty_key(line)
+ parse_iter = (LineParser(line, line_no) for line_no, line in enumerate(line_iter))
+ for line_parser in parse_iter:
+ key, value = self._next_key_value(line_parser, key, value)
if key:
# Flush previous assignment, if any
- self._assignment(key, value)
+ self.assignment(key, value, LineParser('EOF', -1))
- def assignment(self, key, value):
+ def _assignment(self, key, value, line_parser):
"""Called when a full assignment is parsed."""
raise NotImplementedError()
- def new_section(self, section):
+ def assignment(self, key, value, line_parser):
+ self._assignment(key, value, line_parser)
+ return None, []
+
+ def new_section(self, line_parser):
"""Called when a new section is started."""
raise NotImplementedError()
- def comment(self, comment):
+ def comment(self, line_parser):
"""Called when a comment is parsed."""
- pass
-
- def error_invalid_assignment(self, line):
- raise self.parse_exc("No ':' or '=' found in assignment",
- self.lineno, line)
-
- def error_empty_key(self, line):
- raise self.parse_exc('Key cannot be empty', self.lineno, line)
-
- def error_unexpected_continuation(self, line):
- raise self.parse_exc('Unexpected continuation line',
- self.lineno, line)
-
- def error_no_section_end_bracket(self, line):
- raise self.parse_exc('Invalid section (must end with ])',
- self.lineno, line)
-
- def error_no_section_name(self, line):
- raise self.parse_exc('Empty section name', self.lineno, line)
+ raise NotImplementedError()
class ConfigParser(BaseParser):
"""Parses a single config file, populating 'sections' to look like:
- {'DEFAULT': {'key': [value, ...], ...},
- ...}
+ [
+ [
+ 'section1',
+ [
+ ['key1', 'value1\nvalue2'],
+ ['key2', 'value3\nvalue4'],
+ ],
+ ],
+ [
+ 'section2',
+ [
+ ['key3', 'value5\nvalue6'],
+ ],
+ ],
+ ]
"""
- def __init__(self, filename, sections):
+ def __init__(self, filename, sections=None):
super(ConfigParser, self).__init__()
self.filename = filename
- self.sections = sections
+ if sections is not None:
+ self.sections = sections
+ else:
+ self.sections = []
+ self.section_name = None
self.section = None
- def parse(self):
- with open(self.filename) as f:
- return super(ConfigParser, self).parse(f)
+ def parse(self, data=None):
+ if not data:
+ data = self.filename
+ with open(data) as f:
+ return self._parse(f)
- def find_section(self, sections, section):
- return next((i for i, sect in enumerate(sections) if sect == section), -1)
+ def __iter__(self):
+ return iter(self.sections)
- def new_section(self, section):
- self.section = section
- index = self.find_section(self.sections, section)
- if index == -1:
- self.sections.append([section, []])
+ def find_section_index(self, section_name):
+ return next((i for i, (name, value) in enumerate(self) if name == section_name), -1)
- def assignment(self, key, value):
- if not self.section:
- raise self.error_no_section()
+ def find_section(self, section_name):
+ return next((value for name, value in self.sections if name == section_name), None)
- value = '\n'.join(value)
-
- def append(sections, section):
- entry = [key, value]
- index = self.find_section(sections, section)
- sections[index][1].append(entry)
+ def new_section(self, line_parser):
+ section_name = line_parser.section_name
+ index = self.find_section_index(section_name)
+ self.section_name = section_name
+ if index == -1:
+ self.section = [section_name, []]
+ self.sections.append(self.section)
+ else:
+ self.section = self.sections[index]
- append(self.sections, self.section)
+ def _assignment(self, key, value, line_parser):
+ if not self.section_name:
+ raise line_parser.error_no_section_name()
- def parse_exc(self, msg, lineno, line=None):
- return ParseError(msg, lineno, line)
+ value = '\n'.join(value)
+ entry = [key, value]
+ self.section[1].append(entry)
- def error_no_section(self):
- return self.parse_exc('Section must be started before assignment',
- self.lineno)
+ def comment(self, line_parser):
+ """Called when a comment is parsed."""
+ pass
diff --git a/yardstick/ssh.py b/yardstick/ssh.py
index a024cf64a..bb715e4b4 100644
--- a/yardstick/ssh.py
+++ b/yardstick/ssh.py
@@ -432,8 +432,9 @@ class SSH(object):
class AutoConnectSSH(SSH):
+ # always wait or we will get OpenStack SSH errors
def __init__(self, user, host, port=None, pkey=None,
- key_filename=None, password=None, name=None, wait=False):
+ key_filename=None, password=None, name=None, wait=True):
super(AutoConnectSSH, self).__init__(user, host, port, pkey, key_filename, password, name)
self._wait = wait