#
"""Test Chaining functions."""
-from mock_trex import no_op
-
from mock import MagicMock
from mock import patch
+import pytest
+
+from .mock_trex import no_op
from nfvbench.chain_runner import ChainRunner
+from nfvbench.chaining import ChainException
+from nfvbench.chaining import ChainVnfPort
+from nfvbench.chaining import InstancePlacer
from nfvbench.compute import Compute
import nfvbench.credentials
from nfvbench.factory import BasicFactory
from nfvbench.summarizer import _annotate_chain_stats
from nfvbench.traffic_client import TrafficClient
from nfvbench.traffic_gen.traffic_base import Latency
-from nfvbench.traffic_gen.trex import TRex
+from nfvbench.traffic_gen.trex_gen import TRex
# just to get rid of the unused function warning
no_op()
nfvbench.log.setup(mute_stdout=False)
nfvbench.log.set_level(debug=True)
-def _get_chain_config(sc=ChainType.PVP, scc=1, shared_net=True):
+def _get_chain_config(sc=ChainType.PVP, scc=1, shared_net=True, rate='1Mpps'):
config, _ = load_default_config()
config.vm_image_file = 'nfvbenchvm-0.0.qcow2'
config.service_chain_count = scc
config.service_chain = sc
config.service_chain_shared_net = shared_net
- config.rate = '1Mpps'
+ config.rate = rate
config['traffic_generator']['generator_profile'] = [{'name': 'dummy',
'tool': 'dummy',
'ip': '127.0.0.1',
- 'intf_speed': None,
+ 'intf_speed': '10Gbps',
'interfaces': [{'port': 0, 'pci': '0.0'},
{'port': 1, 'pci': '0.0'}]}]
config.ndr_run = False
config.duration_sec = 2
config.interval_sec = 1
config.openrc_file = "dummy.rc"
+ config.no_flow_stats = False
+ config.no_latency_stats = False
+ config.no_latency_streams = False
return config
def test_chain_runner_ext_no_openstack():
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']
- runner = ChainRunner(config, None, specs, BasicFactory())
- runner.close()
-def _mock_get_enabled_az_host_list(self, required_count=1):
- return ['nova:c1', 'nova:c2']
+ for shared_net in [True, False]:
+ for no_arp in [False, True]:
+ for vlan_tag in [False, True]:
+ for scc in [1, 2]:
+ config = _get_chain_config(ChainType.EXT, scc, shared_net)
+ config.no_arp = no_arp
+ if no_arp:
+ # If EXT and no arp, the config must provide mac (1 pair per chain)
+ config['traffic_generator']['mac_addrs_left'] = ['00:00:00:00:00:00'] * scc
+ config['traffic_generator']['mac_addrs_right'] = ['00:00:00:00:01:00'] * scc
+ config['vlan_tagging'] = vlan_tag
+ if vlan_tag:
+ # these are the 2 valid forms of vlan ranges
+ if scc == 1:
+ config.vlans = [100, 200]
+ else:
+ config.vlans = [[port * 100 + index for index in range(scc)]
+ for port in range(2)]
+ runner = ChainRunner(config, None, specs, BasicFactory())
+ runner.close()
+
def _mock_find_image(self, image_name):
- return True
+ return MagicMock()
-@patch.object(Compute, 'get_enabled_az_host_list', _mock_get_enabled_az_host_list)
@patch.object(Compute, 'find_image', _mock_find_image)
@patch('nfvbench.chaining.Client')
@patch('nfvbench.chaining.neutronclient')
openstack_spec = OpenStackSpec()
specs.set_openstack_spec(openstack_spec)
cred = MagicMock(spec=nfvbench.credentials.Credentials)
+ cred.is_admin = True
runner = ChainRunner(config, cred, specs, BasicFactory())
runner.close()
def test_pvp_chain_runner():
"""Test PVP chain runner."""
cred = MagicMock(spec=nfvbench.credentials.Credentials)
+ cred.is_admin = True
for shared_net in [True, False]:
for sc in [ChainType.PVP]:
for scc in [1, 2]:
config = _get_chain_config(sc, scc, shared_net)
_test_pvp_chain(config, cred)
-@patch.object(Compute, 'get_enabled_az_host_list', _mock_get_enabled_az_host_list)
+
+# Test not admin exception with empty value is raised
+@patch.object(Compute, 'find_image', _mock_find_image)
+@patch('nfvbench.chaining.Client')
+@patch('nfvbench.chaining.neutronclient')
+@patch('nfvbench.chaining.glanceclient')
+def _test_pvp_chain_no_admin_no_config_values(config, cred, mock_glance, mock_neutron, mock_client):
+ # instance = self.novaclient.servers.create(name=vmname,...)
+ # instance.status == 'ACTIVE'
+ mock_client.return_value.servers.create.return_value.status = 'ACTIVE'
+ netw = {'id': 0, 'provider:network_type': 'vlan', 'provider:segmentation_id': 1000}
+ mock_neutron.Client.return_value.create_network.return_value = {'network': netw}
+ mock_neutron.Client.return_value.list_networks.return_value = {'networks': None}
+ specs = Specs()
+ openstack_spec = OpenStackSpec()
+ specs.set_openstack_spec(openstack_spec)
+ runner = ChainRunner(config, cred, specs, BasicFactory())
+ runner.close()
+
+def test_pvp_chain_runner_no_admin_no_config_values():
+ """Test PVP/mock chain runner."""
+ cred = MagicMock(spec=nfvbench.credentials.Credentials)
+ cred.is_admin = False
+ for shared_net in [True, False]:
+ for sc in [ChainType.PVP]:
+ for scc in [1, 2]:
+ config = _get_chain_config(sc, scc, shared_net)
+ with pytest.raises(ChainException):
+ _test_pvp_chain_no_admin_no_config_values(config, cred)
+
+# Test not admin with mandatory parameters values in config file
+@patch.object(Compute, 'find_image', _mock_find_image)
+@patch('nfvbench.chaining.Client')
+@patch('nfvbench.chaining.neutronclient')
+@patch('nfvbench.chaining.glanceclient')
+def _test_pvp_chain_no_admin_config_values(config, cred, mock_glance, mock_neutron, mock_client):
+ # instance = self.novaclient.servers.create(name=vmname,...)
+ # instance.status == 'ACTIVE'
+ mock_client.return_value.servers.create.return_value.status = 'ACTIVE'
+ netw = {'id': 0, 'provider:network_type': 'vlan', 'provider:segmentation_id': 1000}
+ mock_neutron.Client.return_value.create_network.return_value = {'network': netw}
+ mock_neutron.Client.return_value.list_networks.return_value = {'networks': None}
+ specs = Specs()
+ openstack_spec = OpenStackSpec()
+ specs.set_openstack_spec(openstack_spec)
+ runner = ChainRunner(config, cred, specs, BasicFactory())
+ runner.close()
+
+def test_pvp_chain_runner_no_admin_config_values():
+ """Test PVP chain runner."""
+ cred = MagicMock(spec=nfvbench.credentials.Credentials)
+ cred.is_admin = False
+ for shared_net in [True, False]:
+ for sc in [ChainType.PVP]:
+ for scc in [1, 2]:
+ config = _get_chain_config(sc, scc, shared_net)
+ config.availability_zone = "az"
+ config.hypervisor_hostname = "server"
+ # these are the 2 valid forms of vlan ranges
+ if scc == 1:
+ config.vlans = [100, 200]
+ else:
+ config.vlans = [[port * 100 + index for index in range(scc)]
+ for port in range(2)]
+ _test_pvp_chain_no_admin_config_values(config, cred)
+
+
@patch.object(Compute, 'find_image', _mock_find_image)
@patch('nfvbench.chaining.Client')
@patch('nfvbench.chaining.neutronclient')
openstack_spec = OpenStackSpec()
specs.set_openstack_spec(openstack_spec)
cred = MagicMock(spec=nfvbench.credentials.Credentials)
+ cred.is_admin = True
runner = ChainRunner(config, cred, specs, BasicFactory())
runner.close()
def test_ext_chain_runner():
- """Test openstack+EXT chain runner."""
+ """Test openstack+EXT chain runner.
+
+ Test 8 combinations of configs:
+ shared/not shared net x arp/no_arp x scc 1 or 2
+ """
cred = MagicMock(spec=nfvbench.credentials.Credentials)
+ cred.is_admin = True
for shared_net in [True, False]:
for no_arp in [False, True]:
for scc in [1, 2]:
config = _get_chain_config(ChainType.EXT, scc, shared_net)
config.no_arp = no_arp
+ # this time use a tuple of network names
+ config['external_networks']['left'] = ('ext-lnet00', 'ext-lnet01')
+ config['external_networks']['right'] = ('ext-rnet00', 'ext-rnet01')
if no_arp:
# If EXT and no arp, the config must provide mac addresses (1 pair per chain)
config['traffic_generator']['mac_addrs_left'] = ['00:00:00:00:00:00'] * scc
if l2_loopback:
config.l2_loopback = True
config.vlans = [[100], [200]]
+ if sc == ChainType.EXT:
+ config['external_networks']['left'] = 'ext-lnet'
+ config['external_networks']['right'] = 'ext-rnet'
factory = BasicFactory()
config_plugin = factory.get_config_plugin_class()(config)
config = config_plugin.get_config()
nfvb = NFVBench(config, openstack_spec, config_plugin, factory)
res = nfvb.run({}, 'pytest')
if res['status'] != 'OK':
- print res
+ print(res)
assert res['status'] == 'OK'
-@patch.object(Compute, 'get_enabled_az_host_list', _mock_get_enabled_az_host_list)
+
+mac_seq = 0
+
+def _mock_get_mac(dummy):
+ global mac_seq
+ mac_seq += 1
+ return '01:00:00:00:00:%02x' % mac_seq
+
@patch.object(Compute, 'find_image', _mock_find_image)
@patch.object(TrafficClient, 'skip_sleep', lambda x: True)
+@patch.object(ChainVnfPort, 'get_mac', _mock_get_mac)
+@patch.object(TrafficClient, 'is_udp', lambda x, y: True)
@patch('nfvbench.chaining.Client')
@patch('nfvbench.chaining.neutronclient')
@patch('nfvbench.chaining.glanceclient')
mock_neutron.Client.return_value.list_networks.return_value = {'networks': None}
_check_nfvbench_openstack()
-@patch.object(Compute, 'get_enabled_az_host_list', _mock_get_enabled_az_host_list)
@patch.object(Compute, 'find_image', _mock_find_image)
@patch.object(TrafficClient, 'skip_sleep', lambda x: True)
+@patch.object(TrafficClient, 'is_udp', lambda x, y: True)
@patch('nfvbench.chaining.Client')
@patch('nfvbench.chaining.neutronclient')
@patch('nfvbench.chaining.glanceclient')
mock_neutron.Client.return_value.list_networks.return_value = {'networks': [netw]}
_check_nfvbench_openstack(sc=ChainType.EXT)
-@patch.object(Compute, 'get_enabled_az_host_list', _mock_get_enabled_az_host_list)
@patch.object(Compute, 'find_image', _mock_find_image)
@patch.object(TrafficClient, 'skip_sleep', lambda x: True)
+@patch.object(TrafficClient, 'is_udp', lambda x, y: True)
@patch('nfvbench.chaining.Client')
@patch('nfvbench.chaining.neutronclient')
@patch('nfvbench.chaining.glanceclient')
assert if_stats[1].tx == CH1_P1_TX + LCH1_P1_TX
assert if_stats[1].rx == CH1_P1_RX + LCH1_P1_RX
+def check_placer(az, hyp, req_az, resolved=False):
+ """Combine multiple combinatoons of placer tests."""
+ placer = InstancePlacer(az, hyp)
+ assert placer.is_resolved() == resolved
+ assert placer.get_required_az() == req_az
+ assert placer.register_full_name('nova:comp1')
+ assert placer.is_resolved()
+ assert placer.get_required_az() == 'nova:comp1'
+
+def test_placer_no_user_pref():
+ """Test placement when user does not provide any preference."""
+ check_placer(None, None, '')
+
+def test_placer_user_az():
+ """Test placement when user only provides an az."""
+ check_placer('nova', None, 'nova:')
+ check_placer(None, 'nova:', 'nova:')
+ check_placer('nebula', 'nova:', 'nova:')
+
+def test_placer_user_hyp():
+ """Test placement when user provides a hypervisor."""
+ check_placer(None, 'comp1', ':comp1')
+ check_placer('nova', 'comp1', 'nova:comp1', resolved=True)
+ check_placer(None, 'nova:comp1', 'nova:comp1', resolved=True)
+ # hyp overrides az
+ check_placer('nebula', 'nova:comp1', 'nova:comp1', resolved=True)
+ # also check for cases of extra parts (more than 1 ':')
+ check_placer('nova:nebula', 'comp1', 'nova:comp1', resolved=True)
+
+
+def test_placer_negative():
+ """Run negative tests on placer."""
+ # AZ mismatch
+ with pytest.raises(Exception):
+ placer = InstancePlacer('nova', None)
+ placer.register('nebula:comp1')
+ # comp mismatch
+ with pytest.raises(Exception):
+ placer = InstancePlacer(None, 'comp1')
+ placer.register('nebula:comp2')
+
# without total, with total and only 2 col
CHAIN_STATS = [{0: {'packets': [2000054, 1999996, 1999996]}},
'total': {'packets': [30000004, 30000004, 30000004, 30000004, 30000004, 30000004]}},
{0: {'packets': [15000002, '', 14000002, 14000002, '', 13000002]},
1: {'packets': [15000002, '', 15000002, 15000002, '', 15000002]},
- 'total': {'packets': [30000004, 29000004, 29000004, 29000004, 29000004, 28000004]}}]
+ 'total': {'packets': [30000004, 29000004, 29000004, 29000004, 29000004, 28000004]}},
+ # example with non-available rx count in last position
+ {0: {'packets': [2000054, 1999996, None]},
+ 1: {'packets': [2000054, 2000054, None]},
+ 'total': {'packets': [4000108, 4000050, 4000050]}}]
XP_CHAIN_STATS = [{0: {'packets': [2000054, '-58 (-0.0029%)', 1999996]}},
{0: {'packets': [2000054, '-58 (-0.0029%)', 1999996]},
1: {'packets': [2000054, '=>', 2000054]},
'-1,000,000 (-7.1429%)']},
1: {'packets': [15000002, '', '=>', '=>', '', 15000002]},
'total': {'packets': [30000004, '-1,000,000 (-3.3333%)', '=>', '=>', '=>',
- '-1,000,000 (-3.4483%)']}}]
+ '-1,000,000 (-3.4483%)']}},
+ {0: {'packets': [2000054, '-58 (-0.0029%)', 'n/a']},
+ 1: {'packets': [2000054, '=>', 'n/a']},
+ 'total': {'packets': [4000108, '-58 (-0.0014%)', 4000050]}}]
def test_summarizer():
for stats, exp_stats in zip(CHAIN_STATS, XP_CHAIN_STATS):
_annotate_chain_stats(stats)
assert stats == exp_stats
+
+@patch.object(TrafficClient, 'skip_sleep', lambda x: True)
+@patch.object(TrafficClient, 'is_udp', lambda x, y: True)
+def test_fixed_rate_no_openstack():
+ """Test FIxed Rate run - no openstack."""
+ config = _get_chain_config(ChainType.EXT, 1, True, rate='100%')
+ specs = Specs()
+ 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']
+ config.no_arp = True
+ config['vlan_tagging'] = True
+ config['traffic'] = {'profile': 'profile_64',
+ 'bidirectional': True}
+ config['traffic_profile'] = [{'name': 'profile_64', 'l2frame_size': ['64']}]
+
+ runner = ChainRunner(config, None, specs, BasicFactory())
+ tg = runner.traffic_client.gen
+
+ tg.set_response_curve(lr_dr=0, ndr=100, max_actual_tx=50, max_11_tx=50)
+ # tx packets should be 50% at requested 50% line rate or higher for 64B and no drops...
+ results = runner.run()
+ assert results
+ # pprint.pprint(results['EXT']['result']['result']['64'])
+ runner.close()