diff options
10 files changed, 130 insertions, 65 deletions
diff --git a/samples/vnf_samples/nsut/vfw/tc_baremetal_http_ixload_1b_Requests-65000_Concurrency.yaml b/samples/vnf_samples/nsut/vfw/tc_baremetal_http_ixload_1b_Requests-65000_Concurrency.yaml index 134722681..d4a4bb706 100644 --- a/samples/vnf_samples/nsut/vfw/tc_baremetal_http_ixload_1b_Requests-65000_Concurrency.yaml +++ b/samples/vnf_samples/nsut/vfw/tc_baremetal_http_ixload_1b_Requests-65000_Concurrency.yaml @@ -37,4 +37,4 @@ context: type: Node name: yardstick nfvi_type: baremetal - file: /etc/yardstick/nodes/pod.yaml + file: /etc/yardstick/nodes/pod_ixia.yaml diff --git a/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py b/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py index 84b42c832..651614d3e 100644 --- a/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py +++ b/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py @@ -26,7 +26,7 @@ import mock from yardstick.benchmark.scenarios.networking.vnf_generic import \ SshManager, NetworkServiceTestCase, IncorrectConfig, \ - IncorrectSetup, open_relative_file + open_relative_file from yardstick.network_services.collector.subscriber import Collector from yardstick.network_services.vnf_generic.vnf.base import \ GenericTrafficGen, GenericVNF @@ -471,7 +471,7 @@ class TestNetworkServiceTestCase(unittest.TestCase): mock.Mock(return_value=(1, SYS_CLASS_NET + IP_ADDR_SHOW, "")) ssh.from_node.return_value = ssh_mock - with self.assertRaises(IncorrectSetup): + with self.assertRaises(IncorrectConfig): self.s.map_topology_to_infrastructure() def test_map_topology_to_infrastructure_config_invalid(self): @@ -694,11 +694,11 @@ class TestNetworkServiceTestCase(unittest.TestCase): def test_probe_missing_values(self): netdevs = self.SAMPLE_NETDEVS.copy() network = {'local_mac': '0a:de:ad:be:ef:f5'} - NetworkServiceTestCase._probe_missing_values(netdevs, network, set()) + NetworkServiceTestCase._probe_missing_values(netdevs, network) assert network['vpci'] == '0000:0b:00.0' network = {'local_mac': '0a:de:ad:be:ef:f4'} - NetworkServiceTestCase._probe_missing_values(netdevs, network, set()) + NetworkServiceTestCase._probe_missing_values(netdevs, network) assert network['vpci'] == '0000:00:19.0' def test_open_relative_path(self): diff --git a/tests/unit/network_services/nfvi/test_resource.py b/tests/unit/network_services/nfvi/test_resource.py index cb26fd085..072f06edf 100644 --- a/tests/unit/network_services/nfvi/test_resource.py +++ b/tests/unit/network_services/nfvi/test_resource.py @@ -108,13 +108,13 @@ class TestResourceProfile(unittest.TestCase): def test_get_cpu_data(self): reskey = ["", "cpufreq", "cpufreq-0"] value = "metric:10" - val = self.resource_profile.get_cpu_data(reskey, value) + val = self.resource_profile.get_cpu_data(reskey[1], reskey[2], value) self.assertIsNotNone(val) def test_get_cpu_data_error(self): reskey = ["", "", ""] value = "metric:10" - val = self.resource_profile.get_cpu_data(reskey, value) + val = self.resource_profile.get_cpu_data(reskey[0], reskey[1], value) self.assertEqual(val, ('error', 'Invalid', '', '')) def test__start_collectd(self): diff --git a/tests/unit/network_services/traffic_profile/test_http_ixload.py b/tests/unit/network_services/traffic_profile/test_http_ixload.py index 2e1b6f4ff..5110439fd 100644 --- a/tests/unit/network_services/traffic_profile/test_http_ixload.py +++ b/tests/unit/network_services/traffic_profile/test_http_ixload.py @@ -16,11 +16,33 @@ from __future__ import absolute_import import unittest import mock -import runpy from oslo_serialization import jsonutils from yardstick.network_services.traffic_profile import http_ixload +from yardstick.network_services.traffic_profile.http_ixload import \ + join_non_strings, validate_non_string_sequence + + +class TestJoinNonStrings(unittest.TestCase): + + def test_validate_non_string_sequence(self): + self.assertEqual(validate_non_string_sequence([1, 2, 3]), [1, 2, 3]) + self.assertIsNone(validate_non_string_sequence('123')) + self.assertIsNone(validate_non_string_sequence(1)) + + self.assertEqual(validate_non_string_sequence(1, 2), 2) + self.assertEqual(validate_non_string_sequence(1, default=2), 2) + + with self.assertRaises(RuntimeError): + validate_non_string_sequence(1, raise_exc=RuntimeError) + + def test_join_non_strings(self): + self.assertEqual(join_non_strings(':'), '') + self.assertEqual(join_non_strings(':', 'a'), 'a') + self.assertEqual(join_non_strings(':', 'a', 2, 'c'), 'a:2:c') + self.assertEqual(join_non_strings(':', ['a', 2, 'c']), 'a:2:c') + self.assertEqual(join_non_strings(':', 'abc'), 'abc') class TestIxLoadTrafficGen(unittest.TestCase): diff --git a/yardstick/benchmark/scenarios/networking/vnf_generic.py b/yardstick/benchmark/scenarios/networking/vnf_generic.py index 599835d56..e6dca929c 100644 --- a/yardstick/benchmark/scenarios/networking/vnf_generic.py +++ b/yardstick/benchmark/scenarios/networking/vnf_generic.py @@ -265,8 +265,25 @@ class NetworkServiceTestCase(base.Scenario): for dpdk_port_num, netdev in enumerate(s): netdev['dpdk_port_num'] = dpdk_port_num + def _probe_netdevs(self, node, node_dict): + cmd = "PATH=$PATH:/sbin:/usr/sbin ip addr show" + netdevs = {} + with SshManager(node_dict) as conn: + if conn: + exit_status = conn.execute(cmd)[0] + if exit_status != 0: + raise IncorrectSetup("Node's %s lacks ip tool." % node) + exit_status, stdout, _ = conn.execute( + self.FIND_NETDEVICE_STRING) + if exit_status != 0: + raise IncorrectSetup( + "Cannot find netdev info in sysfs" % node) + netdevs = node_dict['netdevs'] = self.parse_netdev_info(stdout) + return netdevs + @classmethod - def _probe_missing_values(cls, netdevs, network, missing): + def _probe_missing_values(cls, netdevs, network): + mac_lower = network['local_mac'].lower() for netdev in netdevs.values(): if netdev['address'].lower() != mac_lower: @@ -288,36 +305,30 @@ class NetworkServiceTestCase(base.Scenario): """ for node, node_dict in self.context_cfg["nodes"].items(): - cmd = "PATH=$PATH:/sbin:/usr/sbin ip addr show" - with SshManager(node_dict) as conn: - exit_status = conn.execute(cmd)[0] - if exit_status != 0: - raise IncorrectSetup("Node's %s lacks ip tool." % node) - exit_status, stdout, _ = conn.execute( - self.FIND_NETDEVICE_STRING) - if exit_status != 0: - raise IncorrectSetup( - "Cannot find netdev info in sysfs" % node) - netdevs = node_dict['netdevs'] = self.parse_netdev_info( - stdout) - - for network in node_dict["interfaces"].values(): - missing = self.TOPOLOGY_REQUIRED_KEYS.difference(network) - if not missing: - continue - - try: - self._probe_missing_values(netdevs, network, - missing) - except KeyError: - pass - else: - missing = self.TOPOLOGY_REQUIRED_KEYS.difference( - network) - if missing: - raise IncorrectConfig( - "Require interface fields '%s' not found, topology file " - "corrupted" % ', '.join(missing)) + for network in node_dict["interfaces"].values(): + missing = self.TOPOLOGY_REQUIRED_KEYS.difference(network) + if not missing: + continue + + # only ssh probe if there are missing values + # ssh probe won't work on Ixia, so we had better define all our values + + try: + netdevs = self._probe_netdevs(node, node_dict) + self._probe_missing_values(netdevs, network) + except KeyError: + pass + except (SSHError, SSHTimeout): + raise IncorrectConfig( + "Unable to probe missing interface fields '%s', on node %s " + "SSH Error" % (', '.join(missing), node)) + else: + missing = self.TOPOLOGY_REQUIRED_KEYS.difference( + network) + if missing: + raise IncorrectConfig( + "Require interface fields '%s' not found, topology file " + "corrupted" % ', '.join(missing)) # 3. Use topology file to find connections & resolve dest address self._resolve_topology() diff --git a/yardstick/network_services/nfvi/resource.py b/yardstick/network_services/nfvi/resource.py index ce09b6597..2fb4a8e8e 100644 --- a/yardstick/network_services/nfvi/resource.py +++ b/yardstick/network_services/nfvi/resource.py @@ -73,18 +73,18 @@ class ResourceProfile(object): @classmethod def parse_simple_resource(cls, key, value): - return {'/'.join(key): value.split(":")[1]} + reskey = "/".join(rkey for rkey in key if "nsb_stats" not in rkey) + return {reskey: value.split(":")[1]} @classmethod - def get_cpu_data(cls, key_split, value): + def get_cpu_data(cls, res_key0, res_key1, value): """ Get cpu topology of the host """ pattern = r"-(\d+)" - if "cpufreq" in key_split[0]: - metric = key_split[0] - source = key_split[1] + + if 'cpufreq' in res_key0: + metric, source = res_key0, res_key1 else: - metric = key_split[1] - source = key_split[0] + metric, source = res_key1, res_key0 match = re.search(pattern, source, re.MULTILINE) if not match: @@ -128,7 +128,8 @@ class ResourceProfile(object): res_key1 = next(res_key_iter) if "cpu" in res_key0 or "intel_rdt" in res_key0: - cpu_key, name, metric, testcase = self.get_cpu_data(key_split, value) + cpu_key, name, metric, testcase = \ + self.get_cpu_data(res_key0, res_key1, value) if cpu_key in core_list: result["cpu"].setdefault(cpu_key, {}).update({name: metric}) @@ -136,16 +137,16 @@ class ResourceProfile(object): result["memory"].update({res_key1: value.split(":")[0]}) elif "hugepages" in res_key0: - result["hugepages"].update(self.parse_hugepages(key, value)) + result["hugepages"].update(self.parse_hugepages(key_split, value)) elif "dpdkstat" in res_key0: - result["dpdkstat"].update(self.parse_dpdkstat(key, value)) + result["dpdkstat"].update(self.parse_dpdkstat(key_split, value)) elif "virt" in res_key1: - result["virt"].update(self.parse_virt(key, value)) + result["virt"].update(self.parse_virt(key_split, value)) elif "ovs_stats" in res_key0: - result["ovs_stats"].update(self.parse_ovs_stats(key, value)) + result["ovs_stats"].update(self.parse_ovs_stats(key_split, value)) result["timestamp"] = testcase @@ -153,13 +154,16 @@ class ResourceProfile(object): def amqp_process_for_nfvi_kpi(self): """ amqp collect and return nfvi kpis """ - if self.amqp_client is None: + if self.amqp_client is None and self.enable: self.amqp_client = \ multiprocessing.Process(target=self.run_collectd_amqp) self.amqp_client.start() def amqp_collect_nfvi_kpi(self): """ amqp collect and return nfvi kpis """ + if not self.enable: + return {} + metric = {} while not self._queue.empty(): metric.update(self._queue.get()) @@ -193,8 +197,6 @@ class ResourceProfile(object): def _start_collectd(self, connection, bin_path): LOG.debug("Starting collectd to collect NFVi stats") - # temp disable - return connection.execute('sudo pkill -9 collectd') collectd = os.path.join(bin_path, "collectd.sh") provision_tool(connection, collectd) diff --git a/yardstick/network_services/traffic_profile/http_ixload.py b/yardstick/network_services/traffic_profile/http_ixload.py index 8a4f97f04..348056551 100644 --- a/yardstick/network_services/traffic_profile/http_ixload.py +++ b/yardstick/network_services/traffic_profile/http_ixload.py @@ -18,6 +18,7 @@ from __future__ import print_function import sys import os import logging +import collections # ixload uses its own py2. So importing jsonutils fails. So adding below # workaround to support call from yardstick @@ -26,8 +27,16 @@ try: except ImportError: import json as jsonutils -from yardstick.common.utils import join_non_strings -from yardstick.common.utils import ErrorClass + +class ErrorClass(object): + + def __init__(self, *args, **kwargs): + if 'test' not in kwargs: + raise RuntimeError + + def __getattr__(self, item): + raise AttributeError + try: from IxLoad import IxLoad, StatCollectorUtils @@ -80,11 +89,25 @@ Incoming stats: Time interval: %s """ +def validate_non_string_sequence(value, default=None, raise_exc=None): + if isinstance(value, collections.Sequence) and not isinstance(value, str): + return value + if raise_exc: + raise raise_exc + return default + + +def join_non_strings(separator, *non_strings): + try: + non_strings = validate_non_string_sequence(non_strings[0], raise_exc=RuntimeError) + except (IndexError, RuntimeError): + pass + return str(separator).join(str(non_string) for non_string in non_strings) + + class IXLOADHttpTest(object): def __init__(self, test_input): - self.test_input = jsonutils.loads(test_input) - self.parse_run_test() self.ix_load = None self.stat_utils = None self.remote_server = None @@ -94,6 +117,8 @@ class IXLOADHttpTest(object): self.chassis = None self.card = None self.ports_to_reassign = None + self.test_input = jsonutils.loads(test_input) + self.parse_run_test() @staticmethod def format_ports_for_reassignment(ports): @@ -291,4 +316,5 @@ def main(args): ixload_obj.start_http_test() if __name__ == '__main__': + LOG.info("Start http_ixload test") main(sys.argv) diff --git a/yardstick/network_services/vnf_generic/vnf/tg_ixload.py b/yardstick/network_services/vnf_generic/vnf/tg_ixload.py index c50490020..353d31fc6 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_ixload.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_ixload.py @@ -46,7 +46,7 @@ IXLOAD_CONFIG_TEMPLATE = '''\ }, "remote_server": "%s", "result_dir": "%s", - "ixload_cfg": '"C:/Results/%s" + "ixload_cfg": "C:/Results/%s" }''' IXLOAD_CMD = "{ixloadpy} {http_ixload} {args}" @@ -130,7 +130,7 @@ class IxLoadTrafficGen(SampleVNFTrafficGen): for interface in self.vnfd_helper.interfaces: vpci_list = interface['virtual-interface']["vpci"].split(":") card = vpci_list[0] - ports.append(vpci_list[1]) + ports.append(str(vpci_list[1])) for csv_file in glob.iglob(self.ssh_helper.join_bin_path('*.csv')): os.unlink(csv_file) @@ -142,6 +142,7 @@ class IxLoadTrafficGen(SampleVNFTrafficGen): os.path.basename(self.resource_helper.resource_file_name)) http_ixload_path = os.path.join(VNF_PATH, "../../traffic_profile") + cmd = IXLOAD_CMD.format( ixloadpy=os.path.join(ixia_config["py_bin_path"], "ixloadpython"), http_ixload=os.path.join(http_ixload_path, "http_ixload.py"), @@ -168,7 +169,10 @@ class IxLoadTrafficGen(SampleVNFTrafficGen): def instantiate(self, scenario_cfg, context_cfg): super(IxLoadTrafficGen, self).instantiate(scenario_cfg, context_cfg) - self.done = False + + def wait_for_instantiate(self): + # not needed for Ixload + pass def terminate(self): call(["pkill", "-9", "http_ixload.py"]) diff --git a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py index 4abe06059..78d2bd8ba 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py @@ -63,8 +63,8 @@ class IxiaResourceHelper(ClientResourceHelper): def _build_ports(self): # self.generate_port_pairs(self.topology) - self.priv_ports = [int(x[0][-1]) for x in self.tg_port_pairs] - self.pub_ports = [int(x[1][-1]) for x in self.tg_port_pairs] + self.priv_ports = [int(x[0][2:]) for x in self.tg_port_pairs] + self.pub_ports = [int(x[1][2:]) for x in self.tg_port_pairs] self.my_ports = list(set(self.priv_ports).union(set(self.pub_ports))) def get_stats(self, *args, **kwargs): diff --git a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_trex.py b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_trex.py index 548060849..d94a9a6e6 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_trex.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_trex.py @@ -52,8 +52,8 @@ class TrexRfcResourceHelper(TrexResourceHelper): def _build_ports(self): self.tg_port_pairs, self.networks = MultiPortConfig.get_port_pairs( self.vnfd_helper.interfaces) - self.priv_ports = [int(x[0][-1]) for x in self.tg_port_pairs] - self.pub_ports = [int(x[1][-1]) for x in self.tg_port_pairs] + self.priv_ports = [int(x[0][2:]) for x in self.tg_port_pairs] + self.pub_ports = [int(x[1][2:]) for x in self.tg_port_pairs] self.my_ports = list(set(chain(self.priv_ports, self.pub_ports))) def _run_traffic_once(self, traffic_profile): |