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