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