NFVBENCH-201 Fix port creation with accurate subnet_id
[nfvbench.git] / test / test_chains.py
1 #!/usr/bin/env python
2 # Copyright 2016 Cisco Systems, Inc.  All rights reserved.
3 #
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
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
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
14 #    under the License.
15 #
16 """Test Chaining functions."""
17
18 from mock import MagicMock
19 from mock import patch
20 import pytest
21
22 from .mock_trex import no_op
23
24 from nfvbench.chain_runner import ChainRunner
25 from nfvbench.chaining import ChainException
26 from nfvbench.chaining import ChainVnfPort
27 from nfvbench.chaining import InstancePlacer
28 from nfvbench.compute import Compute
29 import nfvbench.credentials
30 from nfvbench.factory import BasicFactory
31 import nfvbench.log
32 from nfvbench.nfvbench import load_default_config
33 from nfvbench.nfvbench import NFVBench
34 from nfvbench.packet_stats import InterfaceStats
35 from nfvbench.specs import ChainType
36 from nfvbench.specs import OpenStackSpec
37 from nfvbench.specs import Specs
38 from nfvbench.summarizer import _annotate_chain_stats
39 from nfvbench.traffic_client import TrafficClient
40 from nfvbench.traffic_gen.traffic_base import Latency
41 from nfvbench.traffic_gen.trex_gen import TRex
42 from nfvbench import utils
43
44 # just to get rid of the unused function warning
45 no_op()
46
47
48 def setup_module(module):
49     """Enable log."""
50     nfvbench.log.setup(mute_stdout=False)
51     nfvbench.log.set_level(debug=True)
52
53 def _get_chain_config(sc=ChainType.PVP, scc=1, shared_net=True, rate='1Mpps'):
54     config, _ = load_default_config()
55     config.vm_image_file = 'nfvbenchvm-0.0.qcow2'
56     config.service_chain_count = scc
57     config.service_chain = sc
58     config.service_chain_shared_net = shared_net
59     config.rate = rate
60     config['traffic_generator']['generator_profile'] = [{'name': 'dummy',
61                                                          'tool': 'dummy',
62                                                          'ip': '127.0.0.1',
63                                                          'intf_speed': '10Gbps',
64                                                          'interfaces': [{'port': 0, 'pci': '0.0'},
65                                                                         {'port': 1, 'pci': '0.0'}]}]
66     config.ndr_run = False
67     config.pdr_run = False
68     config.single_run = True
69     config.generator_profile = 'dummy'
70     config.duration_sec = 2
71     config.interval_sec = 1
72     config.openrc_file = "dummy.rc"
73     config.no_flow_stats = False
74     config.no_latency_stats = False
75     config.no_latency_streams = False
76     config.loop_vm_arp = True
77     return config
78
79 def test_chain_runner_ext_no_openstack():
80     """Test ChainRunner EXT no openstack."""
81     config = _get_chain_config(sc=ChainType.EXT)
82     specs = Specs()
83     config.vlans = [100, 200]
84     config['traffic_generator']['mac_addrs_left'] = ['00:00:00:00:00:00']
85     config['traffic_generator']['mac_addrs_right'] = ['00:00:00:00:01:00']
86
87     for shared_net in [True, False]:
88         for no_arp in [False, True]:
89             for vlan_tag in [False, True]:
90                 for scc in [1, 2]:
91                     config = _get_chain_config(ChainType.EXT, scc, shared_net)
92                     config.no_arp = no_arp
93                     if no_arp:
94                         # If EXT and no arp, the config must provide mac (1 pair per chain)
95                         config['traffic_generator']['mac_addrs_left'] = ['00:00:00:00:00:00'] * scc
96                         config['traffic_generator']['mac_addrs_right'] = ['00:00:00:00:01:00'] * scc
97                     config['vlan_tagging'] = vlan_tag
98                     if vlan_tag:
99                         # these are the 2 valid forms of vlan ranges
100                         if scc == 1:
101                             config.vlans = [100, 200]
102                         else:
103                             config.vlans = [[port * 100 + index for index in range(scc)]
104                                             for port in range(2)]
105                     runner = ChainRunner(config, None, specs, BasicFactory())
106                     runner.close()
107
108
109 def _mock_find_image(self, image_name):
110     return MagicMock()
111
112 def _mock_waiting_servers_deletion(nova_client, servers):
113     return MagicMock()
114
115 @patch.object(Compute, 'find_image', _mock_find_image)
116 @patch.object(utils, 'waiting_servers_deletion', _mock_waiting_servers_deletion)
117 @patch('nfvbench.chaining.Client')
118 @patch('nfvbench.chaining.neutronclient')
119 @patch('nfvbench.chaining.glanceclient')
120 def _test_pvp_chain(config, cred, mock_glance, mock_neutron, mock_client):
121     # instance = self.novaclient.servers.create(name=vmname,...)
122     # instance.status == 'ACTIVE'
123     mock_client.return_value.servers.create.return_value.status = 'ACTIVE'
124     netw = {'id': 0, 'provider:network_type': 'vlan', 'provider:segmentation_id': 1000}
125     mock_neutron.Client.return_value.create_network.return_value = {'network': netw}
126     mock_neutron.Client.return_value.list_networks.return_value = {'networks': None}
127     specs = Specs()
128     openstack_spec = OpenStackSpec()
129     specs.set_openstack_spec(openstack_spec)
130     cred = MagicMock(spec=nfvbench.credentials.Credentials)
131     cred.is_admin = True
132     runner = ChainRunner(config, cred, specs, BasicFactory())
133     runner.close()
134
135 def test_pvp_chain_runner():
136     """Test PVP chain runner."""
137     cred = MagicMock(spec=nfvbench.credentials.Credentials)
138     cred.is_admin = True
139     for shared_net in [True, False]:
140         for sc in [ChainType.PVP]:
141             for scc in [1, 2]:
142                 config = _get_chain_config(sc, scc, shared_net)
143                 _test_pvp_chain(config, cred)
144
145
146 # Test not admin exception with empty value is raised
147 @patch.object(Compute, 'find_image', _mock_find_image)
148 @patch.object(utils, 'waiting_servers_deletion', _mock_waiting_servers_deletion)
149 @patch('nfvbench.chaining.Client')
150 @patch('nfvbench.chaining.neutronclient')
151 @patch('nfvbench.chaining.glanceclient')
152 def _test_pvp_chain_no_admin_no_config_values(config, cred, mock_glance, mock_neutron, mock_client):
153     # instance = self.novaclient.servers.create(name=vmname,...)
154     # instance.status == 'ACTIVE'
155     mock_client.return_value.servers.create.return_value.status = 'ACTIVE'
156     netw = {'id': 0, 'provider:network_type': 'vlan', 'provider:segmentation_id': 1000}
157     mock_neutron.Client.return_value.create_network.return_value = {'network': netw}
158     mock_neutron.Client.return_value.list_networks.return_value = {'networks': None}
159     specs = Specs()
160     openstack_spec = OpenStackSpec()
161     specs.set_openstack_spec(openstack_spec)
162     runner = ChainRunner(config, cred, specs, BasicFactory())
163     runner.close()
164
165 def test_pvp_chain_runner_no_admin_no_config_values():
166     """Test PVP/mock chain runner."""
167     cred = MagicMock(spec=nfvbench.credentials.Credentials)
168     cred.is_admin = False
169     for shared_net in [True, False]:
170         for sc in [ChainType.PVP]:
171             for scc in [1, 2]:
172                 config = _get_chain_config(sc, scc, shared_net)
173                 with pytest.raises(ChainException):
174                     _test_pvp_chain_no_admin_no_config_values(config, cred)
175
176 # Test not admin with mandatory parameters values in config file
177 @patch.object(Compute, 'find_image', _mock_find_image)
178 @patch.object(utils, 'waiting_servers_deletion', _mock_waiting_servers_deletion)
179 @patch('nfvbench.chaining.Client')
180 @patch('nfvbench.chaining.neutronclient')
181 @patch('nfvbench.chaining.glanceclient')
182 def _test_pvp_chain_no_admin_config_values(config, cred, mock_glance, mock_neutron, mock_client):
183     # instance = self.novaclient.servers.create(name=vmname,...)
184     # instance.status == 'ACTIVE'
185     mock_client.return_value.servers.create.return_value.status = 'ACTIVE'
186     netw = {'id': 0, 'provider:network_type': 'vlan', 'provider:segmentation_id': 1000}
187     mock_neutron.Client.return_value.create_network.return_value = {'network': netw}
188     mock_neutron.Client.return_value.list_networks.return_value = {'networks': None}
189     specs = Specs()
190     openstack_spec = OpenStackSpec()
191     specs.set_openstack_spec(openstack_spec)
192     runner = ChainRunner(config, cred, specs, BasicFactory())
193     runner.close()
194
195 def test_pvp_chain_runner_no_admin_config_values():
196     """Test PVP chain runner."""
197     cred = MagicMock(spec=nfvbench.credentials.Credentials)
198     cred.is_admin = False
199     for shared_net in [True, False]:
200         for sc in [ChainType.PVP]:
201             for scc in [1, 2]:
202                 config = _get_chain_config(sc, scc, shared_net)
203                 config.availability_zone = "az"
204                 config.hypervisor_hostname = "server"
205                 # these are the 2 valid forms of vlan ranges
206                 if scc == 1:
207                     config.vlans = [100, 200]
208                 else:
209                     config.vlans = [[port * 100 + index for index in range(scc)]
210                                     for port in range(2)]
211                 _test_pvp_chain_no_admin_config_values(config, cred)
212
213
214 @patch.object(Compute, 'find_image', _mock_find_image)
215 @patch('nfvbench.chaining.Client')
216 @patch('nfvbench.chaining.neutronclient')
217 @patch('nfvbench.chaining.glanceclient')
218 def _test_ext_chain(config, cred, mock_glance, mock_neutron, mock_client):
219     # instance = self.novaclient.servers.create(name=vmname,...)
220     # instance.status == 'ACTIVE'
221     mock_client.return_value.servers.create.return_value.status = 'ACTIVE'
222     netw = {'id': 0, 'provider:network_type': 'vlan', 'provider:segmentation_id': 1000}
223     mock_neutron.Client.return_value.list_networks.return_value = {'networks': [netw]}
224     specs = Specs()
225     openstack_spec = OpenStackSpec()
226     specs.set_openstack_spec(openstack_spec)
227     cred = MagicMock(spec=nfvbench.credentials.Credentials)
228     cred.is_admin = True
229     runner = ChainRunner(config, cred, specs, BasicFactory())
230     runner.close()
231
232 def test_ext_chain_runner():
233     """Test openstack+EXT chain runner.
234
235     Test 8 combinations of configs:
236     shared/not shared net x arp/no_arp x scc 1 or 2
237     """
238     cred = MagicMock(spec=nfvbench.credentials.Credentials)
239     cred.is_admin = True
240     for shared_net in [True, False]:
241         for no_arp in [False, True]:
242             for scc in [1, 2]:
243                 config = _get_chain_config(ChainType.EXT, scc, shared_net)
244                 config.no_arp = no_arp
245                 # this time use a tuple of network names
246                 config['external_networks']['left'] = ('ext-lnet00', 'ext-lnet01')
247                 config['external_networks']['right'] = ('ext-rnet00', 'ext-rnet01')
248                 if no_arp:
249                     # If EXT and no arp, the config must provide mac addresses (1 pair per chain)
250                     config['traffic_generator']['mac_addrs_left'] = ['00:00:00:00:00:00'] * scc
251                     config['traffic_generator']['mac_addrs_right'] = ['00:00:00:00:01:00'] * scc
252                 _test_ext_chain(config, cred)
253
254 def _check_nfvbench_openstack(sc=ChainType.PVP, l2_loopback=False):
255     for scc in range(1, 3):
256         config = _get_chain_config(sc, scc=scc, shared_net=True)
257         if l2_loopback:
258             config.l2_loopback = True
259             config.vlans = [[100], [200]]
260         if sc == ChainType.EXT:
261             config['external_networks']['left'] = 'ext-lnet'
262             config['external_networks']['right'] = 'ext-rnet'
263         factory = BasicFactory()
264         config_plugin = factory.get_config_plugin_class()(config)
265         config = config_plugin.get_config()
266         openstack_spec = config_plugin.get_openstack_spec()
267         nfvb = NFVBench(config, openstack_spec, config_plugin, factory)
268         res = nfvb.run({}, 'pytest')
269         if res['status'] != 'OK':
270             print(res)
271         assert res['status'] == 'OK'
272
273
274 mac_seq = 0
275
276 def _mock_get_mac(dummy):
277     global mac_seq
278     mac_seq += 1
279     return '01:00:00:00:00:%02x' % mac_seq
280
281 @patch.object(Compute, 'find_image', _mock_find_image)
282 @patch.object(TrafficClient, 'skip_sleep', lambda x: True)
283 @patch.object(ChainVnfPort, 'get_mac', _mock_get_mac)
284 @patch.object(TrafficClient, 'is_udp', lambda x, y: True)
285 @patch.object(utils, 'waiting_servers_deletion', _mock_waiting_servers_deletion)
286 @patch('nfvbench.chaining.Client')
287 @patch('nfvbench.chaining.neutronclient')
288 @patch('nfvbench.chaining.glanceclient')
289 @patch('nfvbench.nfvbench.credentials')
290 def test_nfvbench_run(mock_cred, mock_glance, mock_neutron, mock_client):
291     """Test NFVbench class with openstack+PVP."""
292     # instance = self.novaclient.servers.create(name=vmname,...)
293     # instance.status == 'ACTIVE'
294     mock_client.return_value.servers.create.return_value.status = 'ACTIVE'
295     netw = {'id': 0, 'provider:network_type': 'vlan', 'provider:segmentation_id': 1000}
296     mock_neutron.Client.return_value.create_network.return_value = {'network': netw}
297     mock_neutron.Client.return_value.list_networks.return_value = {'networks': None}
298     _check_nfvbench_openstack()
299
300 @patch.object(Compute, 'find_image', _mock_find_image)
301 @patch.object(TrafficClient, 'skip_sleep', lambda x: True)
302 @patch.object(TrafficClient, 'is_udp', lambda x, y: True)
303 @patch('nfvbench.chaining.Client')
304 @patch('nfvbench.chaining.neutronclient')
305 @patch('nfvbench.chaining.glanceclient')
306 @patch('nfvbench.nfvbench.credentials')
307 def test_nfvbench_ext_arp(mock_cred, mock_glance, mock_neutron, mock_client):
308     """Test NFVbench class with openstack+EXT+ARP."""
309     # instance = self.novaclient.servers.create(name=vmname,...)
310     # instance.status == 'ACTIVE'
311     mock_client.return_value.servers.create.return_value.status = 'ACTIVE'
312     netw = {'id': 0, 'provider:network_type': 'vlan', 'provider:segmentation_id': 1000}
313     mock_neutron.Client.return_value.list_networks.return_value = {'networks': [netw]}
314     _check_nfvbench_openstack(sc=ChainType.EXT)
315
316 @patch.object(Compute, 'find_image', _mock_find_image)
317 @patch.object(TrafficClient, 'skip_sleep', lambda x: True)
318 @patch.object(TrafficClient, 'is_udp', lambda x, y: True)
319 @patch('nfvbench.chaining.Client')
320 @patch('nfvbench.chaining.neutronclient')
321 @patch('nfvbench.chaining.glanceclient')
322 @patch('nfvbench.nfvbench.credentials')
323 def test_nfvbench_l2_loopback(mock_cred, mock_glance, mock_neutron, mock_client):
324     """Test NFVbench class with l2-loopback."""
325     # instance = self.novaclient.servers.create(name=vmname,...)
326     # instance.status == 'ACTIVE'
327     mock_client.return_value.servers.create.return_value.status = 'ACTIVE'
328     _check_nfvbench_openstack(l2_loopback=True)
329
330
331 # This is a reduced version of flow stats coming from Trex
332 # with 2 chains and latency for a total of 8 packet groups
333 # Random numbers with random losses
334 CH0_P0_TX = 1234
335 CH0_P1_RX = 1200
336 CH0_P1_TX = 28900
337 CH0_P0_RX = 28000
338 LCH0_P0_TX = 167
339 LCH0_P1_RX = 130
340 LCH0_P1_TX = 523
341 LCH0_P0_RX = 490
342 CH1_P0_TX = 132344
343 CH1_P1_RX = 132004
344 CH1_P1_TX = 1289300
345 CH1_P0_RX = 1280400
346 LCH1_P0_TX = 51367
347 LCH1_P1_RX = 5730
348 LCH1_P1_TX = 35623
349 LCH1_P0_RX = 67
350
351 TREX_STATS = {
352     'flow_stats': {
353         # chain 0 port 0 normal stream
354         0: {'rx_pkts': {0: 0, 1: CH0_P1_RX, 'total': CH0_P1_RX},
355             'tx_pkts': {0: CH0_P0_TX, 1: 0, 'total': CH0_P0_TX}},
356         # chain 1 port 0 normal stream
357         1: {'rx_pkts': {0: 0, 1: CH1_P1_RX, 'total': CH1_P1_RX},
358             'tx_pkts': {0: CH1_P0_TX, 1: 0, 'total': CH1_P0_TX}},
359         # chain 0 port 1 normal stream
360         128: {'rx_pkts': {0: CH0_P0_RX, 1: 0, 'total': CH0_P0_RX},
361               'tx_pkts': {0: 0, 1: CH0_P1_TX, 'total': CH0_P1_TX}},
362         # chain 1 port 1 normal stream
363         129: {'rx_pkts': {0: CH1_P0_RX, 1: 0, 'total': CH1_P0_RX},
364               'tx_pkts': {0: 0, 1: CH1_P1_TX, 'total': CH1_P1_TX}},
365         # chain 0 port 0 latency stream
366         256: {'rx_pkts': {0: 0, 1: LCH0_P1_RX, 'total': LCH0_P1_RX},
367               'tx_pkts': {0: LCH0_P0_TX, 1: 0, 'total': LCH0_P0_TX}},
368         # chain 1 port 0 latency stream
369         257: {'rx_pkts': {0: 0, 1: LCH1_P1_RX, 'total': LCH1_P1_RX},
370               'tx_pkts': {0: LCH1_P0_TX, 1: 0, 'total': LCH1_P0_TX}},
371         # chain 0 port 1 latency stream
372         384: {'rx_pkts': {0: LCH0_P0_RX, 1: 0, 'total': LCH0_P0_RX},
373               'tx_pkts': {0: 0, 1: LCH0_P1_TX, 'total': LCH0_P1_TX}},
374         # chain 1 port 1 latency stream
375         385: {'rx_pkts': {0: LCH1_P0_RX, 1: 0, 'total': LCH1_P0_RX},
376               'tx_pkts': {0: 0, 1: LCH1_P1_TX, 'total': LCH1_P1_TX}}}}
377
378 def test_trex_streams_stats():
379     """Test TRex stats for chains 0 and 1."""
380     traffic_client = MagicMock()
381     trex = TRex(traffic_client)
382     if_stats = [InterfaceStats("p0", "dev0"), InterfaceStats("p1", "dev1")]
383     latencies = [Latency()] * 2
384     trex.get_stream_stats(TREX_STATS, if_stats, latencies, 0)
385     assert if_stats[0].tx == CH0_P0_TX + LCH0_P0_TX
386     assert if_stats[0].rx == CH0_P0_RX + LCH0_P0_RX
387     assert if_stats[1].tx == CH0_P1_TX + LCH0_P1_TX
388     assert if_stats[1].rx == CH0_P1_RX + LCH0_P1_RX
389
390     trex.get_stream_stats(TREX_STATS, if_stats, latencies, 1)
391     assert if_stats[0].tx == CH1_P0_TX + LCH1_P0_TX
392     assert if_stats[0].rx == CH1_P0_RX + LCH1_P0_RX
393     assert if_stats[1].tx == CH1_P1_TX + LCH1_P1_TX
394     assert if_stats[1].rx == CH1_P1_RX + LCH1_P1_RX
395
396 def check_placer(az, hyp, req_az, resolved=False):
397     """Combine multiple combinatoons of placer tests."""
398     placer = InstancePlacer(az, hyp)
399     assert placer.is_resolved() == resolved
400     assert placer.get_required_az() == req_az
401     assert placer.register_full_name('nova:comp1')
402     assert placer.is_resolved()
403     assert placer.get_required_az() == 'nova:comp1'
404
405 def test_placer_no_user_pref():
406     """Test placement when user does not provide any preference."""
407     check_placer(None, None, '')
408
409 def test_placer_user_az():
410     """Test placement when user only provides an az."""
411     check_placer('nova', None, 'nova:')
412     check_placer(None, 'nova:', 'nova:')
413     check_placer('nebula', 'nova:', 'nova:')
414
415 def test_placer_user_hyp():
416     """Test placement when user provides a hypervisor."""
417     check_placer(None, 'comp1', ':comp1')
418     check_placer('nova', 'comp1', 'nova:comp1', resolved=True)
419     check_placer(None, 'nova:comp1', 'nova:comp1', resolved=True)
420     # hyp overrides az
421     check_placer('nebula', 'nova:comp1', 'nova:comp1', resolved=True)
422     # also check for cases of extra parts (more than 1 ':')
423     check_placer('nova:nebula', 'comp1', 'nova:comp1', resolved=True)
424
425
426 def test_placer_negative():
427     """Run negative tests on placer."""
428     # AZ mismatch
429     with pytest.raises(Exception):
430         placer = InstancePlacer('nova', None)
431         placer.register('nebula:comp1')
432     # comp mismatch
433     with pytest.raises(Exception):
434         placer = InstancePlacer(None, 'comp1')
435         placer.register('nebula:comp2')
436
437
438 # without total, with total and only 2 col
439 CHAIN_STATS = [{0: {'packets': [2000054, 1999996, 1999996]}},
440                {0: {'packets': [2000054, 1999996, 1999996]},
441                 1: {'packets': [2000054, 2000054, 2000054]},
442                 'total': {'packets': [4000108, 4000050, 4000050]}},
443                {0: {'packets': [2000054, 2000054]}},
444                {0: {'packets': [2000054, 1999996]}},
445                # shared networks no drops, shared nets will have empty strings
446                {0: {'packets': [15000002, '', 15000002, 15000002, '', 15000002]},
447                 1: {'packets': [15000002, '', 15000002, 15000002, '', 15000002]},
448                 'total': {'packets': [30000004, 30000004, 30000004, 30000004, 30000004, 30000004]}},
449                {0: {'packets': [15000002, '', 14000002, 14000002, '', 13000002]},
450                 1: {'packets': [15000002, '', 15000002, 15000002, '', 15000002]},
451                 'total': {'packets': [30000004, 29000004, 29000004, 29000004, 29000004, 28000004]}},
452                # example with non-available rx count in last position
453                {0: {'packets': [2000054, 1999996, None]},
454                 1: {'packets': [2000054, 2000054, None]},
455                 'total': {'packets': [4000108, 4000050, 4000050]}}]
456 XP_CHAIN_STATS = [{0: {'packets': [2000054, '-58 (-0.0029%)', 1999996]}},
457                   {0: {'packets': [2000054, '-58 (-0.0029%)', 1999996]},
458                    1: {'packets': [2000054, '=>', 2000054]},
459                    'total': {'packets': [4000108, '-58 (-0.0014%)', 4000050]}},
460                   {0: {'packets': [2000054, 2000054]}},
461                   {0: {'packets': [2000054, '-58 (-0.0029%)']}},
462                   # shared net, leave spaces alone
463                   {0: {'packets': [15000002, '', '=>', '=>', '', 15000002]},
464                    1: {'packets': [15000002, '', '=>', '=>', '', 15000002]},
465                    'total': {'packets': [30000004, '=>', '=>', '=>', '=>', 30000004]}},
466                   {0: {'packets': [15000002, '', '-1,000,000 (-6.6667%)', '=>', '',
467                                    '-1,000,000 (-7.1429%)']},
468                    1: {'packets': [15000002, '', '=>', '=>', '', 15000002]},
469                    'total': {'packets': [30000004, '-1,000,000 (-3.3333%)', '=>', '=>', '=>',
470                                          '-1,000,000 (-3.4483%)']}},
471                   {0: {'packets': [2000054, '-58 (-0.0029%)', 'n/a']},
472                    1: {'packets': [2000054, '=>', 'n/a']},
473                    'total': {'packets': [4000108, '-58 (-0.0014%)', 4000050]}}]
474
475
476 def test_summarizer():
477     """Test Summarizer class."""
478     for stats, exp_stats in zip(CHAIN_STATS, XP_CHAIN_STATS):
479         _annotate_chain_stats(stats)
480         assert stats == exp_stats
481
482 @patch.object(TrafficClient, 'skip_sleep', lambda x: True)
483 @patch.object(TrafficClient, 'is_udp', lambda x, y: True)
484 def test_fixed_rate_no_openstack():
485     """Test FIxed Rate run - no openstack."""
486     config = _get_chain_config(ChainType.EXT, 1, True, rate='100%')
487     specs = Specs()
488     config.vlans = [100, 200]
489     config['traffic_generator']['mac_addrs_left'] = ['00:00:00:00:00:00']
490     config['traffic_generator']['mac_addrs_right'] = ['00:00:00:00:01:00']
491     config.no_arp = True
492     config['vlan_tagging'] = True
493     config['traffic'] = {'profile': 'profile_64',
494                          'bidirectional': True}
495     config['traffic_profile'] = [{'name': 'profile_64', 'l2frame_size': ['64']}]
496
497     runner = ChainRunner(config, None, specs, BasicFactory())
498     tg = runner.traffic_client.gen
499
500     tg.set_response_curve(lr_dr=0, ndr=100, max_actual_tx=50, max_11_tx=50)
501     # tx packets should be 50% at requested 50% line rate or higher for 64B and no drops...
502     results = runner.run()
503     assert results
504     # pprint.pprint(results['EXT']['result']['result']['64'])
505     runner.close()