NSB: move interface probe to VNF, and attempt driver-only probe first
[yardstick.git] / tests / unit / network_services / helpers / test_dpdkbindnic_helper.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2016-2017 Intel Corporation
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 #      http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 import mock
18 import unittest
19
20 import os
21
22 from yardstick.error import IncorrectConfig, SSHError
23 from yardstick.error import IncorrectNodeSetup
24 from yardstick.error import IncorrectSetup
25 from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkInterface
26 from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkNode
27 from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkBindHelper
28 from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkBindHelperException
29 from yardstick.network_services.helpers.dpdkbindnic_helper import NETWORK_KERNEL
30 from yardstick.network_services.helpers.dpdkbindnic_helper import NETWORK_DPDK
31 from yardstick.network_services.helpers.dpdkbindnic_helper import CRYPTO_KERNEL
32 from yardstick.network_services.helpers.dpdkbindnic_helper import CRYPTO_DPDK
33 from yardstick.network_services.helpers.dpdkbindnic_helper import NETWORK_OTHER
34 from yardstick.network_services.helpers.dpdkbindnic_helper import CRYPTO_OTHER
35
36
37 NAME = "tg_0"
38
39
40 class TestDpdkInterface(unittest.TestCase):
41
42     SAMPLE_NETDEVS = {
43         'enp11s0': {
44             'address': '0a:de:ad:be:ef:f5',
45             'device': '0x1533',
46             'driver': 'igb',
47             'ifindex': '2',
48             'interface_name': 'enp11s0',
49             'operstate': 'down',
50             'pci_bus_id': '0000:0b:00.0',
51             'subsystem_device': '0x1533',
52             'subsystem_vendor': '0x15d9',
53             'vendor': '0x8086'
54         },
55         'lan': {
56             'address': '0a:de:ad:be:ef:f4',
57             'device': '0x153a',
58             'driver': 'e1000e',
59             'ifindex': '3',
60             'interface_name': 'lan',
61             'operstate': 'up',
62             'pci_bus_id': '0000:00:19.0',
63             'subsystem_device': '0x153a',
64             'subsystem_vendor': '0x15d9',
65             'vendor': '0x8086'
66         }
67     }
68
69     SAMPLE_VM_NETDEVS = {
70         'eth1': {
71             'address': 'fa:de:ad:be:ef:5b',
72             'device': '0x0001',
73             'driver': 'virtio_net',
74             'ifindex': '3',
75             'interface_name': 'eth1',
76             'operstate': 'down',
77             'pci_bus_id': '0000:00:04.0',
78             'vendor': '0x1af4'
79         }
80     }
81
82     def test_parse_netdev_info(self):
83         output = """\
84 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/ifindex:2
85 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/address:0a:de:ad:be:ef:f5
86 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/operstate:down
87 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/vendor:0x8086
88 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/device:0x1533
89 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/subsystem_vendor:0x15d9
90 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/subsystem_device:0x1533
91 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/driver:igb
92 /sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/pci_bus_id:0000:0b:00.0
93 /sys/devices/pci0000:00/0000:00:19.0/net/lan/ifindex:3
94 /sys/devices/pci0000:00/0000:00:19.0/net/lan/address:0a:de:ad:be:ef:f4
95 /sys/devices/pci0000:00/0000:00:19.0/net/lan/operstate:up
96 /sys/devices/pci0000:00/0000:00:19.0/net/lan/device/vendor:0x8086
97 /sys/devices/pci0000:00/0000:00:19.0/net/lan/device/device:0x153a
98 /sys/devices/pci0000:00/0000:00:19.0/net/lan/device/subsystem_vendor:0x15d9
99 /sys/devices/pci0000:00/0000:00:19.0/net/lan/device/subsystem_device:0x153a
100 /sys/devices/pci0000:00/0000:00:19.0/net/lan/driver:e1000e
101 /sys/devices/pci0000:00/0000:00:19.0/net/lan/pci_bus_id:0000:00:19.0
102 """
103         res = DpdkBindHelper.parse_netdev_info(output)
104         self.assertDictEqual(res, self.SAMPLE_NETDEVS)
105
106     def test_parse_netdev_info_virtio(self):
107         output = """\
108 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/ifindex:3
109 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/address:fa:de:ad:be:ef:5b
110 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/operstate:down
111 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/device/vendor:0x1af4
112 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/device/device:0x0001
113 /sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/driver:virtio_net
114 """
115         res = DpdkBindHelper.parse_netdev_info(output)
116         self.assertDictEqual(res, self.SAMPLE_VM_NETDEVS)
117
118     def test_probe_missing_values(self):
119         mock_dpdk_node = mock.Mock()
120         mock_dpdk_node.netdevs = self.SAMPLE_NETDEVS.copy()
121
122         interface = {'local_mac': '0a:de:ad:be:ef:f5'}
123         dpdk_intf = DpdkInterface(mock_dpdk_node, interface)
124
125         dpdk_intf.probe_missing_values()
126         self.assertEqual(interface['vpci'], '0000:0b:00.0')
127
128         interface['local_mac'] = '0a:de:ad:be:ef:f4'
129         dpdk_intf.probe_missing_values()
130         self.assertEqual(interface['vpci'], '0000:00:19.0')
131
132     def test_probe_missing_values_no_update(self):
133         mock_dpdk_node = mock.Mock()
134         mock_dpdk_node.netdevs = self.SAMPLE_NETDEVS.copy()
135         del mock_dpdk_node.netdevs['enp11s0']['driver']
136         del mock_dpdk_node.netdevs['lan']['driver']
137
138         interface = {'local_mac': '0a:de:ad:be:ef:f5'}
139         dpdk_intf = DpdkInterface(mock_dpdk_node, interface)
140
141         dpdk_intf.probe_missing_values()
142         self.assertNotIn('vpci', interface)
143         self.assertNotIn('driver', interface)
144
145     def test_probe_missing_values_negative(self):
146         mock_dpdk_node = mock.Mock()
147         mock_dpdk_node.netdevs.values.side_effect = IncorrectNodeSetup
148
149         interface = {'local_mac': '0a:de:ad:be:ef:f5'}
150         dpdk_intf = DpdkInterface(mock_dpdk_node, interface)
151
152         with self.assertRaises(IncorrectConfig):
153             dpdk_intf.probe_missing_values()
154
155
156 class TestDpdkNode(unittest.TestCase):
157
158     INTERFACES = [
159         {'name': 'name1',
160          'virtual-interface': {
161              'local_mac': 404,
162              'vpci': 'pci10',
163          }},
164         {'name': 'name2',
165          'virtual-interface': {
166              'local_mac': 404,
167              'vpci': 'pci2',
168          }},
169         {'name': 'name3',
170          'virtual-interface': {
171              'local_mac': 404,
172              'vpci': 'some-pci1',
173          }},
174     ]
175
176     def test_probe_dpdk_drivers(self):
177         mock_ssh_helper = mock.Mock()
178         mock_ssh_helper.execute.return_value = 0, '', ''
179
180         interfaces = [
181             {'name': 'name1',
182              'virtual-interface': {
183                  'local_mac': 404,
184                  'vpci': 'pci10',
185              }},
186             {'name': 'name2',
187              'virtual-interface': {
188                  'local_mac': 404,
189                  'vpci': 'pci2',
190              }},
191             {'name': 'name3',
192              'virtual-interface': {
193                  'local_mac': 404,
194                  'vpci': 'some-pci1',
195              }},
196         ]
197
198         dpdk_node = DpdkNode(NAME, interfaces, mock_ssh_helper)
199         dpdk_helper = dpdk_node.dpdk_helper
200
201         dpdk_helper.probe_real_kernel_drivers = mock.Mock()
202         dpdk_helper.real_kernel_interface_driver_map = {
203             'pci1': 'driver1',
204             'pci2': 'driver2',
205             'pci3': 'driver3',
206             'pci4': 'driver1',
207             'pci6': 'driver3',
208         }
209
210         dpdk_node._probe_dpdk_drivers()
211         self.assertNotIn('driver', interfaces[0]['virtual-interface'])
212         self.assertEqual(interfaces[1]['virtual-interface']['driver'], 'driver2')
213         self.assertEqual(interfaces[2]['virtual-interface']['driver'], 'driver1')
214
215     def test_check(self):
216         def update():
217             if not mock_force_rebind.called:
218                 raise IncorrectConfig
219
220             interfaces[0]['virtual-interface'].update({
221                 'vpci': '0000:01:02.1',
222                 'local_ip': '10.20.30.40',
223                 'netmask': '255.255.0.0',
224                 'driver': 'ixgbe',
225             })
226
227         mock_ssh_helper = mock.Mock()
228         mock_ssh_helper.execute.return_value = 0, '', ''
229
230         interfaces = [
231             {'name': 'name1',
232              'virtual-interface': {
233                  'local_mac': 404,
234              }},
235         ]
236
237         dpdk_node = DpdkNode(NAME, interfaces, mock_ssh_helper)
238         dpdk_node._probe_missing_values = mock_probe_missing = mock.Mock(side_effect=update)
239         dpdk_node._force_rebind = mock_force_rebind = mock.Mock()
240
241         self.assertIsNone(dpdk_node.check())
242         self.assertEqual(mock_probe_missing.call_count, 2)
243
244     @mock.patch('yardstick.network_services.helpers.dpdkbindnic_helper.DpdkInterface')
245     def test_check_negative(self, mock_intf_type):
246         mock_ssh_helper = mock.Mock()
247         mock_ssh_helper.execute.return_value = 0, '', ''
248
249         mock_intf_type().check.side_effect = SSHError
250
251         dpdk_node = DpdkNode(NAME, self.INTERFACES, mock_ssh_helper)
252
253         with self.assertRaises(IncorrectSetup):
254             dpdk_node.check()
255
256     def test_probe_netdevs(self):
257         mock_ssh_helper = mock.Mock()
258         mock_ssh_helper.execute.return_value = 0, '', ''
259
260         expected = {'key1': 500, 'key2': 'hello world'}
261         update = {'key1': 1000, 'key3': []}
262
263         dpdk_node = DpdkNode(NAME, self.INTERFACES, mock_ssh_helper)
264         dpdk_helper = dpdk_node.dpdk_helper
265         dpdk_helper.find_net_devices = mock.Mock(side_effect=[expected, update])
266
267         self.assertDictEqual(dpdk_node.netdevs, {})
268         dpdk_node._probe_netdevs()
269         self.assertDictEqual(dpdk_node.netdevs, expected)
270
271         expected = {'key1': 1000, 'key2': 'hello world', 'key3': []}
272         dpdk_node._probe_netdevs()
273         self.assertDictEqual(dpdk_node.netdevs, expected)
274
275     def test_probe_netdevs_setup_negative(self):
276         mock_ssh_helper = mock.Mock()
277         mock_ssh_helper.execute.return_value = 0, '', ''
278
279         dpdk_node = DpdkNode(NAME, self.INTERFACES, mock_ssh_helper)
280         dpdk_helper = dpdk_node.dpdk_helper
281         dpdk_helper.find_net_devices = mock.Mock(side_effect=DpdkBindHelperException)
282
283         with self.assertRaises(DpdkBindHelperException):
284             dpdk_node._probe_netdevs()
285
286     def test_force_rebind(self):
287         mock_ssh_helper = mock.Mock()
288         mock_ssh_helper.execute.return_value = 0, '', ''
289
290         dpdk_node = DpdkNode(NAME, self.INTERFACES, mock_ssh_helper)
291         dpdk_helper = dpdk_node.dpdk_helper
292         dpdk_helper.force_dpdk_rebind = mock_helper_func = mock.Mock()
293
294         dpdk_node._force_rebind()
295         self.assertEqual(mock_helper_func.call_count, 1)
296
297
298 class TestDpdkBindHelper(unittest.TestCase):
299     bin_path = "/opt/nsb_bin"
300     EXAMPLE_OUTPUT = """
301
302 Network devices using DPDK-compatible driver
303 ============================================
304 0000:00:04.0 'Virtio network device' drv=igb_uio unused=
305 0000:00:05.0 'Virtio network device' drv=igb_uio unused=
306
307 Network devices using kernel driver
308 ===================================
309 0000:00:03.0 'Virtio network device' if=ens3 drv=virtio-pci unused=igb_uio *Active*
310
311 Other network devices
312 =====================
313 <none>
314
315 Crypto devices using DPDK-compatible driver
316 ===========================================
317 <none>
318
319 Crypto devices using kernel driver
320 ==================================
321 <none>
322
323 Other crypto devices
324 ====================
325 <none>
326 """
327
328     PARSED_EXAMPLE = {
329         NETWORK_DPDK: [
330             {'active': False,
331              'dev_type': 'Virtio network device',
332              'driver': 'igb_uio',
333              'iface': None,
334              'unused': '',
335              'vpci': '0000:00:04.0',
336              },
337             {'active': False,
338              'dev_type': 'Virtio network device',
339              'driver': 'igb_uio',
340              'iface': None,
341              'unused': '',
342              'vpci': '0000:00:05.0',
343              }
344         ],
345         NETWORK_KERNEL: [
346             {'active': True,
347              'dev_type': 'Virtio network device',
348              'driver': 'virtio-pci',
349              'iface': 'ens3',
350              'unused': 'igb_uio',
351              'vpci': '0000:00:03.0',
352              }
353         ],
354         CRYPTO_KERNEL: [],
355         CRYPTO_DPDK: [],
356         NETWORK_OTHER: [],
357         CRYPTO_OTHER: [],
358     }
359
360     CLEAN_STATUS = {
361         NETWORK_KERNEL: [],
362         NETWORK_DPDK: [],
363         CRYPTO_KERNEL: [],
364         CRYPTO_DPDK: [],
365         NETWORK_OTHER: [],
366         CRYPTO_OTHER: [],
367     }
368
369     ONE_INPUT_LINE = ("0000:00:03.0 'Virtio network device' if=ens3 "
370                       "drv=virtio-pci unused=igb_uio *Active*")
371
372     ONE_INPUT_LINE_PARSED = [{
373         'vpci': '0000:00:03.0',
374         'dev_type': 'Virtio network device',
375         'iface': 'ens3',
376         'driver': 'virtio-pci',
377         'unused': 'igb_uio',
378         'active': True,
379     }]
380
381     def test___init__(self):
382         conn = mock.Mock()
383         conn.provision_tool = mock.Mock(return_value='path_to_tool')
384         conn.join_bin_path.return_value = os.path.join(self.bin_path, DpdkBindHelper.DPDK_DEVBIND)
385
386         dpdk_bind_helper = DpdkBindHelper(conn)
387
388         self.assertEqual(conn, dpdk_bind_helper.ssh_helper)
389         self.assertEqual(self.CLEAN_STATUS, dpdk_bind_helper.dpdk_status)
390         self.assertIsNone(dpdk_bind_helper.status_nic_row_re)
391         self.assertEqual(dpdk_bind_helper.dpdk_devbind,
392                          os.path.join(self.bin_path, dpdk_bind_helper.DPDK_DEVBIND))
393         self.assertIsNone(dpdk_bind_helper._status_cmd_attr)
394
395     def test__dpdk_execute(self):
396         conn = mock.Mock()
397         conn.execute = mock.Mock(return_value=(0, 'output', 'error'))
398         conn.provision_tool = mock.Mock(return_value='tool_path')
399         dpdk_bind_helper = DpdkBindHelper(conn)
400         self.assertEqual((0, 'output', 'error'), dpdk_bind_helper._dpdk_execute('command'))
401
402     def test__dpdk_execute_failure(self):
403         conn = mock.Mock()
404         conn.execute = mock.Mock(return_value=(1, 'output', 'error'))
405         conn.provision_tool = mock.Mock(return_value='tool_path')
406         dpdk_bind_helper = DpdkBindHelper(conn)
407         with self.assertRaises(DpdkBindHelperException):
408             dpdk_bind_helper._dpdk_execute('command')
409
410     def test__addline(self):
411         conn = mock.Mock()
412
413         dpdk_bind_helper = DpdkBindHelper(conn)
414
415         dpdk_bind_helper._add_line(NETWORK_KERNEL, self.ONE_INPUT_LINE)
416
417         self.assertIsNotNone(dpdk_bind_helper.dpdk_status)
418         self.assertEqual(self.ONE_INPUT_LINE_PARSED, dpdk_bind_helper.dpdk_status[NETWORK_KERNEL])
419
420     def test__switch_active_dict_by_header(self):
421         line = "Crypto devices using DPDK-compatible driver"
422         olddict = 'olddict'
423         self.assertEqual(CRYPTO_DPDK, DpdkBindHelper._switch_active_dict(line, olddict))
424
425     def test__switch_active_dict_by_header_empty(self):
426         line = "<none>"
427         olddict = 'olddict'
428         self.assertEqual(olddict, DpdkBindHelper._switch_active_dict(line, olddict))
429
430     def test_parse_dpdk_status_output(self):
431         conn = mock.Mock()
432
433         dpdk_bind_helper = DpdkBindHelper(conn)
434
435         dpdk_bind_helper._parse_dpdk_status_output(self.EXAMPLE_OUTPUT)
436
437         self.maxDiff = None
438         self.assertEqual(self.PARSED_EXAMPLE, dpdk_bind_helper.dpdk_status)
439
440     def test_kernel_bound_pci_addresses(self):
441         mock_ssh_helper = mock.Mock()
442         mock_ssh_helper.execute.return_value = 0, '', ''
443
444         expected = ['a', 'b', 3]
445
446         dpdk_helper = DpdkBindHelper(mock_ssh_helper)
447         dpdk_helper.dpdk_status = {
448             NETWORK_DPDK: [{'vpci': 4}, {'vpci': 5}, {'vpci': 'g'}],
449             NETWORK_KERNEL: [{'vpci': 'a'}, {'vpci': 'b'}, {'vpci': 3}],
450             CRYPTO_DPDK: [],
451         }
452
453         result = dpdk_helper.kernel_bound_pci_addresses
454         self.assertEqual(result, expected)
455
456     def test_find_net_devices_negative(self):
457         mock_ssh_helper = mock.Mock()
458         mock_ssh_helper.execute.return_value = 1, 'error', 'debug'
459
460         dpdk_helper = DpdkBindHelper(mock_ssh_helper)
461
462         self.assertDictEqual(dpdk_helper.find_net_devices(), {})
463
464     def test_read_status(self):
465         conn = mock.Mock()
466         conn.execute = mock.Mock(return_value=(0, self.EXAMPLE_OUTPUT, ''))
467         conn.provision_tool = mock.Mock(return_value='path_to_tool')
468
469         dpdk_bind_helper = DpdkBindHelper(conn)
470
471         self.assertEqual(self.PARSED_EXAMPLE, dpdk_bind_helper.read_status())
472
473     def test__get_bound_pci_addresses(self):
474         conn = mock.Mock()
475
476         dpdk_bind_helper = DpdkBindHelper(conn)
477
478         dpdk_bind_helper._parse_dpdk_status_output(self.EXAMPLE_OUTPUT)
479
480         self.assertEqual(['0000:00:04.0', '0000:00:05.0'],
481                           dpdk_bind_helper._get_bound_pci_addresses(NETWORK_DPDK))
482         self.assertEqual(['0000:00:03.0'],
483                           dpdk_bind_helper._get_bound_pci_addresses(NETWORK_KERNEL))
484
485     def test_interface_driver_map(self):
486         conn = mock.Mock()
487
488         dpdk_bind_helper = DpdkBindHelper(conn)
489
490         dpdk_bind_helper._parse_dpdk_status_output(self.EXAMPLE_OUTPUT)
491
492         self.assertEqual({'0000:00:04.0': 'igb_uio',
493                           '0000:00:03.0': 'virtio-pci',
494                           '0000:00:05.0': 'igb_uio',
495                           },
496                          dpdk_bind_helper.interface_driver_map)
497
498     def test_bind(self):
499         conn = mock.Mock()
500         conn.execute = mock.Mock(return_value=(0, '', ''))
501         conn.join_bin_path.return_value = os.path.join(self.bin_path, DpdkBindHelper.DPDK_DEVBIND)
502
503         dpdk_bind_helper = DpdkBindHelper(conn)
504         dpdk_bind_helper.read_status = mock.Mock()
505
506         dpdk_bind_helper.bind(['0000:00:03.0', '0000:00:04.0'], 'my_driver')
507
508         conn.execute.assert_called_with('sudo /opt/nsb_bin/dpdk-devbind.py --force '
509                                         '-b my_driver 0000:00:03.0 0000:00:04.0')
510         dpdk_bind_helper.read_status.assert_called_once()
511
512     def test_bind_single_pci(self):
513         conn = mock.Mock()
514         conn.execute = mock.Mock(return_value=(0, '', ''))
515         conn.join_bin_path.return_value = os.path.join(self.bin_path, DpdkBindHelper.DPDK_DEVBIND)
516
517         dpdk_bind_helper = DpdkBindHelper(conn)
518         dpdk_bind_helper.read_status = mock.Mock()
519
520         dpdk_bind_helper.bind('0000:00:03.0', 'my_driver')
521
522         conn.execute.assert_called_with('sudo /opt/nsb_bin/dpdk-devbind.py --force '
523                                         '-b my_driver 0000:00:03.0')
524         dpdk_bind_helper.read_status.assert_called_once()
525
526     def test_rebind_drivers(self):
527         conn = mock.Mock()
528
529         dpdk_bind_helper = DpdkBindHelper(conn)
530
531         dpdk_bind_helper.bind = mock.Mock()
532         dpdk_bind_helper.used_drivers = {
533             'd1': ['0000:05:00.0'],
534             'd3': ['0000:05:01.0', '0000:05:02.0'],
535         }
536
537         dpdk_bind_helper.rebind_drivers()
538
539         dpdk_bind_helper.bind.assert_any_call(['0000:05:00.0'], 'd1', True)
540         dpdk_bind_helper.bind.assert_any_call(['0000:05:01.0', '0000:05:02.0'], 'd3', True)
541
542     def test_save_used_drivers(self):
543         conn = mock.Mock()
544         dpdk_bind_helper = DpdkBindHelper(conn)
545         dpdk_bind_helper.dpdk_status = self.PARSED_EXAMPLE
546
547         dpdk_bind_helper.save_used_drivers()
548
549         expected = {
550             'igb_uio': ['0000:00:04.0', '0000:00:05.0'],
551             'virtio-pci': ['0000:00:03.0'],
552         }
553
554         self.assertDictEqual(expected, dpdk_bind_helper.used_drivers)
555
556     def test_force_dpdk_rebind(self):
557         mock_ssh_helper = mock.Mock()
558         mock_ssh_helper.execute.return_value = 0, '', ''
559
560         dpdk_helper = DpdkBindHelper(mock_ssh_helper, 'driver2')
561         dpdk_helper.dpdk_status = {
562             NETWORK_DPDK: [
563                 {
564                     'vpci': 'pci1',
565                 },
566                 {
567                     'vpci': 'pci3',
568                 },
569                 {
570                     'vpci': 'pci6',
571                 },
572                 {
573                     'vpci': 'pci3',
574                 },
575             ]
576         }
577         dpdk_helper.real_kernel_interface_driver_map = {
578             'pci1': 'real_driver1',
579             'pci2': 'real_driver2',
580             'pci3': 'real_driver1',
581             'pci4': 'real_driver4',
582             'pci6': 'real_driver6',
583         }
584         dpdk_helper.load_dpdk_driver = mock.Mock()
585         dpdk_helper.read_status = mock.Mock()
586         dpdk_helper.save_real_kernel_interface_driver_map = mock.Mock()
587         dpdk_helper.save_used_drivers = mock.Mock()
588         dpdk_helper.bind = mock_bind = mock.Mock()
589
590         dpdk_helper.force_dpdk_rebind()
591         self.assertEqual(mock_bind.call_count, 2)
592
593     def test_save_real_kernel_drivers(self):
594         mock_ssh_helper = mock.Mock()
595         mock_ssh_helper.execute.return_value = 0, '', ''
596
597         dpdk_helper = DpdkBindHelper(mock_ssh_helper)
598         dpdk_helper.real_kernel_drivers = {
599             'abc': '123',
600         }
601         dpdk_helper.real_kernel_interface_driver_map = {
602             'abc': 'AAA',
603             'def': 'DDD',
604             'abs': 'AAA',
605             'ghi': 'GGG',
606         }
607
608         # save_used_drivers must be called before save_real_kernel_drivers can be
609         with self.assertRaises(AttributeError):
610             dpdk_helper.save_real_kernel_drivers()
611
612         dpdk_helper.save_used_drivers()
613
614         expected_used_drivers = {
615             'AAA': ['abc', 'abs'],
616             'DDD': ['def'],
617             'GGG': ['ghi'],
618         }
619         dpdk_helper.save_real_kernel_drivers()
620         self.assertDictEqual(dpdk_helper.used_drivers, expected_used_drivers)
621         self.assertDictEqual(dpdk_helper.real_kernel_drivers, {})
622
623     def test_get_real_kernel_driver(self):
624         mock_ssh_helper = mock.Mock()
625         mock_ssh_helper.execute.side_effect = [
626             (0, 'non-matching text', ''),
627             (0, 'pre Kernel modules: real_driver1', ''),
628             (0, 'before Ethernet middle Virtio network device after', ''),
629         ]
630
631         dpdk_helper = DpdkBindHelper(mock_ssh_helper)
632
633         self.assertIsNone(dpdk_helper.get_real_kernel_driver('abc'))
634         self.assertEqual(dpdk_helper.get_real_kernel_driver('abc'), 'real_driver1')
635         self.assertEqual(dpdk_helper.get_real_kernel_driver('abc'), DpdkBindHelper.VIRTIO_DRIVER)