2 # Copyright 2016 Cisco Systems, Inc. All rights reserved.
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
17 from attrdict import AttrDict
19 from nfvbench.config import config_loads
20 from nfvbench.connection import SSH
21 from nfvbench.credentials import Credentials
22 from nfvbench.fluentd import FluentLogHandler
24 from nfvbench.network import Interface
25 from nfvbench.network import Network
26 from nfvbench.specs import Encaps
27 import nfvbench.traffic_gen.traffic_utils as traffic_utils
31 __location__ = os.path.realpath(os.path.join(os.getcwd(),
32 os.path.dirname(__file__)))
37 def mock_init(self, ssh_access, *args, **kwargs):
38 self.ssh_access = ssh_access
39 if ssh_access.private_key:
40 self.pkey = self._get_pkey(ssh_access.private_key)
44 self.connect_timeout = 2
45 self.connect_retry_count = 1
46 self.connect_retry_wait_sec = 1
47 super(SSH, self).__init__()
49 monkeypatch.setattr(SSH, '__init__', mock_init)
53 def openstack_vxlan_spec():
56 'openstack': AttrDict({
58 'encaps': Encaps.VxLAN}
60 'run_spec': AttrDict({
66 # =========================================================================
68 # =========================================================================
70 def test_chain_interface():
71 iface = Interface('testname', 'vpp', tx_packets=1234, rx_packets=4321)
72 assert iface.name == 'testname'
73 assert iface.device == 'vpp'
74 assert iface.get_packet_count('tx') == 1234
75 assert iface.get_packet_count('rx') == 4321
76 assert iface.get_packet_count('wrong_key') == 0
79 @pytest.fixture(scope='session')
81 return Interface('iface1', 'trex', tx_packets=10000, rx_packets=1234)
84 @pytest.fixture(scope='session')
86 return Interface('iface2', 'n9k', tx_packets=1234, rx_packets=9901)
89 @pytest.fixture(scope='session')
91 return Interface('iface3', 'n9k', tx_packets=9900, rx_packets=1234)
94 @pytest.fixture(scope='session')
96 return Interface('iface4', 'vpp', tx_packets=1234, rx_packets=9801)
99 @pytest.fixture(scope='session')
100 def net1(iface1, iface2, iface3, iface4):
101 return Network([iface1, iface2, iface3, iface4], reverse=False)
104 @pytest.fixture(scope='session')
105 def net2(iface1, iface2, iface3):
106 return Network([iface1, iface2, iface3], reverse=True)
109 def test_chain_network(net1, net2, iface1, iface2, iface3, iface4):
110 assert [iface1, iface2, iface3, iface4] == net1.get_interfaces()
111 assert [iface3, iface2, iface1] == net2.get_interfaces()
112 net2.add_interface(iface4)
113 assert [iface4, iface3, iface2, iface1] == net2.get_interfaces()
117 def test_chain_analysis(net1, monkeypatch, openstack_vxlan_spec):
118 def mock_empty(self, *args, **kwargs):
121 monkeypatch.setattr(ServiceChain, '_setup', mock_empty)
123 f = ServiceChain(AttrDict({'service_chain': 'DUMMY'}), [], {'tor': {}}, openstack_vxlan_spec,
124 lambda x, y, z: None)
125 result = f.get_analysis([net1])
126 assert result[1]['packet_drop_count'] == 99
127 assert result[1]['packet_drop_percentage'] == 0.99
128 assert result[2]['packet_drop_count'] == 1
129 assert result[2]['packet_drop_percentage'] == 0.01
130 assert result[3]['packet_drop_count'] == 99
131 assert result[3]['packet_drop_percentage'] == 0.99
134 result = f.get_analysis([net1])
135 assert result[1]['packet_drop_count'] == 0
136 assert result[1]['packet_drop_percentage'] == 0.0
137 assert result[2]['packet_drop_count'] == 0
138 assert result[2]['packet_drop_percentage'] == 0.0
139 assert result[3]['packet_drop_count'] == 0
140 assert result[3]['packet_drop_percentage'] == 0.0
144 def pvp_chain(monkeypatch, openstack_vxlan_spec):
145 tor_vni1 = Interface('vni-4097', 'n9k', 50, 77)
146 vsw_vni1 = Interface('vxlan_tunnel0', 'vpp', 77, 48)
147 vsw_vif1 = Interface('VirtualEthernet0/0/2', 'vpp', 48, 77)
148 vsw_vif2 = Interface('VirtualEthernet0/0/3', 'vpp', 77, 47)
149 vsw_vni2 = Interface('vxlan_tunnel1', 'vpp', 43, 77)
150 tor_vni2 = Interface('vni-4098', 'n9k', 77, 40)
152 def mock_init(self, *args, **kwargs):
153 self.vni_ports = [4097, 4098]
154 self.specs = openstack_vxlan_spec
157 'set_interface_counters': lambda: None,
160 self.worker = AttrDict({
164 def mock_empty(self, *args, **kwargs):
167 def mock_get_network(self, traffic_port, vni_id, reverse=False):
169 return Network([tor_vni1, vsw_vni1, vsw_vif1], reverse)
171 return Network([tor_vni2, vsw_vni2, vsw_vif2], reverse)
173 def mock_get_data(self):
176 monkeypatch.setattr(PVPChain, '_get_network', mock_get_network)
177 monkeypatch.setattr(PVPChain, '_get_data', mock_get_data)
178 monkeypatch.setattr(PVPChain, '_setup', mock_empty)
179 monkeypatch.setattr(VxLANWorker, '_clear_interfaces', mock_empty)
180 monkeypatch.setattr(PVPChain, '_generate_traffic', mock_empty)
181 monkeypatch.setattr(PVPChain, '__init__', mock_init)
182 return PVPChain(None, None, {'vm': None, 'vpp': None, 'tor': None, 'traffic': None}, None)
185 def test_pvp_chain_run(pvp_chain):
186 result = pvp_chain.run()
191 'direction-forward': [
193 ('interface', 'vni-4097'),
198 ('interface', 'vxlan_tunnel0'),
200 ('packet_count', 48),
201 ('packet_drop_count', 2),
202 ('packet_drop_percentage', 4.0)
205 ('interface', 'VirtualEthernet0/0/2'),
207 ('packet_count', 48),
208 ('packet_drop_count', 0),
209 ('packet_drop_percentage', 0.0)
212 ('interface', 'VirtualEthernet0/0/3'),
214 ('packet_count', 47),
215 ('packet_drop_count', 1),
216 ('packet_drop_percentage', 2.0)
219 ('interface', 'vxlan_tunnel1'),
221 ('packet_count', 43),
222 ('packet_drop_count', 4),
223 ('packet_drop_percentage', 8.0)
226 ('interface', 'vni-4098'),
228 ('packet_count', 40),
229 ('packet_drop_count', 3),
230 ('packet_drop_percentage', 6.0)
233 'direction-reverse': [
235 ('interface', 'vni-4098'),
240 ('interface', 'vxlan_tunnel1'),
242 ('packet_count', 77),
243 ('packet_drop_count', 0),
244 ('packet_drop_percentage', 0.0)
247 ('interface', 'VirtualEthernet0/0/3'),
249 ('packet_count', 77),
250 ('packet_drop_count', 0),
251 ('packet_drop_percentage', 0.0)
254 ('interface', 'VirtualEthernet0/0/2'),
256 ('packet_count', 77),
257 ('packet_drop_count', 0),
258 ('packet_drop_percentage', 0.0)
261 ('interface', 'vxlan_tunnel0'),
263 ('packet_count', 77),
264 ('packet_drop_count', 0),
265 ('packet_drop_percentage', 0.0)
268 ('interface', 'vni-4097'),
270 ('packet_count', 77),
271 ('packet_drop_count', 0),
272 ('packet_drop_percentage', 0.0)
277 assert result == expected_result
281 # =========================================================================
283 # =========================================================================
287 def pvvp_chain(monkeypatch, openstack_vxlan_spec):
288 tor_vni1 = Interface('vni-4097', 'n9k', 50, 77)
289 vsw_vni1 = Interface('vxlan_tunnel0', 'vpp', 77, 48)
290 vsw_vif1 = Interface('VirtualEthernet0/0/2', 'vpp', 48, 77)
291 vsw_vif3 = Interface('VirtualEthernet0/0/0', 'vpp', 77, 47)
292 vsw_vif4 = Interface('VirtualEthernet0/0/1', 'vpp', 45, 77)
293 vsw_vif2 = Interface('VirtualEthernet0/0/3', 'vpp', 77, 44)
294 vsw_vni2 = Interface('vxlan_tunnel1', 'vpp', 43, 77)
295 tor_vni2 = Interface('vni-4098', 'n9k', 77, 40)
297 def mock_init(self, *args, **kwargs):
298 self.vni_ports = [4099, 4100]
299 self.v2vnet = V2VNetwork()
300 self.specs = openstack_vxlan_spec
303 'get_v2v_network': lambda reverse=None: Network([vsw_vif3, vsw_vif4], reverse),
304 'set_interface_counters': lambda pvvp=None: None,
305 'set_v2v_counters': lambda: None,
308 self.worker = AttrDict({
312 def mock_empty(self, *args, **kwargs):
315 def mock_get_network(self, traffic_port, vni_id, reverse=False):
317 return Network([tor_vni1, vsw_vni1, vsw_vif1], reverse)
319 return Network([tor_vni2, vsw_vni2, vsw_vif2], reverse)
321 def mock_get_data(self):
324 monkeypatch.setattr(PVVPChain, '_get_network', mock_get_network)
325 monkeypatch.setattr(PVVPChain, '_get_data', mock_get_data)
326 monkeypatch.setattr(PVVPChain, '_setup', mock_empty)
327 monkeypatch.setattr(VxLANWorker, '_clear_interfaces', mock_empty)
328 monkeypatch.setattr(PVVPChain, '_generate_traffic', mock_empty)
329 monkeypatch.setattr(PVVPChain, '__init__', mock_init)
331 return PVVPChain(None, None, {'vm': None, 'vpp': None, 'tor': None, 'traffic': None}, None)
334 def test_pvvp_chain_run(pvvp_chain):
335 result = pvvp_chain.run()
341 {'direction-forward': [
343 ('interface', 'vni-4097'),
348 ('interface', 'vxlan_tunnel0'),
350 ('packet_count', 48),
351 ('packet_drop_count', 2),
352 ('packet_drop_percentage', 4.0)
355 ('interface', 'VirtualEthernet0/0/2'),
357 ('packet_count', 48),
358 ('packet_drop_count', 0),
359 ('packet_drop_percentage', 0.0)
362 ('interface', 'VirtualEthernet0/0/0'),
364 ('packet_count', 47),
365 ('packet_drop_count', 1),
366 ('packet_drop_percentage', 2.0)
369 ('interface', 'VirtualEthernet0/0/1'),
371 ('packet_count', 45),
372 ('packet_drop_count', 2),
373 ('packet_drop_percentage', 4.0)
376 ('interface', 'VirtualEthernet0/0/3'),
378 ('packet_count', 44),
379 ('packet_drop_count', 1),
380 ('packet_drop_percentage', 2.0)
383 ('interface', 'vxlan_tunnel1'),
385 ('packet_count', 43),
386 ('packet_drop_count', 1),
387 ('packet_drop_percentage', 2.0)
390 ('interface', 'vni-4098'),
392 ('packet_count', 40),
393 ('packet_drop_count', 3),
394 ('packet_drop_percentage', 6.0)
397 'direction-reverse': [
399 ('interface', 'vni-4098'),
404 ('interface', 'vxlan_tunnel1'),
406 ('packet_count', 77),
407 ('packet_drop_count', 0),
408 ('packet_drop_percentage', 0.0)
411 ('interface', 'VirtualEthernet0/0/3'),
413 ('packet_count', 77),
414 ('packet_drop_count', 0),
415 ('packet_drop_percentage', 0.0)
418 ('interface', 'VirtualEthernet0/0/1'),
420 ('packet_count', 77),
421 ('packet_drop_count', 0),
422 ('packet_drop_percentage', 0.0)
425 ('interface', 'VirtualEthernet0/0/0'),
427 ('packet_count', 77),
428 ('packet_drop_count', 0),
429 ('packet_drop_percentage', 0.0)
432 ('interface', 'VirtualEthernet0/0/2'),
434 ('packet_count', 77),
435 ('packet_drop_count', 0),
436 ('packet_drop_percentage', 0.0)
439 ('interface', 'vxlan_tunnel0'),
441 ('packet_count', 77),
442 ('packet_drop_count', 0),
443 ('packet_drop_percentage', 0.0)
446 ('interface', 'vni-4097'),
448 ('packet_count', 77),
449 ('packet_drop_count', 0),
450 ('packet_drop_percentage', 0.0)
454 assert result == expected_result
457 # =========================================================================
458 # Traffic client tests
459 # =========================================================================
461 def test_parse_rate_str():
462 parse_rate_str = traffic_utils.parse_rate_str
464 assert parse_rate_str('100%') == {'rate_percent': '100.0'}
465 assert parse_rate_str('37.5%') == {'rate_percent': '37.5'}
466 assert parse_rate_str('100%') == {'rate_percent': '100.0'}
467 assert parse_rate_str('60pps') == {'rate_pps': '60'}
468 assert parse_rate_str('60kpps') == {'rate_pps': '60000'}
469 assert parse_rate_str('6Mpps') == {'rate_pps': '6000000'}
470 assert parse_rate_str('6gpps') == {'rate_pps': '6000000000'}
471 assert parse_rate_str('80bps') == {'rate_bps': '80'}
472 assert parse_rate_str('80bps') == {'rate_bps': '80'}
473 assert parse_rate_str('80kbps') == {'rate_bps': '80000'}
474 assert parse_rate_str('80kBps') == {'rate_bps': '640000'}
475 assert parse_rate_str('80Mbps') == {'rate_bps': '80000000'}
476 assert parse_rate_str('80 MBps') == {'rate_bps': '640000000'}
477 assert parse_rate_str('80Gbps') == {'rate_bps': '80000000000'}
478 except Exception as exc:
479 assert False, exc.message
481 def should_raise_error(str):
489 assert should_raise_error('101')
490 assert should_raise_error('201%')
491 assert should_raise_error('10Kbps')
492 assert should_raise_error('0kbps')
493 assert should_raise_error('0pps')
494 assert should_raise_error('-1bps')
496 def test_rate_conversion():
497 assert traffic_utils.load_to_bps(50, 10000000000) == pytest.approx(5000000000.0)
498 assert traffic_utils.load_to_bps(37, 10000000000) == pytest.approx(3700000000.0)
499 assert traffic_utils.load_to_bps(100, 10000000000) == pytest.approx(10000000000.0)
501 assert traffic_utils.bps_to_load(5000000000.0, 10000000000) == pytest.approx(50.0)
502 assert traffic_utils.bps_to_load(3700000000.0, 10000000000) == pytest.approx(37.0)
503 assert traffic_utils.bps_to_load(10000000000.0, 10000000000) == pytest.approx(100.0)
505 assert traffic_utils.bps_to_pps(500000, 64) == pytest.approx(744.047619048)
506 assert traffic_utils.bps_to_pps(388888, 1518) == pytest.approx(31.6066319896)
507 assert traffic_utils.bps_to_pps(9298322222, 340.3) == pytest.approx(3225895.85831)
509 assert traffic_utils.pps_to_bps(744.047619048, 64) == pytest.approx(500000)
510 assert traffic_utils.pps_to_bps(31.6066319896, 1518) == pytest.approx(388888)
511 assert traffic_utils.pps_to_bps(3225895.85831, 340.3) == pytest.approx(9298322222)
516 def traffic_client(monkeypatch):
518 def mock_init(self, *args, **kwargs):
520 'bidirectional': False,
521 'l2frame_size': '64',
523 'rates': [{'rate_percent': '10'}, {'rate_pps': '1'}]
526 self.config = AttrDict({
527 'generator_config': {
528 'intf_speed': 10000000000
541 self.runner = AttrDict({
542 'time_elapsed': lambda: 30,
543 'stop': lambda: None,
544 'client': AttrDict({'get_stats': lambda: None})
547 self.current_load = None
554 14.0625: 2.47154392563,
555 13.28125: 0.000663797066801,
558 13.18359375: 0.00359387347122,
559 13.671875: 0.307939922531,
560 13.4765625: 0.0207718516156,
561 13.57421875: 0.0661795060969
564 def mock_modify_load(self, load):
565 self.run_config['rates'][0] = {'rate_percent': str(load)}
566 self.current_load = load
568 def mock_run_traffic(self):
571 'drop_rate_percent': self.dummy_stats[self.current_load],
574 'avg_delay_usec': 0.0,
575 'max_delay_usec': 0.0,
576 'min_delay_usec': 0.0
581 monkeypatch.setattr(TrafficClient, '__init__', mock_init)
582 monkeypatch.setattr(TrafficClient, 'modify_load', mock_modify_load)
583 monkeypatch.setattr(TrafficClient, 'run_traffic', mock_run_traffic)
585 return TrafficClient()
588 def test_ndr_pdr_search(traffic_client):
591 'l2frame_size': '64',
592 'initial_rate_type': 'rate_percent',
595 'drop_rate_percent': 0.0661795060969,
596 'min_delay_usec': 0.0,
597 'avg_delay_usec': 0.0,
598 'max_delay_usec': 0.0
601 'load_percent_per_direction': 13.57421875,
602 'rate_percent': 13.57422547,
603 'rate_bps': 1357422547.0,
604 'rate_pps': 2019974.0282738095,
608 'l2frame_size': '64',
609 'initial_rate_type': 'rate_percent',
612 'drop_rate_percent': 0.0,
613 'min_delay_usec': 0.0,
614 'avg_delay_usec': 0.0,
615 'max_delay_usec': 0.0
618 'load_percent_per_direction': 13.0859375,
619 'rate_percent': 13.08594422,
620 'rate_bps': 1308594422.0,
621 'rate_pps': 1947313.1279761905,
626 results = traffic_client.get_ndr_and_pdr()
627 assert len(results) == 2
628 for result in results.values():
629 result.pop('timestamp_sec')
630 result.pop('time_taken_sec')
631 assert results == expected_results
634 # =========================================================================
636 # =========================================================================
638 def setup_module(module):
639 nfvbench.log.setup(mute_stdout=True)
641 def test_no_credentials():
642 cred = Credentials('/completely/wrong/path/openrc', None, False)
644 # shouldn't get valid data unless user set environment variables
650 refcfg = {1: 100, 2: {21: 100, 22: 200}, 3: None}
651 res1 = {1: 10, 2: {21: 100, 22: 200}, 3: None}
652 res2 = {1: 100, 2: {21: 1000, 22: 200}, 3: None}
653 res3 = {1: 100, 2: {21: 100, 22: 200}, 3: "abc"}
654 assert(config_loads("{}", refcfg) == refcfg)
655 assert(config_loads("{1: 10}", refcfg) == res1)
656 assert(config_loads("{2: {21: 1000}}", refcfg) == res2)
657 assert(config_loads('{3: "abc"}', refcfg) == res3)
660 # pairs of input string and expected subset (None if identical)
663 ["{2: {21: 100, 30: 50}}", "{2: {30: 50}}"],
664 ["{2: {0: 1, 1: 2}, 5: 5}", None],
665 ["{1: 'abc', 2: {21: 0}}", "{1: 'abc'}"],
668 for fail_pair in fail_pairs:
669 with pytest.raises(Exception) as e_info:
670 config_loads(fail_pair[0], refcfg)
671 expected = fail_pair[1]
673 expected = fail_pair[0]
674 assert expected in e_info.value.message
677 flavor = {'flavor': {'vcpus': 2, 'ram': 8192, 'disk': 0,
678 'extra_specs': {'hw:cpu_policy': 'dedicated'}}}
679 new_flavor = {'flavor': {'vcpus': 2, 'ram': 8192, 'disk': 0,
680 'extra_specs': {'hw:cpu_policy': 'dedicated', 'hw:numa_nodes': 2}}}
681 assert(config_loads("{'flavor': {'extra_specs': {'hw:numa_nodes': 2}}}", flavor,
682 whitelist_keys=['alpha', 'extra_specs']) == new_flavor)
685 logger = logging.getLogger('fluent-logger')
686 handler = FluentLogHandler('nfvbench', fluentd_port=7081)
687 logger.addHandler(handler)
688 logger.setLevel(logging.INFO)
690 logger.warning('test %d', 100)
692 raise Exception("test")
694 logger.exception("got exception")