Merge "Yardstick TC082: move sample test case perf.yaml"
[yardstick.git] / tests / unit / benchmark / scenarios / networking / test_vnf_generic.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2016-2017 Intel Corporation
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 #      http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17
18 # Unittest for yardstick.benchmark.scenarios.networking.test_vnf_generic
19
20 from __future__ import absolute_import
21
22 import os
23 import errno
24 import unittest
25 import mock
26
27 from tests.unit import STL_MOCKS
28 from yardstick.benchmark.scenarios.networking.vnf_generic import \
29     SshManager, NetworkServiceTestCase, IncorrectConfig, \
30     open_relative_file
31 from yardstick.network_services.collector.subscriber import Collector
32 from yardstick.network_services.vnf_generic.vnf.base import \
33     GenericTrafficGen, GenericVNF
34
35
36 COMPLETE_TREX_VNFD = {
37     'vnfd:vnfd-catalog': {
38         'vnfd': [
39             {
40                 'benchmark': {
41                     'kpi': [
42                         'rx_throughput_fps',
43                         'tx_throughput_fps',
44                         'tx_throughput_mbps',
45                         'rx_throughput_mbps',
46                         'tx_throughput_pc_linerate',
47                         'rx_throughput_pc_linerate',
48                         'min_latency',
49                         'max_latency',
50                         'avg_latency',
51                     ],
52                 },
53                 'connection-point': [
54                     {
55                         'name': 'xe0',
56                         'type': 'VPORT',
57                     },
58                     {
59                         'name': 'xe1',
60                         'type': 'VPORT',
61                     },
62                 ],
63                 'description': 'TRex stateless traffic generator for RFC2544',
64                 'id': 'TrexTrafficGen',
65                 'mgmt-interface': {
66                     'ip': '1.1.1.1',
67                     'password': 'berta',
68                     'user': 'berta',
69                     'vdu-id': 'trexgen-baremetal',
70                 },
71                 'name': 'trexgen',
72                 'short-name': 'trexgen',
73                 'class-name': 'TrexTrafficGen',
74                 'vdu': [
75                     {
76                         'description': 'TRex stateless traffic generator for RFC2544',
77                         'external-interface': [
78                             {
79                                 'name': 'xe0',
80                                 'virtual-interface': {
81                                     'bandwidth': '10 Gbps',
82                                     'dst_ip': '1.1.1.1',
83                                     'dst_mac': '00:01:02:03:04:05',
84                                     'local_ip': '1.1.1.2',
85                                     'local_mac': '00:01:02:03:05:05',
86                                     'type': 'PCI-PASSTHROUGH',
87                                     'netmask': "255.255.255.0",
88                                     'driver': 'i40',
89                                     'vpci': '0000:00:10.2',
90                                 },
91                                 'vnfd-connection-point-ref': 'xe0',
92                             },
93                             {
94                                 'name': 'xe1',
95                                 'virtual-interface': {
96                                     'bandwidth': '10 Gbps',
97                                     'dst_ip': '2.1.1.1',
98                                     'dst_mac': '00:01:02:03:04:06',
99                                     'local_ip': '2.1.1.2',
100                                     'local_mac': '00:01:02:03:05:06',
101                                     'type': 'PCI-PASSTHROUGH',
102                                     'netmask': "255.255.255.0",
103                                     'driver': 'i40',
104                                     'vpci': '0000:00:10.1',
105                                 },
106                                 'vnfd-connection-point-ref': 'xe1',
107                             },
108                         ],
109                         'id': 'trexgen-baremetal',
110                         'name': 'trexgen-baremetal',
111                     },
112                 ],
113             },
114         ],
115     },
116 }
117
118 IP_ADDR_SHOW = """
119 28: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP \
120 group default qlen 1000
121     link/ether 90:e2:ba:a7:6a:c8 brd ff:ff:ff:ff:ff:ff
122     inet 1.1.1.1/8 brd 1.255.255.255 scope global eth1
123     inet6 fe80::92e2:baff:fea7:6ac8/64 scope link
124        valid_lft forever preferred_lft forever
125 29: eth5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP \
126 group default qlen 1000
127     link/ether 90:e2:ba:a7:6a:c9 brd ff:ff:ff:ff:ff:ff
128     inet 2.1.1.1/8 brd 2.255.255.255 scope global eth5
129     inet6 fe80::92e2:baff:fea7:6ac9/64 scope link tentative
130        valid_lft forever preferred_lft forever
131 """
132
133 SYS_CLASS_NET = """
134 lrwxrwxrwx 1 root root 0 sie 10 14:16 eth1 -> \
135 ../../devices/pci0000:80/0000:80:02.2/0000:84:00.1/net/eth1
136 lrwxrwxrwx 1 root root 0 sie  3 10:37 eth2 -> \
137 ../../devices/pci0000:00/0000:00:01.1/0000:84:00.2/net/eth5
138 """
139
140 TRAFFIC_PROFILE = {
141     "schema": "isb:traffic_profile:0.1",
142     "name": "fixed",
143     "description": "Fixed traffic profile to run UDP traffic",
144     "traffic_profile": {
145         "traffic_type": "FixedTraffic",
146         "frame_rate": 100,  # pps
147         "flow_number": 10,
148         "frame_size": 64,
149     },
150 }
151
152
153 class TestNetworkServiceTestCase(unittest.TestCase):
154
155     def setUp(self):
156         self.tg__1 = {
157             'name': 'trafficgen_1.yardstick',
158             'ip': '10.10.10.11',
159             'role': 'TrafficGen',
160             'user': 'root',
161             'password': 'r00t',
162             'interfaces': {
163                 'xe0': {
164                     'netmask': '255.255.255.0',
165                     'local_ip': '152.16.100.20',
166                     'local_mac': '00:00:00:00:00:01',
167                     'driver': 'i40e',
168                     'vpci': '0000:07:00.0',
169                     'dpdk_port_num': 0,
170                 },
171                 'xe1': {
172                     'netmask': '255.255.255.0',
173                     'local_ip': '152.16.40.20',
174                     'local_mac': '00:00:00:00:00:02',
175                     'driver': 'i40e',
176                     'vpci': '0000:07:00.1',
177                     'dpdk_port_num': 1,
178                 },
179             },
180         }
181
182         self.vnf__1 = {
183             'name': 'vnf.yardstick',
184             'ip': '10.10.10.12',
185             'host': '10.223.197.164',
186             'role': 'vnf',
187             'user': 'root',
188             'password': 'r00t',
189             'interfaces': {
190                 'xe0': {
191                     'netmask': '255.255.255.0',
192                     'local_ip': '152.16.100.19',
193                     'local_mac': '00:00:00:00:00:03',
194                     'driver': 'i40e',
195                     'vpci': '0000:07:00.0',
196                     'dpdk_port_num': 0,
197                 },
198                 'xe1': {
199                     'netmask': '255.255.255.0',
200                     'local_ip': '152.16.40.19',
201                     'local_mac': '00:00:00:00:00:04',
202                     'driver': 'i40e',
203                     'vpci': '0000:07:00.1',
204                     'dpdk_port_num': 1,
205                 },
206             },
207             'routing_table': [
208                 {
209                     'netmask': '255.255.255.0',
210                     'gateway': '152.16.100.20',
211                     'network': '152.16.100.20',
212                     'if': 'xe0',
213                 },
214                 {
215                     'netmask': '255.255.255.0',
216                     'gateway': '152.16.40.20',
217                     'network': '152.16.40.20',
218                     'if': 'xe1',
219                 },
220             ],
221             'nd_route_tbl': [
222                 {
223                     'netmask': '112',
224                     'gateway': '0064:ff9b:0:0:0:0:9810:6414',
225                     'network': '0064:ff9b:0:0:0:0:9810:6414',
226                     'if': 'xe0',
227                 },
228                 {
229                     'netmask': '112',
230                     'gateway': '0064:ff9b:0:0:0:0:9810:2814',
231                     'network': '0064:ff9b:0:0:0:0:9810:2814',
232                     'if': 'xe1',
233                 },
234             ],
235         }
236
237         self.context_cfg = {
238             'nodes': {
239                 'tg__1': self.tg__1,
240                 'vnf__1': self.vnf__1,
241             },
242             'networks': {
243                 'private': {
244                     'vld_id': 'private',
245                 },
246                 'public': {
247                     'vld_id': 'public',
248                 },
249             },
250         }
251
252         self.vld0 = {
253             'vnfd-connection-point-ref': [
254                 {
255                     'vnfd-connection-point-ref': 'xe0',
256                     'member-vnf-index-ref': '1',
257                     'vnfd-id-ref': 'trexgen'
258                 },
259                 {
260                     'vnfd-connection-point-ref': 'xe0',
261                     'member-vnf-index-ref': '2',
262                     'vnfd-id-ref': 'trexgen'
263                 }
264             ],
265             'type': 'ELAN',
266             'id': 'private',
267             'name': 'tg__1 to vnf__1 link 1'
268         }
269
270         self.vld1 = {
271             'vnfd-connection-point-ref': [
272                 {
273                     'vnfd-connection-point-ref': 'xe1',
274                     'member-vnf-index-ref': '1',
275                     'vnfd-id-ref': 'trexgen'
276                 },
277                 {
278                     'vnfd-connection-point-ref': 'xe1',
279                     'member-vnf-index-ref': '2',
280                     'vnfd-id-ref': 'trexgen'
281                 }
282             ],
283             'type': 'ELAN',
284             'id': 'public',
285             'name': 'vnf__1 to tg__1 link 2'
286         }
287
288         self.topology = {
289             'id': 'trex-tg-topology',
290             'short-name': 'trex-tg-topology',
291             'name': 'trex-tg-topology',
292             'description': 'trex-tg-topology',
293             'constituent-vnfd': [
294                 {
295                     'member-vnf-index': '1',
296                     'VNF model': 'tg_trex_tpl.yaml',
297                     'vnfd-id-ref': 'tg__1',
298                 },
299                 {
300                     'member-vnf-index': '2',
301                     'VNF model': 'tg_trex_tpl.yaml',
302                     'vnfd-id-ref': 'vnf__1',
303                 },
304             ],
305             'vld': [self.vld0, self.vld1],
306         }
307
308         self.scenario_cfg = {
309             'task_path': "",
310             "topology": self._get_file_abspath("vpe_vnf_topology.yaml"),
311             'task_id': 'a70bdf4a-8e67-47a3-9dc1-273c14506eb7',
312             'tc': 'tc_ipv4_1Mflow_64B_packetsize',
313             'traffic_profile': 'ipv4_throughput_vpe.yaml',
314             'type': 'ISB',
315             'tc_options': {
316                 'rfc2544': {
317                     'allowed_drop_rate': '0.8 - 1',
318                 },
319             },
320             'options': {
321                 'framesize': {'64B': 100}
322             },
323             'runner': {
324                 'object': 'NetworkServiceTestCase',
325                 'interval': 35,
326                 'output_filename': 'yardstick.out',
327                 'runner_id': 74476,
328                 'duration': 400,
329                 'type': 'Duration',
330             },
331             'traffic_options': {
332                 'flow': 'ipv4_1flow_Packets_vpe.yaml',
333                 'imix': 'imix_voice.yaml'
334             },
335             'nodes': {
336                 'tg__2': 'trafficgen_2.yardstick',
337                 'tg__1': 'trafficgen_1.yardstick',
338                 'vnf__1': 'vnf.yardstick',
339             },
340         }
341
342         self.s = NetworkServiceTestCase(self.scenario_cfg, self.context_cfg)
343
344     def _get_file_abspath(self, filename):
345         curr_path = os.path.dirname(os.path.abspath(__file__))
346         file_path = os.path.join(curr_path, filename)
347         return file_path
348
349     def test_ssh_manager(self):
350         with mock.patch("yardstick.ssh.SSH") as ssh:
351             ssh_mock = mock.Mock(autospec=ssh.SSH)
352             ssh_mock.execute = \
353                 mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, ""))
354             ssh.from_node.return_value = ssh_mock
355             for node, node_dict in self.context_cfg["nodes"].items():
356                 with SshManager(node_dict) as conn:
357                     self.assertIsNotNone(conn)
358
359     def test___init__(self):
360         assert self.topology
361
362     def test__get_ip_flow_range(self):
363         self.scenario_cfg["traffic_options"]["flow"] = \
364             self._get_file_abspath("ipv4_1flow_Packets_vpe.yaml")
365         result = '152.16.100.2-152.16.100.254'
366         self.assertEqual(result, self.s._get_ip_flow_range({"tg__1": 'xe0'}))
367
368     def test___get_traffic_flow(self):
369         self.scenario_cfg["traffic_options"]["flow"] = \
370             self._get_file_abspath("ipv4_1flow_Packets_vpe.yaml")
371         self.scenario_cfg["options"] = {}
372         self.scenario_cfg['options'] = {
373             'flow': {
374               'src_ip': [
375                 {
376                   'tg__1': 'xe0',
377                 },
378               ],
379               'dst_ip': [
380                 {
381                   'tg__1': 'xe1',
382                 },
383               ],
384               'public_ip': ['1.1.1.1'],
385             },
386         }
387         result = {'flow': {'dst_ip0': '152.16.40.2-152.16.40.254',
388                            'src_ip0': '152.16.100.2-152.16.100.254'}}
389
390         self.assertEqual({'flow': {}}, self.s._get_traffic_flow())
391
392     def test___get_traffic_flow_error(self):
393         self.scenario_cfg["traffic_options"]["flow"] = \
394             "ipv4_1flow_Packets_vpe.yaml1"
395         self.assertEqual({'flow': {}}, self.s._get_traffic_flow())
396
397     def test_get_vnf_imp(self):
398         vnfd = COMPLETE_TREX_VNFD['vnfd:vnfd-catalog']['vnfd'][0]['class-name']
399         with mock.patch.dict("sys.modules", STL_MOCKS):
400             self.assertIsNotNone(self.s.get_vnf_impl(vnfd))
401
402             with self.assertRaises(IncorrectConfig) as raised:
403                 self.s.get_vnf_impl('NonExistentClass')
404
405             exc_str = str(raised.exception)
406             print(exc_str)
407             self.assertIn('No implementation', exc_str)
408             self.assertIn('found in', exc_str)
409
410     def test_load_vnf_models_invalid(self):
411         self.context_cfg["nodes"]['tg__1']['VNF model'] = \
412             self._get_file_abspath("tg_trex_tpl.yaml")
413         self.context_cfg["nodes"]['vnf__1']['VNF model'] = \
414             self._get_file_abspath("tg_trex_tpl.yaml")
415
416         vnf = mock.Mock(autospec=GenericVNF)
417         self.s.get_vnf_impl = mock.Mock(return_value=vnf)
418
419         self.assertIsNotNone(
420             self.s.load_vnf_models(self.scenario_cfg, self.context_cfg))
421
422     def test_map_topology_to_infrastructure(self):
423         with mock.patch("yardstick.ssh.SSH") as ssh:
424             ssh_mock = mock.Mock(autospec=ssh.SSH)
425             ssh_mock.execute = \
426                 mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, ""))
427             ssh.from_node.return_value = ssh_mock
428             self.s.map_topology_to_infrastructure()
429
430         nodes = self.context_cfg["nodes"]
431         self.assertEqual("../../vnf_descriptors/tg_rfc2544_tpl.yaml", nodes['tg__1']['VNF model'])
432         self.assertEqual("../../vnf_descriptors/vpe_vnf.yaml", nodes['vnf__1']['VNF model'])
433
434     def test_map_topology_to_infrastructure_insufficient_nodes(self):
435         del self.context_cfg['nodes']['vnf__1']
436         with mock.patch("yardstick.ssh.SSH") as ssh:
437             ssh_mock = mock.Mock(autospec=ssh.SSH)
438             ssh_mock.execute = \
439                 mock.Mock(return_value=(1, SYS_CLASS_NET + IP_ADDR_SHOW, ""))
440             ssh.from_node.return_value = ssh_mock
441
442             with self.assertRaises(IncorrectConfig):
443                 self.s.map_topology_to_infrastructure()
444
445     def test_map_topology_to_infrastructure_config_invalid(self):
446         cfg = dict(self.context_cfg)
447         del cfg['nodes']['vnf__1']['interfaces']['xe0']['local_mac']
448         with mock.patch("yardstick.ssh.SSH") as ssh:
449             ssh_mock = mock.Mock(autospec=ssh.SSH)
450             ssh_mock.execute = \
451                 mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, ""))
452             ssh.from_node.return_value = ssh_mock
453
454             with self.assertRaises(IncorrectConfig):
455                 self.s.map_topology_to_infrastructure()
456
457     def test__resolve_topology_invalid_config(self):
458         with mock.patch("yardstick.ssh.SSH") as ssh:
459             ssh_mock = mock.Mock(autospec=ssh.SSH)
460             ssh_mock.execute = \
461                 mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, ""))
462             ssh.from_node.return_value = ssh_mock
463
464             # purge an important key from the data structure
465             for interface in self.tg__1['interfaces'].values():
466                 del interface['local_mac']
467
468             with mock.patch(
469                 "yardstick.benchmark.scenarios.networking.vnf_generic.LOG") as mock_log:
470                 with self.assertRaises(IncorrectConfig) as raised:
471                     self.s._resolve_topology()
472
473             self.assertIn('not found', str(raised.exception))
474
475             # restore local_mac
476             for index, interface in enumerate(self.tg__1['interfaces'].values()):
477                 interface['local_mac'] = '00:00:00:00:00:{:2x}'.format(index)
478
479             # make a connection point ref with 3 points
480             self.s.topology["vld"][0]['vnfd-connection-point-ref'].append(
481                 self.s.topology["vld"][0]['vnfd-connection-point-ref'][0])
482
483             with mock.patch(
484                 "yardstick.benchmark.scenarios.networking.vnf_generic.LOG") as mock_log:
485                 with self.assertRaises(IncorrectConfig) as raised:
486                     self.s._resolve_topology()
487
488             self.assertIn('wrong endpoint count', str(raised.exception))
489
490             # make a connection point ref with 1 point
491             self.s.topology["vld"][0]['vnfd-connection-point-ref'] = \
492                 self.s.topology["vld"][0]['vnfd-connection-point-ref'][:1]
493
494             with mock.patch(
495                 "yardstick.benchmark.scenarios.networking.vnf_generic.LOG") as mock_log:
496                 with self.assertRaises(IncorrectConfig) as raised:
497                     self.s._resolve_topology()
498
499             self.assertIn('wrong endpoint count', str(raised.exception))
500
501     def test_run(self):
502         tgen = mock.Mock(autospec=GenericTrafficGen)
503         tgen.traffic_finished = True
504         verified_dict = {"verified": True}
505         tgen.verify_traffic = lambda x: verified_dict
506         tgen.name = "tgen__1"
507         vnf = mock.Mock(autospec=GenericVNF)
508         vnf.runs_traffic = False
509         self.s.vnfs = [tgen, vnf]
510         self.s.traffic_profile = mock.Mock()
511         self.s.collector = mock.Mock(autospec=Collector)
512         self.s.collector.get_kpi = \
513             mock.Mock(return_value={tgen.name: verified_dict})
514         result = {}
515         self.s.run(result)
516         self.assertDictEqual(result, {tgen.name: verified_dict})
517
518     def test_setup(self):
519         with mock.patch("yardstick.ssh.SSH") as ssh:
520             ssh_mock = mock.Mock(autospec=ssh.SSH)
521             ssh_mock.execute = \
522                 mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, ""))
523             ssh.from_node.return_value = ssh_mock
524
525             tgen = mock.Mock(autospec=GenericTrafficGen)
526             tgen.traffic_finished = True
527             verified_dict = {"verified": True}
528             tgen.verify_traffic = lambda x: verified_dict
529             tgen.terminate = mock.Mock(return_value=True)
530             tgen.name = "tgen__1"
531             vnf = mock.Mock(autospec=GenericVNF)
532             vnf.runs_traffic = False
533             vnf.terminate = mock.Mock(return_value=True)
534             self.s.vnfs = [tgen, vnf]
535             self.s.traffic_profile = mock.Mock()
536             self.s.collector = mock.Mock(autospec=Collector)
537             self.s.collector.get_kpi = \
538                 mock.Mock(return_value={tgen.name: verified_dict})
539             self.s.map_topology_to_infrastructure = mock.Mock(return_value=0)
540             self.s.load_vnf_models = mock.Mock(return_value=self.s.vnfs)
541             self.s._fill_traffic_profile = \
542                 mock.Mock(return_value=TRAFFIC_PROFILE)
543             self.assertEqual(None, self.s.setup())
544
545     def test__get_traffic_profile(self):
546         self.scenario_cfg["traffic_profile"] = \
547             self._get_file_abspath("ipv4_throughput_vpe.yaml")
548         self.assertIsNotNone(self.s._get_traffic_profile())
549
550     def test__get_traffic_profile_exception(self):
551         with mock.patch.dict(self.scenario_cfg, {'traffic_profile': ''}):
552             with self.assertRaises(IOError):
553                 self.s._get_traffic_profile()
554
555     def test___get_traffic_imix_exception(self):
556         with mock.patch.dict(self.scenario_cfg["traffic_options"], {'imix': ''}):
557             self.assertEqual({'imix': {'64B': 100}}, self.s._get_traffic_imix())
558
559     def test__fill_traffic_profile(self):
560         with mock.patch.dict("sys.modules", STL_MOCKS):
561             self.scenario_cfg["traffic_profile"] = \
562                 self._get_file_abspath("ipv4_throughput_vpe.yaml")
563             self.scenario_cfg["traffic_options"]["flow"] = \
564                 self._get_file_abspath("ipv4_1flow_Packets_vpe.yaml")
565             self.scenario_cfg["traffic_options"]["imix"] = \
566                 self._get_file_abspath("imix_voice.yaml")
567             self.assertIsNotNone(self.s._fill_traffic_profile())
568
569     def test_teardown(self):
570         vnf = mock.Mock(autospec=GenericVNF)
571         vnf.terminate = \
572             mock.Mock(return_value=True)
573         self.s.vnfs = [vnf]
574         self.s.traffic_profile = mock.Mock()
575         self.s.collector = mock.Mock(autospec=Collector)
576         self.s.collector.stop = \
577             mock.Mock(return_value=True)
578         self.assertIsNone(self.s.teardown())
579
580     SAMPLE_NETDEVS = {
581         'enp11s0': {
582             'address': '0a:de:ad:be:ef:f5',
583             'device': '0x1533',
584             'driver': 'igb',
585             'ifindex': '2',
586             'interface_name': 'enp11s0',
587             'operstate': 'down',
588             'pci_bus_id': '0000:0b:00.0',
589             'subsystem_device': '0x1533',
590             'subsystem_vendor': '0x15d9',
591             'vendor': '0x8086'
592         },
593         'lan': {
594             'address': '0a:de:ad:be:ef:f4',
595             'device': '0x153a',
596             'driver': 'e1000e',
597             'ifindex': '3',
598             'interface_name': 'lan',
599             'operstate': 'up',
600             'pci_bus_id': '0000:00:19.0',
601             'subsystem_device': '0x153a',
602             'subsystem_vendor': '0x15d9',
603             'vendor': '0x8086'
604         }
605     }
606
607     SAMPLE_VM_NETDEVS = {
608         'eth1': {
609             'address': 'fa:de:ad:be:ef:5b',
610             'device': '0x0001',
611             'driver': 'virtio_net',
612             'ifindex': '3',
613             'interface_name': 'eth1',
614             'operstate': 'down',
615             'pci_bus_id': '0000:00:04.0',
616             'vendor': '0x1af4'
617         }
618     }
619
620     def test_parse_netdev_info(self):
621         output = """\
622 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/ifindex:2
623 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/address:0a:de:ad:be:ef:f5
624 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/operstate:down
625 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/vendor:0x8086
626 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/device:0x1533
627 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/subsystem_vendor:0x15d9
628 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/subsystem_device:0x1533
629 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/driver:igb
630 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/pci_bus_id:0000:0b:00.0
631 /sys/devices/pci0000:00/0000:00:19.0/net/lan/ifindex:3
632 /sys/devices/pci0000:00/0000:00:19.0/net/lan/address:0a:de:ad:be:ef:f4
633 /sys/devices/pci0000:00/0000:00:19.0/net/lan/operstate:up
634 /sys/devices/pci0000:00/0000:00:19.0/net/lan/device/vendor:0x8086
635 /sys/devices/pci0000:00/0000:00:19.0/net/lan/device/device:0x153a
636 /sys/devices/pci0000:00/0000:00:19.0/net/lan/device/subsystem_vendor:0x15d9
637 /sys/devices/pci0000:00/0000:00:19.0/net/lan/device/subsystem_device:0x153a
638 /sys/devices/pci0000:00/0000:00:19.0/net/lan/driver:e1000e
639 /sys/devices/pci0000:00/0000:00:19.0/net/lan/pci_bus_id:0000:00:19.0
640 """
641         res = NetworkServiceTestCase.parse_netdev_info(output)
642         assert res == self.SAMPLE_NETDEVS
643
644     def test_parse_netdev_info_virtio(self):
645         output = """\
646 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/ifindex:3
647 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/address:fa:de:ad:be:ef:5b
648 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/operstate:down
649 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/device/vendor:0x1af4
650 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/device/device:0x0001
651 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/driver:virtio_net
652 """
653         res = NetworkServiceTestCase.parse_netdev_info(output)
654         assert res == self.SAMPLE_VM_NETDEVS
655
656     def test_sort_dpdk_port_num(self):
657         netdevs = self.SAMPLE_NETDEVS.copy()
658         NetworkServiceTestCase._sort_dpdk_port_num(netdevs)
659         assert netdevs['lan']['dpdk_port_num'] == 0
660         assert netdevs['enp11s0']['dpdk_port_num'] == 1
661
662     def test_probe_missing_values(self):
663         netdevs = self.SAMPLE_NETDEVS.copy()
664         network = {'local_mac': '0a:de:ad:be:ef:f5'}
665         NetworkServiceTestCase._probe_missing_values(netdevs, network)
666         assert network['vpci'] == '0000:0b:00.0'
667
668         network = {'local_mac': '0a:de:ad:be:ef:f4'}
669         NetworkServiceTestCase._probe_missing_values(netdevs, network)
670         assert network['vpci'] == '0000:00:19.0'
671
672     def test_open_relative_path(self):
673         mock_open = mock.mock_open()
674         mock_open_result = mock_open()
675         mock_open_call_count = 1  # initial call to get result
676
677         module_name = \
678             'yardstick.benchmark.scenarios.networking.vnf_generic.open'
679
680         # test
681         with mock.patch(module_name, mock_open, create=True):
682             self.assertEqual(open_relative_file('foo', 'bar'), mock_open_result)
683
684             mock_open_call_count += 1  # one more call expected
685             self.assertEqual(mock_open.call_count, mock_open_call_count)
686             self.assertIn('foo', mock_open.call_args_list[-1][0][0])
687             self.assertNotIn('bar', mock_open.call_args_list[-1][0][0])
688
689             def open_effect(*args, **kwargs):
690                 if kwargs.get('name', args[0]) == os.path.join('bar', 'foo'):
691                     return mock_open_result
692                 raise IOError(errno.ENOENT, 'not found')
693
694             mock_open.side_effect = open_effect
695             self.assertEqual(open_relative_file('foo', 'bar'), mock_open_result)
696
697             mock_open_call_count += 2  # two more calls expected
698             self.assertEqual(mock_open.call_count, mock_open_call_count)
699             self.assertIn('foo', mock_open.call_args_list[-1][0][0])
700             self.assertIn('bar', mock_open.call_args_list[-1][0][0])
701
702             # test an IOError of type ENOENT
703             mock_open.side_effect = IOError(errno.ENOENT, 'not found')
704             with self.assertRaises(IOError):
705                 # the second call still raises
706                 open_relative_file('foo', 'bar')
707
708             mock_open_call_count += 2  # two more calls expected
709             self.assertEqual(mock_open.call_count, mock_open_call_count)
710             self.assertIn('foo', mock_open.call_args_list[-1][0][0])
711             self.assertIn('bar', mock_open.call_args_list[-1][0][0])
712
713             # test an IOError other than ENOENT
714             mock_open.side_effect = IOError(errno.EBUSY, 'busy')
715             with self.assertRaises(IOError):
716                 open_relative_file('foo', 'bar')
717
718             mock_open_call_count += 1  # one more call expected
719             self.assertEqual(mock_open.call_count, mock_open_call_count)