X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=test%2Ftest_nfvbench.py;h=7a0a9edd22bc200dc98ff7ea5d310a8817dc5b93;hb=24314713446b6411cedce4329ab5ebfd6da678a2;hp=ad78a9ead123051a8ddd5ca63dcaa265c3d9af32;hpb=2d55474511a5057015e77547c326120c1649c0b7;p=nfvbench.git diff --git a/test/test_nfvbench.py b/test/test_nfvbench.py index ad78a9e..7a0a9ed 100644 --- a/test/test_nfvbench.py +++ b/test/test_nfvbench.py @@ -13,446 +13,34 @@ # License for the specific language governing permissions and limitations # under the License. # +from mock_trex import no_op -from attrdict import AttrDict +import json import logging -from nfvbench.config import get_err_config -from nfvbench.connection import SSH +import sys + +from attrdict import AttrDict +from mock import patch +import pytest + +from nfvbench.config import config_loads from nfvbench.credentials import Credentials from nfvbench.fluentd import FluentLogHandler import nfvbench.log -from nfvbench.network import Interface -from nfvbench.network import Network -from nfvbench.specs import Encaps +import nfvbench.nfvbench +from nfvbench.traffic_client import Device +from nfvbench.traffic_client import GeneratorConfig +from nfvbench.traffic_client import IpBlock +from nfvbench.traffic_client import TrafficClient import nfvbench.traffic_gen.traffic_utils as traffic_utils -import os -import pytest - -__location__ = os.path.realpath(os.path.join(os.getcwd(), - os.path.dirname(__file__))) - - -@pytest.fixture -def ssh(monkeypatch): - def mock_init(self, ssh_access, *args, **kwargs): - self.ssh_access = ssh_access - if ssh_access.private_key: - self.pkey = self._get_pkey(ssh_access.private_key) - else: - self.pkey = None - self._client = False - self.connect_timeout = 2 - self.connect_retry_count = 1 - self.connect_retry_wait_sec = 1 - super(SSH, self).__init__() - - monkeypatch.setattr(SSH, '__init__', mock_init) - - -@pytest.fixture -def openstack_vxlan_spec(): - return AttrDict( - { - 'openstack': AttrDict({ - 'vswitch': "VTS", - 'encaps': Encaps.VxLAN} - ), - 'run_spec': AttrDict({ - 'use_vpp': True - }) - } - ) - -# ========================================================================= -# PVP Chain tests -# ========================================================================= - -def test_chain_interface(): - iface = Interface('testname', 'vpp', tx_packets=1234, rx_packets=4321) - assert iface.name == 'testname' - assert iface.device == 'vpp' - assert iface.get_packet_count('tx') == 1234 - assert iface.get_packet_count('rx') == 4321 - assert iface.get_packet_count('wrong_key') == 0 - - -@pytest.fixture(scope='session') -def iface1(): - return Interface('iface1', 'trex', tx_packets=10000, rx_packets=1234) - - -@pytest.fixture(scope='session') -def iface2(): - return Interface('iface2', 'n9k', tx_packets=1234, rx_packets=9901) - - -@pytest.fixture(scope='session') -def iface3(): - return Interface('iface3', 'n9k', tx_packets=9900, rx_packets=1234) - - -@pytest.fixture(scope='session') -def iface4(): - return Interface('iface4', 'vpp', tx_packets=1234, rx_packets=9801) - - -@pytest.fixture(scope='session') -def net1(iface1, iface2, iface3, iface4): - return Network([iface1, iface2, iface3, iface4], reverse=False) - - -@pytest.fixture(scope='session') -def net2(iface1, iface2, iface3): - return Network([iface1, iface2, iface3], reverse=True) - - -def test_chain_network(net1, net2, iface1, iface2, iface3, iface4): - assert [iface1, iface2, iface3, iface4] == net1.get_interfaces() - assert [iface3, iface2, iface1] == net2.get_interfaces() - net2.add_interface(iface4) - assert [iface4, iface3, iface2, iface1] == net2.get_interfaces() - - -""" -def test_chain_analysis(net1, monkeypatch, openstack_vxlan_spec): - def mock_empty(self, *args, **kwargs): - pass - - monkeypatch.setattr(ServiceChain, '_setup', mock_empty) - - f = ServiceChain(AttrDict({'service_chain': 'DUMMY'}), [], {'tor': {}}, openstack_vxlan_spec, - lambda x, y, z: None) - result = f.get_analysis([net1]) - assert result[1]['packet_drop_count'] == 99 - assert result[1]['packet_drop_percentage'] == 0.99 - assert result[2]['packet_drop_count'] == 1 - assert result[2]['packet_drop_percentage'] == 0.01 - assert result[3]['packet_drop_count'] == 99 - assert result[3]['packet_drop_percentage'] == 0.99 - - net1.reverse = True - result = f.get_analysis([net1]) - assert result[1]['packet_drop_count'] == 0 - assert result[1]['packet_drop_percentage'] == 0.0 - assert result[2]['packet_drop_count'] == 0 - assert result[2]['packet_drop_percentage'] == 0.0 - assert result[3]['packet_drop_count'] == 0 - assert result[3]['packet_drop_percentage'] == 0.0 - -@pytest.fixture -def pvp_chain(monkeypatch, openstack_vxlan_spec): - tor_vni1 = Interface('vni-4097', 'n9k', 50, 77) - vsw_vni1 = Interface('vxlan_tunnel0', 'vpp', 77, 48) - vsw_vif1 = Interface('VirtualEthernet0/0/2', 'vpp', 48, 77) - vsw_vif2 = Interface('VirtualEthernet0/0/3', 'vpp', 77, 47) - vsw_vni2 = Interface('vxlan_tunnel1', 'vpp', 43, 77) - tor_vni2 = Interface('vni-4098', 'n9k', 77, 40) - - def mock_init(self, *args, **kwargs): - self.vni_ports = [4097, 4098] - self.specs = openstack_vxlan_spec - self.clients = { - 'vpp': AttrDict({ - 'set_interface_counters': lambda: None, - }) - } - self.worker = AttrDict({ - 'run': lambda: None, - }) - - def mock_empty(self, *args, **kwargs): - pass - - def mock_get_network(self, traffic_port, vni_id, reverse=False): - if vni_id == 0: - return Network([tor_vni1, vsw_vni1, vsw_vif1], reverse) - else: - return Network([tor_vni2, vsw_vni2, vsw_vif2], reverse) - - def mock_get_data(self): - return {} - - monkeypatch.setattr(PVPChain, '_get_network', mock_get_network) - monkeypatch.setattr(PVPChain, '_get_data', mock_get_data) - monkeypatch.setattr(PVPChain, '_setup', mock_empty) - monkeypatch.setattr(VxLANWorker, '_clear_interfaces', mock_empty) - monkeypatch.setattr(PVPChain, '_generate_traffic', mock_empty) - monkeypatch.setattr(PVPChain, '__init__', mock_init) - return PVPChain(None, None, {'vm': None, 'vpp': None, 'tor': None, 'traffic': None}, None) - - -def test_pvp_chain_run(pvp_chain): - result = pvp_chain.run() - expected_result = { - 'raw_data': {}, - 'stats': None, - 'packet_analysis': { - 'direction-forward': [ - OrderedDict([ - ('interface', 'vni-4097'), - ('device', 'n9k'), - ('packet_count', 50) - ]), - OrderedDict([ - ('interface', 'vxlan_tunnel0'), - ('device', 'vpp'), - ('packet_count', 48), - ('packet_drop_count', 2), - ('packet_drop_percentage', 4.0) - ]), - OrderedDict([ - ('interface', 'VirtualEthernet0/0/2'), - ('device', 'vpp'), - ('packet_count', 48), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]), - OrderedDict([ - ('interface', 'VirtualEthernet0/0/3'), - ('device', 'vpp'), - ('packet_count', 47), - ('packet_drop_count', 1), - ('packet_drop_percentage', 2.0) - ]), - OrderedDict([ - ('interface', 'vxlan_tunnel1'), - ('device', 'vpp'), - ('packet_count', 43), - ('packet_drop_count', 4), - ('packet_drop_percentage', 8.0) - ]), - OrderedDict([ - ('interface', 'vni-4098'), - ('device', 'n9k'), - ('packet_count', 40), - ('packet_drop_count', 3), - ('packet_drop_percentage', 6.0) - ]) - ], - 'direction-reverse': [ - OrderedDict([ - ('interface', 'vni-4098'), - ('device', 'n9k'), - ('packet_count', 77) - ]), - OrderedDict([ - ('interface', 'vxlan_tunnel1'), - ('device', 'vpp'), - ('packet_count', 77), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]), - OrderedDict([ - ('interface', 'VirtualEthernet0/0/3'), - ('device', 'vpp'), - ('packet_count', 77), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]), - OrderedDict([ - ('interface', 'VirtualEthernet0/0/2'), - ('device', 'vpp'), - ('packet_count', 77), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]), - OrderedDict([ - ('interface', 'vxlan_tunnel0'), - ('device', 'vpp'), - ('packet_count', 77), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]), - OrderedDict([ - ('interface', 'vni-4097'), - ('device', 'n9k'), - ('packet_count', 77), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]) - ] - } - } - assert result == expected_result -""" +# just to get rid of the unused function warning +no_op() -# ========================================================================= -# PVVP Chain tests -# ========================================================================= - -""" -@pytest.fixture -def pvvp_chain(monkeypatch, openstack_vxlan_spec): - tor_vni1 = Interface('vni-4097', 'n9k', 50, 77) - vsw_vni1 = Interface('vxlan_tunnel0', 'vpp', 77, 48) - vsw_vif1 = Interface('VirtualEthernet0/0/2', 'vpp', 48, 77) - vsw_vif3 = Interface('VirtualEthernet0/0/0', 'vpp', 77, 47) - vsw_vif4 = Interface('VirtualEthernet0/0/1', 'vpp', 45, 77) - vsw_vif2 = Interface('VirtualEthernet0/0/3', 'vpp', 77, 44) - vsw_vni2 = Interface('vxlan_tunnel1', 'vpp', 43, 77) - tor_vni2 = Interface('vni-4098', 'n9k', 77, 40) - - def mock_init(self, *args, **kwargs): - self.vni_ports = [4099, 4100] - self.v2vnet = V2VNetwork() - self.specs = openstack_vxlan_spec - self.clients = { - 'vpp': AttrDict({ - 'get_v2v_network': lambda reverse=None: Network([vsw_vif3, vsw_vif4], reverse), - 'set_interface_counters': lambda pvvp=None: None, - 'set_v2v_counters': lambda: None, - }) - } - self.worker = AttrDict({ - 'run': lambda: None, - }) - - def mock_empty(self, *args, **kwargs): - pass - - def mock_get_network(self, traffic_port, vni_id, reverse=False): - if vni_id == 0: - return Network([tor_vni1, vsw_vni1, vsw_vif1], reverse) - else: - return Network([tor_vni2, vsw_vni2, vsw_vif2], reverse) - - def mock_get_data(self): - return {} - - monkeypatch.setattr(PVVPChain, '_get_network', mock_get_network) - monkeypatch.setattr(PVVPChain, '_get_data', mock_get_data) - monkeypatch.setattr(PVVPChain, '_setup', mock_empty) - monkeypatch.setattr(VxLANWorker, '_clear_interfaces', mock_empty) - monkeypatch.setattr(PVVPChain, '_generate_traffic', mock_empty) - monkeypatch.setattr(PVVPChain, '__init__', mock_init) - - return PVVPChain(None, None, {'vm': None, 'vpp': None, 'tor': None, 'traffic': None}, None) - - -def test_pvvp_chain_run(pvvp_chain): - result = pvvp_chain.run() - - expected_result = { - 'raw_data': {}, - 'stats': None, - 'packet_analysis': - {'direction-forward': [ - OrderedDict([ - ('interface', 'vni-4097'), - ('device', 'n9k'), - ('packet_count', 50) - ]), - OrderedDict([ - ('interface', 'vxlan_tunnel0'), - ('device', 'vpp'), - ('packet_count', 48), - ('packet_drop_count', 2), - ('packet_drop_percentage', 4.0) - ]), - OrderedDict([ - ('interface', 'VirtualEthernet0/0/2'), - ('device', 'vpp'), - ('packet_count', 48), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]), - OrderedDict([ - ('interface', 'VirtualEthernet0/0/0'), - ('device', 'vpp'), - ('packet_count', 47), - ('packet_drop_count', 1), - ('packet_drop_percentage', 2.0) - ]), - OrderedDict([ - ('interface', 'VirtualEthernet0/0/1'), - ('device', 'vpp'), - ('packet_count', 45), - ('packet_drop_count', 2), - ('packet_drop_percentage', 4.0) - ]), - OrderedDict([ - ('interface', 'VirtualEthernet0/0/3'), - ('device', 'vpp'), - ('packet_count', 44), - ('packet_drop_count', 1), - ('packet_drop_percentage', 2.0) - ]), - OrderedDict([ - ('interface', 'vxlan_tunnel1'), - ('device', 'vpp'), - ('packet_count', 43), - ('packet_drop_count', 1), - ('packet_drop_percentage', 2.0) - ]), - OrderedDict([ - ('interface', 'vni-4098'), - ('device', 'n9k'), - ('packet_count', 40), - ('packet_drop_count', 3), - ('packet_drop_percentage', 6.0) - ]) - ], - 'direction-reverse': [ - OrderedDict([ - ('interface', 'vni-4098'), - ('device', 'n9k'), - ('packet_count', 77) - ]), - OrderedDict([ - ('interface', 'vxlan_tunnel1'), - ('device', 'vpp'), - ('packet_count', 77), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]), - OrderedDict([ - ('interface', 'VirtualEthernet0/0/3'), - ('device', 'vpp'), - ('packet_count', 77), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]), - OrderedDict([ - ('interface', 'VirtualEthernet0/0/1'), - ('device', 'vpp'), - ('packet_count', 77), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]), - OrderedDict([ - ('interface', 'VirtualEthernet0/0/0'), - ('device', 'vpp'), - ('packet_count', 77), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]), - OrderedDict([ - ('interface', 'VirtualEthernet0/0/2'), - ('device', 'vpp'), - ('packet_count', 77), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]), - OrderedDict([ - ('interface', 'vxlan_tunnel0'), - ('device', 'vpp'), - ('packet_count', 77), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]), - OrderedDict([ - ('interface', 'vni-4097'), - ('device', 'n9k'), - ('packet_count', 77), - ('packet_drop_count', 0), - ('packet_drop_percentage', 0.0) - ]) - ]} - } - assert result == expected_result -""" +def setup_module(module): + """Enable log.""" + nfvbench.log.setup(mute_stdout=True) # ========================================================================= # Traffic client tests @@ -484,7 +72,8 @@ def test_parse_rate_str(): except Exception: return True else: - assert False + return False + return False assert should_raise_error('101') assert should_raise_error('201%') @@ -493,6 +82,7 @@ def test_parse_rate_str(): assert should_raise_error('0pps') assert should_raise_error('-1bps') + def test_rate_conversion(): assert traffic_utils.load_to_bps(50, 10000000000) == pytest.approx(5000000000.0) assert traffic_utils.load_to_bps(37, 10000000000) == pytest.approx(3700000000.0) @@ -511,133 +101,43 @@ def test_rate_conversion(): assert traffic_utils.pps_to_bps(3225895.85831, 340.3) == pytest.approx(9298322222) -""" -@pytest.fixture -def traffic_client(monkeypatch): - - def mock_init(self, *args, **kwargs): - self.run_config = { - 'bidirectional': False, - 'l2frame_size': '64', - 'duration_sec': 30, - 'rates': [{'rate_percent': '10'}, {'rate_pps': '1'}] - } - - self.config = AttrDict({ - 'generator_config': { - 'intf_speed': 10000000000 - }, - 'ndr_run': True, - 'pdr_run': True, - 'single_run': False, - 'attempts': 1, - 'measurement': { - 'NDR': 0.0, - 'PDR': 0.1, - 'load_epsilon': 0.1 - } - }) - - self.runner = AttrDict({ - 'time_elapsed': lambda: 30, - 'stop': lambda: None, - 'client': AttrDict({'get_stats': lambda: None}) - }) +# pps at 10Gbps line rate for 64 byte frames +LR_64B_PPS = 14880952 +LR_1518B_PPS = 812743 - self.current_load = None - self.dummy_stats = { - 50.0: 72.6433562831, - 25.0: 45.6095059858, - 12.5: 0.0, - 18.75: 27.218642979, - 15.625: 12.68585861, - 14.0625: 2.47154392563, - 13.28125: 0.000663797066801, - 12.890625: 0.0, - 13.0859375: 0.0, - 13.18359375: 0.00359387347122, - 13.671875: 0.307939922531, - 13.4765625: 0.0207718516156, - 13.57421875: 0.0661795060969 - } - - def mock_modify_load(self, load): - self.run_config['rates'][0] = {'rate_percent': str(load)} - self.current_load = load - - def mock_run_traffic(self): - yield { - 'overall': { - 'drop_rate_percent': self.dummy_stats[self.current_load], - 'rx': { - 'total_pkts': 1, - 'avg_delay_usec': 0.0, - 'max_delay_usec': 0.0, - 'min_delay_usec': 0.0 - } - } - } - - monkeypatch.setattr(TrafficClient, '__init__', mock_init) - monkeypatch.setattr(TrafficClient, 'modify_load', mock_modify_load) - monkeypatch.setattr(TrafficClient, 'run_traffic', mock_run_traffic) - - return TrafficClient() - - -def test_ndr_pdr_search(traffic_client): - expected_results = { - 'pdr': { - 'l2frame_size': '64', - 'initial_rate_type': 'rate_percent', - 'stats': { - 'overall': { - 'drop_rate_percent': 0.0661795060969, - 'min_delay_usec': 0.0, - 'avg_delay_usec': 0.0, - 'max_delay_usec': 0.0 - } - }, - 'load_percent_per_direction': 13.57421875, - 'rate_percent': 13.57422547, - 'rate_bps': 1357422547.0, - 'rate_pps': 2019974.0282738095, - 'duration_sec': 30 - }, - 'ndr': { - 'l2frame_size': '64', - 'initial_rate_type': 'rate_percent', - 'stats': { - 'overall': { - 'drop_rate_percent': 0.0, - 'min_delay_usec': 0.0, - 'avg_delay_usec': 0.0, - 'max_delay_usec': 0.0 - } - }, - 'load_percent_per_direction': 13.0859375, - 'rate_percent': 13.08594422, - 'rate_bps': 1308594422.0, - 'rate_pps': 1947313.1279761905, - 'duration_sec': 30 - } - } +def assert_equivalence(reference, value, allowance_pct=1): + """Assert if a value is equivalent to a reference value with given margin. - results = traffic_client.get_ndr_and_pdr() - assert len(results) == 2 - for result in results.values(): - result.pop('timestamp_sec') - result.pop('time_taken_sec') - assert results == expected_results -""" + :param float reference: reference value to compare to + :param float value: value to compare to reference + :param float allowance_pct: max allowed percentage of margin + 0 : requires exact match + 1 : must be equal within 1% of the reference value + ... + 100: always true + """ + if reference == 0: + assert value == 0 + else: + assert abs(value - reference) * 100 / reference <= allowance_pct + +def test_load_from_rate(): + assert traffic_utils.get_load_from_rate('100%') == 100 + assert_equivalence(100, traffic_utils.get_load_from_rate(str(LR_64B_PPS) + 'pps')) + assert_equivalence(50, traffic_utils.get_load_from_rate(str(LR_64B_PPS / 2) + 'pps')) + assert_equivalence(100, traffic_utils.get_load_from_rate('10Gbps')) + assert_equivalence(50, traffic_utils.get_load_from_rate('5000Mbps')) + assert_equivalence(1, traffic_utils.get_load_from_rate('100Mbps')) + assert_equivalence(100, traffic_utils.get_load_from_rate(str(LR_1518B_PPS) + 'pps', + avg_frame_size=1518)) + assert_equivalence(100, traffic_utils.get_load_from_rate(str(LR_1518B_PPS * 2) + 'pps', + avg_frame_size=1518, + line_rate='20Gbps')) # ========================================================================= # Other tests # ========================================================================= -def setup_module(module): - nfvbench.log.setup(mute_stdout=True) - def test_no_credentials(): cred = Credentials('/completely/wrong/path/openrc', None, False) if cred.rc_auth_url: @@ -646,31 +146,275 @@ def test_no_credentials(): else: assert True +def test_ip_block(): + ipb = IpBlock('10.0.0.0', '0.0.0.1', 256) + assert ipb.get_ip() == '10.0.0.0' + assert ipb.get_ip(255) == '10.0.0.255' + with pytest.raises(IndexError): + ipb.get_ip(256) + ipb = IpBlock('10.0.0.0', '0.0.0.1', 1) + assert ipb.get_ip() == '10.0.0.0' + with pytest.raises(IndexError): + ipb.get_ip(1) + + ipb = IpBlock('10.0.0.0', '0.0.0.2', 256) + assert ipb.get_ip() == '10.0.0.0' + assert ipb.get_ip(1) == '10.0.0.2' + assert ipb.get_ip(127) == '10.0.0.254' + assert ipb.get_ip(128) == '10.0.1.0' + with pytest.raises(IndexError): + ipb.get_ip(256) + + # verify with step larger than 1 + ipb = IpBlock('10.0.0.0', '0.0.0.2', 256) + assert ipb.get_ip() == '10.0.0.0' + assert ipb.get_ip(1) == '10.0.0.2' + assert ipb.get_ip(128) == '10.0.1.0' + assert ipb.get_ip(255) == '10.0.1.254' + with pytest.raises(IndexError): + ipb.get_ip(256) + +def check_stream_configs(gen_config): + """Verify that the range for each chain have adjacent IP ranges without holes between chains.""" + config = gen_config.config + tgc = config['traffic_generator'] + step = Device.ip_to_int(tgc['ip_addrs_step']) + cfc = 0 + sip = Device.ip_to_int(tgc['ip_addrs'][0].split('/')[0]) + dip = Device.ip_to_int(tgc['ip_addrs'][1].split('/')[0]) + stream_configs = gen_config.devices[0].get_stream_configs() + for index in range(config['service_chain_count']): + stream_cfg = stream_configs[index] + assert stream_cfg['ip_src_count'] == stream_cfg['ip_dst_count'] + assert Device.ip_to_int(stream_cfg['ip_src_addr']) == sip + assert Device.ip_to_int(stream_cfg['ip_dst_addr']) == dip + count = stream_cfg['ip_src_count'] + cfc += count + sip += count * step + dip += count * step + assert cfc == int(config['flow_count'] / 2) + +def _check_device_flow_config(step_ip): + config = _get_dummy_tg_config('PVP', '1Mpps', scc=10, fc=99999, step_ip=step_ip) + gen_config = GeneratorConfig(config) + check_stream_configs(gen_config) + +def test_device_flow_config(): + _check_device_flow_config('0.0.0.1') + _check_device_flow_config('0.0.0.2') + def test_config(): refcfg = {1: 100, 2: {21: 100, 22: 200}, 3: None} - assert(get_err_config({}, refcfg) is None) - assert(get_err_config({1: 10}, refcfg) is None) - assert(get_err_config({2: {21: 1000}}, refcfg) is None) - assert(get_err_config({3: "abc"}, refcfg) is None) + res1 = {1: 10, 2: {21: 100, 22: 200}, 3: None} + res2 = {1: 100, 2: {21: 1000, 22: 200}, 3: None} + res3 = {1: 100, 2: {21: 100, 22: 200}, 3: "abc"} + assert config_loads("{}", refcfg) == refcfg + assert config_loads("{1: 10}", refcfg) == res1 + assert config_loads("{2: {21: 1000}}", refcfg) == res2 + assert config_loads('{3: "abc"}', refcfg) == res3 + # correctly fails - assert(get_err_config({4: 0}, refcfg) == {4: 0}) - assert(get_err_config({2: {21: 100, 30: 50}}, refcfg) == {2: {30: 50}}) - assert(get_err_config({2: {0: 1, 1: 2}}, refcfg) == {2: {0: 1, 1: 2}}) - assert(get_err_config({2: {0: 1, 1: 2}, 5: 5}, refcfg) == {2: {0: 1, 1: 2}, 5: 5}) - # invalid value type - assert(get_err_config({1: 'abc', 2: {21: 0}}, refcfg) == {1: 'abc'}) - assert(get_err_config({2: 100}, refcfg) == {2: 100}) - # both correctly fail and invalid value type - assert(get_err_config({2: 100, 5: 10}, refcfg) == {2: 100, 5: 10}) + # pairs of input string and expected subset (None if identical) + fail_pairs = [ + ["{4: 0}", None], + ["{2: {21: 100, 30: 50}}", "{2: {30: 50}}"], + ["{2: {0: 1, 1: 2}, 5: 5}", None], + ["{1: 'abc', 2: {21: 0}}", "{1: 'abc'}"], + ["{2: 100}", None] + ] + for fail_pair in fail_pairs: + with pytest.raises(Exception) as e_info: + config_loads(fail_pair[0], refcfg) + expected = fail_pair[1] + if expected is None: + expected = fail_pair[0] + assert expected in str(e_info) + + # whitelist keys + flavor = {'flavor': {'vcpus': 2, 'ram': 8192, 'disk': 0, + 'extra_specs': {'hw:cpu_policy': 'dedicated'}}} + new_flavor = {'flavor': {'vcpus': 2, 'ram': 8192, 'disk': 0, + 'extra_specs': {'hw:cpu_policy': 'dedicated', 'hw:numa_nodes': 2}}} + assert config_loads("{'flavor': {'extra_specs': {'hw:numa_nodes': 2}}}", flavor, + whitelist_keys=['alpha', 'extra_specs']) == new_flavor + def test_fluentd(): logger = logging.getLogger('fluent-logger') - handler = FluentLogHandler('nfvbench', fluentd_port=7081) + + class FluentdConfig(dict): + def __getattr__(self, attr): + return self.get(attr) + + fluentd_configs = [ + FluentdConfig({ + 'logging_tag': 'nfvbench', + 'result_tag': 'resultnfvbench', + 'ip': '127.0.0.1', + 'port': 7081 + }), + FluentdConfig({ + 'logging_tag': 'nfvbench', + 'result_tag': 'resultnfvbench', + 'ip': '127.0.0.1', + 'port': 24224 + }), + FluentdConfig({ + 'logging_tag': None, + 'result_tag': 'resultnfvbench', + 'ip': '127.0.0.1', + 'port': 7082 + }), + FluentdConfig({ + 'logging_tag': 'nfvbench', + 'result_tag': None, + 'ip': '127.0.0.1', + 'port': 7083 + }) + ] + + handler = FluentLogHandler(fluentd_configs=fluentd_configs) logger.addHandler(handler) logger.setLevel(logging.INFO) logger.info('test') logger.warning('test %d', 100) + try: raise Exception("test") except Exception: logger.exception("got exception") + +def assert_ndr_pdr(stats, ndr, ndr_dr, pdr, pdr_dr): + assert stats['ndr']['rate_percent'] == ndr + assert stats['ndr']['stats']['overall']['drop_percentage'] == ndr_dr + assert_equivalence(pdr, stats['pdr']['rate_percent']) + assert_equivalence(pdr_dr, stats['pdr']['stats']['overall']['drop_percentage']) + +def _get_dummy_tg_config(chain_type, rate, scc=1, fc=10, step_ip='0.0.0.1', + ip0='10.0.0.0/8', ip1='20.0.0.0/8'): + return AttrDict({ + 'traffic_generator': {'host_name': 'nfvbench_tg', + 'default_profile': 'dummy', + 'generator_profile': [{'name': 'dummy', + 'tool': 'dummy', + 'ip': '127.0.0.1', + 'intf_speed': '10Gbps', + 'interfaces': [{'port': 0, 'pci': '0.0'}, + {'port': 1, 'pci': '0.0'}]}], + 'ip_addrs_step': step_ip, + 'ip_addrs': [ip0, ip1], + 'tg_gateway_ip_addrs': ['1.1.0.100', '2.2.0.100'], + 'tg_gateway_ip_addrs_step': step_ip, + 'gateway_ip_addrs': ['1.1.0.2', '2.2.0.2'], + 'gateway_ip_addrs_step': step_ip, + 'mac_addrs_left': None, + 'mac_addrs_right': None, + 'udp_src_port': None, + 'udp_dst_port': None}, + 'traffic': {'profile': 'profile_64', + 'bidirectional': True}, + 'traffic_profile': [{'name': 'profile_64', 'l2frame_size': ['64']}], + 'generator_profile': None, + 'service_chain': chain_type, + 'service_chain_count': scc, + 'flow_count': fc, + 'vlan_tagging': True, + 'no_arp': False, + 'duration_sec': 1, + 'interval_sec': 1, + 'pause_sec': 1, + 'rate': rate, + 'check_traffic_time_sec': 200, + 'generic_poll_sec': 2, + 'measurement': {'NDR': 0.001, 'PDR': 0.1, 'load_epsilon': 0.1}, + 'l2_loopback': False, + 'cores': None, + 'mbuf_factor': None, + 'disable_hdrh': None, + 'mbuf_64': None, + 'service_mode': False, + 'no_flow_stats': False, + 'no_latency_stats': False, + 'no_latency_streams': False + + }) + +def _get_traffic_client(): + config = _get_dummy_tg_config('PVP', 'ndr_pdr') + config['vxlan'] = False + config['ndr_run'] = True + config['pdr_run'] = True + config['generator_profile'] = 'dummy' + config['single_run'] = False + traffic_client = TrafficClient(config) + traffic_client.start_traffic_generator() + traffic_client.set_traffic('64', True) + return traffic_client + +@patch.object(TrafficClient, 'skip_sleep', lambda x: True) +def test_ndr_at_lr(): + """Test NDR at line rate.""" + traffic_client = _get_traffic_client() + tg = traffic_client.gen + # this is a perfect sut with no loss at LR + tg.set_response_curve(lr_dr=0, ndr=100, max_actual_tx=100, max_11_tx=100) + # tx packets should be line rate for 64B and no drops... + assert tg.get_tx_pps_dropped_pps(100) == (LR_64B_PPS, 0) + # NDR and PDR should be at 100% + # traffic_client.ensure_end_to_end() + results = traffic_client.get_ndr_and_pdr() + assert_ndr_pdr(results, 200.0, 0.0, 200.0, 0.0) + +@patch.object(TrafficClient, 'skip_sleep', lambda x: True) +def test_ndr_at_50(): + """Test NDR at 50% line rate. + + This is a sut with an NDR of 50% and linear drop rate after NDR up to 20% drops at LR + (meaning that if you send 100% TX, you will only receive 80% RX) + the tg requested TX/actual TX ratio is up to 50%, after 50% + is linear up 80% actuak TX when requesting 100% + """ + traffic_client = _get_traffic_client() + tg = traffic_client.gen + + tg.set_response_curve(lr_dr=20, ndr=50, max_actual_tx=80, max_11_tx=50) + # tx packets should be half line rate for 64B and no drops... + assert tg.get_tx_pps_dropped_pps(50) == (LR_64B_PPS / 2, 0) + # at 100% TX requested, actual TX is 80% where the drop rate is 3/5 of 20% of the actual TX + assert tg.get_tx_pps_dropped_pps(100) == (int(LR_64B_PPS * 0.8), + int(LR_64B_PPS * 0.8 * 0.6 * 0.2)) + results = traffic_client.get_ndr_and_pdr() + assert_ndr_pdr(results, 100.0, 0.0, 100.781, 0.09374) + +@patch.object(TrafficClient, 'skip_sleep', lambda x: True) +def test_ndr_pdr_low_cpu(): + """Test NDR and PDR with too low cpu. + + This test is for the case where the TG is underpowered and cannot send fast enough for the NDR + true NDR=40%, actual TX at 50% = 30%, actual measured DR is 0% + The ndr/pdr should bail out with a warning and a best effort measured NDR of 30% + """ + traffic_client = _get_traffic_client() + tg = traffic_client.gen + tg.set_response_curve(lr_dr=50, ndr=40, max_actual_tx=60, max_11_tx=0) + # tx packets should be 30% at requested half line rate for 64B and no drops... + assert tg.get_tx_pps_dropped_pps(50) == (int(LR_64B_PPS * 0.3), 0) + results = traffic_client.get_ndr_and_pdr() + assert results + # import pprint + # pp = pprint.PrettyPrinter(indent=4) + # pp.pprint(results) + +@patch.object(TrafficClient, 'skip_sleep', lambda x: True) +def test_no_openstack(): + """Test nfvbench using main.""" + config = _get_dummy_tg_config('EXT', '1000pps') + config.openrc_file = None + config.vlans = [[100], [200]] + config['traffic_generator']['mac_addrs_left'] = ['00:00:00:00:00:00'] + config['traffic_generator']['mac_addrs_right'] = ['00:00:00:00:01:00'] + del config['generator_profile'] + old_argv = sys.argv + sys.argv = [old_argv[0], '-c', json.dumps(config)] + nfvbench.nfvbench.main() + sys.argv = old_argv