022e66ff15e7373132a8961740bf265568602eb0
[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     def test_teardown(self):
649         vnf = mock.Mock(autospec=GenericVNF)
650         vnf.terminate = mock.Mock(return_value=True)
651         vnf.name = str(vnf)
652         self.s.vnfs = [vnf]
653         self.s.traffic_profile = mock.Mock()
654         self.s.collector = mock.Mock(autospec=Collector)
655         self.s.collector.stop = \
656             mock.Mock(return_value=True)
657         self.assertIsNone(self.s.teardown())
658
659     def test_teardown_exception(self):
660         vnf = mock.Mock(autospec=GenericVNF)
661         vnf.terminate = mock.Mock(
662             side_effect=RuntimeError("error duing terminate"))
663         vnf.name = str(vnf)
664         self.s.vnfs = [vnf]
665         self.s.traffic_profile = mock.Mock()
666         self.s.collector = mock.Mock(autospec=Collector)
667         self.s.collector.stop = \
668             mock.Mock(return_value=True)
669         with self.assertRaises(RuntimeError):
670             self.s.teardown()
671
672     SAMPLE_NETDEVS = {
673         'enp11s0': {
674             'address': '0a:de:ad:be:ef:f5',
675             'device': '0x1533',
676             'driver': 'igb',
677             'ifindex': '2',
678             'interface_name': 'enp11s0',
679             'operstate': 'down',
680             'pci_bus_id': '0000:0b:00.0',
681             'subsystem_device': '0x1533',
682             'subsystem_vendor': '0x15d9',
683             'vendor': '0x8086'
684         },
685         'lan': {
686             'address': '0a:de:ad:be:ef:f4',
687             'device': '0x153a',
688             'driver': 'e1000e',
689             'ifindex': '3',
690             'interface_name': 'lan',
691             'operstate': 'up',
692             'pci_bus_id': '0000:00:19.0',
693             'subsystem_device': '0x153a',
694             'subsystem_vendor': '0x15d9',
695             'vendor': '0x8086'
696         }
697     }
698
699     SAMPLE_VM_NETDEVS = {
700         'eth1': {
701             'address': 'fa:de:ad:be:ef:5b',
702             'device': '0x0001',
703             'driver': 'virtio_net',
704             'ifindex': '3',
705             'interface_name': 'eth1',
706             'operstate': 'down',
707             'pci_bus_id': '0000:00:04.0',
708             'vendor': '0x1af4'
709         }
710     }
711
712     def test_parse_netdev_info(self):
713         output = """\
714 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/ifindex:2
715 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/address:0a:de:ad:be:ef:f5
716 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/operstate:down
717 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/vendor:0x8086
718 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/device:0x1533
719 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/subsystem_vendor:0x15d9
720 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/subsystem_device:0x1533
721 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/driver:igb
722 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/pci_bus_id:0000:0b:00.0
723 /sys/devices/pci0000:00/0000:00:19.0/net/lan/ifindex:3
724 /sys/devices/pci0000:00/0000:00:19.0/net/lan/address:0a:de:ad:be:ef:f4
725 /sys/devices/pci0000:00/0000:00:19.0/net/lan/operstate:up
726 /sys/devices/pci0000:00/0000:00:19.0/net/lan/device/vendor:0x8086
727 /sys/devices/pci0000:00/0000:00:19.0/net/lan/device/device:0x153a
728 /sys/devices/pci0000:00/0000:00:19.0/net/lan/device/subsystem_vendor:0x15d9
729 /sys/devices/pci0000:00/0000:00:19.0/net/lan/device/subsystem_device:0x153a
730 /sys/devices/pci0000:00/0000:00:19.0/net/lan/driver:e1000e
731 /sys/devices/pci0000:00/0000:00:19.0/net/lan/pci_bus_id:0000:00:19.0
732 """
733         res = vnf_generic.NetworkServiceTestCase.parse_netdev_info(output)
734         assert res == self.SAMPLE_NETDEVS
735
736     def test_parse_netdev_info_virtio(self):
737         output = """\
738 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/ifindex:3
739 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/address:fa:de:ad:be:ef:5b
740 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/operstate:down
741 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/device/vendor:0x1af4
742 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/device/device:0x0001
743 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/driver:virtio_net
744 """
745         res = vnf_generic.NetworkServiceTestCase.parse_netdev_info(output)
746         assert res == self.SAMPLE_VM_NETDEVS
747
748     def test_probe_missing_values(self):
749         netdevs = self.SAMPLE_NETDEVS.copy()
750         network = {'local_mac': '0a:de:ad:be:ef:f5'}
751         vnf_generic.NetworkServiceTestCase._probe_missing_values(netdevs,
752                                                                  network)
753         assert network['vpci'] == '0000:0b:00.0'
754
755         network = {'local_mac': '0a:de:ad:be:ef:f4'}
756         vnf_generic.NetworkServiceTestCase._probe_missing_values(netdevs,
757                                                                  network)
758         assert network['vpci'] == '0000:00:19.0'
759
760     @mock.patch.object(six.moves.builtins, 'open')
761     def test_open_relative_path(self, mock_open):
762         # NOTE(ralonsoh): the mocked function is not properly used and tested.
763         mock_open_result = mock_open()
764         mock_open_call_count = 1  # initial call to get result
765         self.assertEqual(utils.open_relative_file('foo', 'bar'),
766                          mock_open_result)
767
768         mock_open_call_count += 1  # one more call expected
769         self.assertEqual(mock_open.call_count, mock_open_call_count)
770         self.assertIn('foo', mock_open.call_args_list[-1][0][0])
771         self.assertNotIn('bar', mock_open.call_args_list[-1][0][0])
772
773         def open_effect(*args, **kwargs):
774             if kwargs.get('name', args[0]) == os.path.join('bar', 'foo'):
775                 return mock_open_result
776             raise IOError(errno.ENOENT, 'not found')
777
778         mock_open.side_effect = open_effect
779         self.assertEqual(utils.open_relative_file('foo', 'bar'),
780                          mock_open_result)
781
782         mock_open_call_count += 2  # two more calls expected
783         self.assertEqual(mock_open.call_count, mock_open_call_count)
784         self.assertIn('foo', mock_open.call_args_list[-1][0][0])
785         self.assertIn('bar', mock_open.call_args_list[-1][0][0])
786
787         # test an IOError of type ENOENT
788         mock_open.side_effect = IOError(errno.ENOENT, 'not found')
789         with self.assertRaises(IOError):
790             # the second call still raises
791             utils.open_relative_file('foo', 'bar')
792
793         mock_open_call_count += 2  # two more calls expected
794         self.assertEqual(mock_open.call_count, mock_open_call_count)
795         self.assertIn('foo', mock_open.call_args_list[-1][0][0])
796         self.assertIn('bar', mock_open.call_args_list[-1][0][0])
797
798         # test an IOError other than ENOENT
799         mock_open.side_effect = IOError(errno.EBUSY, 'busy')
800         with self.assertRaises(IOError):
801             utils.open_relative_file('foo', 'bar')
802
803         mock_open_call_count += 1  # one more call expected
804         self.assertEqual(mock_open.call_count, mock_open_call_count)