Setup hugepages on SA host(sriov, ovs_dpdk)
[yardstick.git] / yardstick / tests / unit / benchmark / contexts / standalone / test_ovs_dpdk.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 import io
16 import os
17
18 import mock
19 import six
20 import unittest
21
22 from yardstick.benchmark import contexts
23 from yardstick.benchmark.contexts import base
24 from yardstick.benchmark.contexts.standalone import model
25 from yardstick.benchmark.contexts.standalone import ovs_dpdk
26 from yardstick.common import exceptions
27 from yardstick.common import utils as common_utils
28 from yardstick.network_services import utils
29
30
31 class OvsDpdkContextTestCase(unittest.TestCase):
32
33     NODES_SAMPLE = "nodes_sample.yaml"
34     NODES_ovs_dpdk_SAMPLE = "nodes_ovs_dpdk_sample.yaml"
35     NODES_DUPLICATE_SAMPLE = "nodes_duplicate_sample.yaml"
36
37     NETWORKS = {
38         'private_0': {
39             'phy_port': "0000:05:00.0",
40             'vpci': "0000:00:07.0",
41             'cidr': '152.16.100.10/24',
42             'interface': 'if0',
43             'mac': "00:00:00:00:00:01",
44             'vf_pci': {'vf_pci': 0},
45             'gateway_ip': '152.16.100.20'},
46         'public_0': {
47             'phy_port': "0000:05:00.1",
48             'vpci': "0000:00:08.0",
49             'cidr': '152.16.40.10/24',
50             'interface': 'if0',
51             'vf_pci': {'vf_pci': 0},
52             'mac': "00:00:00:00:00:01",
53             'gateway_ip': '152.16.100.20'},
54     }
55
56     def setUp(self):
57         self.attrs = {
58             'name': 'foo',
59             'task_id': '1234567890',
60             'file': self._get_file_abspath(self.NODES_ovs_dpdk_SAMPLE)
61         }
62         self.ovs_dpdk = ovs_dpdk.OvsDpdkContext()
63         self._mock_log = mock.patch.object(ovs_dpdk, 'LOG')
64         self.mock_log = self._mock_log.start()
65         self.addCleanup(self._remove_contexts)
66         self.addCleanup(self._stop_mocks)
67
68     @staticmethod
69     def _remove_contexts():
70         for context in base.Context.list:
71             context._delete_context()
72         base.Context.list = []
73
74     def _stop_mocks(self):
75         self._mock_log.stop()
76
77     @mock.patch('yardstick.benchmark.contexts.standalone.model.Server')
78     @mock.patch('yardstick.benchmark.contexts.standalone.model.StandaloneContextHelper')
79     def test___init__(self, mock_helper, mock_server):
80         self.ovs_dpdk.helper = mock_helper
81         self.ovs_dpdk.vnf_node = mock_server
82         self.assertIsNone(self.ovs_dpdk.file_path)
83         self.assertTrue(self.ovs_dpdk.first_run)
84
85     def test_init(self):
86         ATTRS = {
87             'name': contexts.CONTEXT_STANDALONEOVSDPDK,
88             'task_id': '1234567890',
89             'file': 'pod',
90             'flavor': {},
91             'servers': {},
92             'networks': {},
93         }
94
95         self.ovs_dpdk.helper.parse_pod_file = mock.Mock(
96             return_value=[{}, {}, {}])
97         self.assertIsNone(self.ovs_dpdk.init(ATTRS))
98
99     def test_setup_ovs(self):
100         fake_path = '/fake_path'
101         fake_dpdk_nic_bind = 'dpdk_tool.py'
102         self.ovs_dpdk.ovs_properties = {'vpath': fake_path}
103         self.ovs_dpdk.dpdk_devbind = fake_dpdk_nic_bind
104         self.ovs_dpdk.networks = self.NETWORKS
105         self.ovs_dpdk.connection = mock.Mock()
106         self.ovs_dpdk.connection.execute = mock.Mock(return_value=(0, 0, 0))
107         create_from = fake_path + '/etc/openvswitch/conf.db'
108         create_to = fake_path + '/share/openvswitch/vswitch.ovsschema'
109         cmd_list = [
110             'killall -r "ovs.*" -q | true',
111             'mkdir -p {0}/etc/openvswitch'.format(fake_path),
112             'mkdir -p {0}/var/run/openvswitch'.format(fake_path),
113             'rm {0}/etc/openvswitch/conf.db | true'.format(fake_path),
114             'ovsdb-tool create {0} {1}'.format(create_from, create_to),
115             'modprobe vfio-pci',
116             'chmod a+x /dev/vfio',
117             'chmod 0666 /dev/vfio/*',
118             '{0} --force -b vfio-pci {1}'.format(fake_dpdk_nic_bind,
119                 self.ovs_dpdk.networks['private_0']['phy_port']),
120             '{0} --force -b vfio-pci {1}'.format(fake_dpdk_nic_bind,
121                 self.ovs_dpdk.networks['public_0']['phy_port'])
122         ]
123         calls = [mock.call(cmd, timeout=self.ovs_dpdk.CMD_TIMEOUT)
124                  for cmd in cmd_list]
125
126         self.ovs_dpdk.setup_ovs()
127         self.ovs_dpdk.connection.execute.assert_has_calls(calls,
128                                                           any_order=True)
129
130     def test_setup_ovs_exception(self):
131         self.ovs_dpdk.networks = self.NETWORKS
132         self.ovs_dpdk.connection = mock.Mock()
133         self.ovs_dpdk.connection.execute = mock.Mock(return_value=(1, 0, 0))
134
135         with self.assertRaises(exceptions.OVSSetupError):
136             self.ovs_dpdk.setup_ovs()
137
138     def test_start_ovs_serverswitch(self):
139         with mock.patch("yardstick.ssh.SSH") as ssh:
140             ssh_mock = mock.Mock(autospec=ssh.SSH)
141             ssh_mock.execute = \
142                 mock.Mock(return_value=(0, "a", ""))
143             ssh.return_value = ssh_mock
144             self.ovs_dpdk.connection = ssh_mock
145             self.ovs_dpdk.networks = self.NETWORKS
146             self.ovs_dpdk.ovs_properties = {}
147             self.ovs_dpdk.wait_for_vswitchd = 0
148             self.assertIsNone(self.ovs_dpdk.start_ovs_serverswitch())
149
150     def test_setup_ovs_bridge_add_flows(self):
151         with mock.patch("yardstick.ssh.SSH") as ssh:
152             ssh_mock = mock.Mock(autospec=ssh.SSH)
153             ssh_mock.execute = \
154                 mock.Mock(return_value=(0, "a", ""))
155             ssh.return_value = ssh_mock
156             self.ovs_dpdk.connection = ssh_mock
157             self.ovs_dpdk.networks = self.NETWORKS
158             self.ovs_dpdk.ovs_properties = {
159                 'version': {'ovs': '2.7.0'}
160             }
161             self.ovs_dpdk.wait_for_vswitchd = 0
162             self.assertIsNone(self.ovs_dpdk.setup_ovs_bridge_add_flows())
163
164     @mock.patch("yardstick.ssh.SSH")
165     def test_cleanup_ovs_dpdk_env(self, mock_ssh):
166        mock_ssh.execute.return_value = 0, "a", ""
167        self.ovs_dpdk.connection = mock_ssh
168        self.ovs_dpdk.networks = self.NETWORKS
169        self.ovs_dpdk.ovs_properties = {
170            'version': {'ovs': '2.7.0'}
171        }
172        self.ovs_dpdk.wait_for_vswitchd = 0
173        self.assertIsNone(self.ovs_dpdk.cleanup_ovs_dpdk_env())
174
175     @mock.patch.object(utils, 'get_nsb_option')
176     @mock.patch.object(model.OvsDeploy, 'ovs_deploy')
177     def test_check_ovs_dpdk_env(self, mock_ovs_deploy, mock_get_nsb_option):
178         self.ovs_dpdk.connection = mock.Mock()
179         self.ovs_dpdk.connection.execute = mock.Mock(
180             return_value=(1, 0, 0))
181         self.ovs_dpdk.networks = self.NETWORKS
182         self.ovs_dpdk.ovs_properties = {
183             'version': {'ovs': '2.7.0', 'dpdk': '16.11.1'}
184         }
185         self.ovs_dpdk.wait_for_vswitchd = 0
186         self.ovs_dpdk.cleanup_ovs_dpdk_env = mock.Mock()
187         mock_get_nsb_option.return_value = 'fake_path'
188
189         self.ovs_dpdk.check_ovs_dpdk_env()
190         mock_ovs_deploy.assert_called_once()
191         mock_get_nsb_option.assert_called_once_with('bin_path')
192
193     def test_check_ovs_dpdk_env_wrong_version(self):
194         self.ovs_dpdk.connection = mock.Mock()
195         self.ovs_dpdk.connection.execute = mock.Mock(
196             return_value=(1, 0, 0))
197         self.ovs_dpdk.networks = self.NETWORKS
198         self.ovs_dpdk.ovs_properties = {
199             'version': {'ovs': '0.0.1', 'dpdk': '9.8.7'}
200         }
201         self.ovs_dpdk.wait_for_vswitchd = 0
202         self.ovs_dpdk.cleanup_ovs_dpdk_env = mock.Mock()
203
204         with self.assertRaises(exceptions.OVSUnsupportedVersion):
205             self.ovs_dpdk.check_ovs_dpdk_env()
206
207     @mock.patch('yardstick.ssh.SSH')
208     def test_deploy(self, *args):
209         self.ovs_dpdk.vm_deploy = False
210         self.assertIsNone(self.ovs_dpdk.deploy())
211
212         self.ovs_dpdk.vm_deploy = True
213         self.ovs_dpdk.host_mgmt = {}
214         self.ovs_dpdk.install_req_libs = mock.Mock()
215         self.ovs_dpdk.helper.get_nic_details = mock.Mock(return_value={})
216         self.ovs_dpdk.check_ovs_dpdk_env = mock.Mock(return_value={})
217         self.ovs_dpdk.setup_ovs = mock.Mock(return_value={})
218         self.ovs_dpdk.start_ovs_serverswitch = mock.Mock(return_value={})
219         self.ovs_dpdk.setup_ovs_bridge_add_flows = mock.Mock(return_value={})
220         self.ovs_dpdk.setup_ovs_dpdk_context = mock.Mock(return_value={})
221         self.ovs_dpdk.wait_for_vnfs_to_start = mock.Mock(return_value={})
222         # TODO(elfoley): This test should check states/sideeffects instead of
223         # output.
224         self.assertIsNone(self.ovs_dpdk.deploy())
225
226     @mock.patch.object(model.Libvirt, 'check_if_vm_exists_and_delete')
227     def test_undeploy(self, mock_libvirt):
228         self.ovs_dpdk.vm_deploy = True
229         self.ovs_dpdk.connection = mock.Mock()
230         self.ovs_dpdk.vm_names = ['vm-0', 'vm-1']
231         self.ovs_dpdk.drivers = ['vm-0', 'vm-1']
232         self.ovs_dpdk.cleanup_ovs_dpdk_env = mock.Mock()
233         self.ovs_dpdk.networks = self.NETWORKS
234         self.ovs_dpdk.undeploy()
235         mock_libvirt.assert_has_calls([
236             mock.call(self.ovs_dpdk.vm_names[0], self.ovs_dpdk.connection),
237             mock.call(self.ovs_dpdk.vm_names[1], self.ovs_dpdk.connection)
238         ])
239
240     def _get_file_abspath(self, filename):
241         curr_path = os.path.dirname(os.path.abspath(__file__))
242         file_path = os.path.join(curr_path, filename)
243         return file_path
244
245     def test__get_server_with_dic_attr_name(self):
246
247         self.ovs_dpdk.init(self.attrs)
248
249         attr_name = {'name': 'foo.bar'}
250         result = self.ovs_dpdk._get_server(attr_name)
251
252         self.assertEqual(result, None)
253
254     def test__get_server_not_found(self):
255
256         self.ovs_dpdk.helper.parse_pod_file = mock.Mock(
257             return_value=[{}, {}, {}])
258         self.ovs_dpdk.init(self.attrs)
259
260         attr_name = 'bar.foo'
261         result = self.ovs_dpdk._get_server(attr_name)
262
263         self.assertEqual(result, None)
264
265     def test__get_server_mismatch(self):
266
267         self.ovs_dpdk.init(self.attrs)
268
269         attr_name = 'bar.foo1'
270         result = self.ovs_dpdk._get_server(attr_name)
271
272         self.assertEqual(result, None)
273
274     def test__get_server_duplicate(self):
275
276         self.attrs['file'] = self._get_file_abspath(self.NODES_DUPLICATE_SAMPLE)
277
278         self.ovs_dpdk.init(self.attrs)
279
280         attr_name = 'node1.foo-12345678'
281         with self.assertRaises(ValueError):
282             self.ovs_dpdk._get_server(attr_name)
283
284     def test__get_server_found(self):
285
286         self.ovs_dpdk.init(self.attrs)
287
288         attr_name = 'node1.foo-12345678'
289         result = self.ovs_dpdk._get_server(attr_name)
290
291         self.assertEqual(result['ip'], '10.229.47.137')
292         self.assertEqual(result['name'], 'node1.foo-12345678')
293         self.assertEqual(result['user'], 'root')
294         self.assertEqual(result['key_filename'], '/root/.yardstick_key')
295
296     def test__get_physical_node_for_server(self):
297         attrs = self.attrs
298         attrs.update({'servers': {'server1': {}}})
299         self.ovs_dpdk.init(attrs)
300
301         # When server is not from this context
302         result = self.ovs_dpdk._get_physical_node_for_server('server1.another-context')
303         self.assertIsNone(result)
304
305         # When node_name is not from this context
306         result = self.ovs_dpdk._get_physical_node_for_server('fake.foo-12345678')
307         self.assertIsNone(result)
308
309         result = self.ovs_dpdk._get_physical_node_for_server('server1.foo-12345678')
310         self.assertEqual(result, 'node5.foo')
311
312     # TODO(elfoley): Split this test for networks that exist and networks that
313     #                don't
314     def test__get_network(self):
315         network1 = {
316             'name': 'net_1',
317             'vld_id': 'vld111',
318             'segmentation_id': 'seg54',
319             'network_type': 'type_a',
320             'physical_network': 'phys',
321         }
322         network2 = {
323             'name': 'net_2',
324             'vld_id': 'vld999',
325         }
326         self.ovs_dpdk.networks = {
327             'a': network1,
328             'b': network2,
329         }
330
331         # Tests for networks that do not exist
332         attr_name = {}
333         self.assertIsNone(self.ovs_dpdk._get_network(attr_name))
334
335         attr_name = {'vld_id': 'vld777'}
336         self.assertIsNone(self.ovs_dpdk._get_network(attr_name))
337
338         self.assertIsNone(self.ovs_dpdk._get_network(None))
339
340         # TODO(elfoley): Split this test
341         attr_name = 'vld777'
342         self.assertIsNone(self.ovs_dpdk._get_network(attr_name))
343
344         # Tests for networks that exist
345         attr_name = {'vld_id': 'vld999'}
346         expected = {
347             "name": 'net_2',
348             "vld_id": 'vld999',
349             "segmentation_id": None,
350             "network_type": None,
351             "physical_network": None,
352         }
353         result = self.ovs_dpdk._get_network(attr_name)
354         self.assertDictEqual(result, expected)
355
356         attr_name = 'a'
357         expected = network1
358         result = self.ovs_dpdk._get_network(attr_name)
359         self.assertDictEqual(result, expected)
360
361     def test_configure_nics_for_ovs_dpdk(self):
362         with mock.patch("yardstick.ssh.SSH") as ssh:
363             ssh_mock = mock.Mock(autospec=ssh.SSH)
364             ssh_mock.execute = \
365                 mock.Mock(return_value=(0, "a", ""))
366             ssh.return_value = ssh_mock
367         self.ovs_dpdk.vm_deploy = True
368         self.ovs_dpdk.connection = ssh_mock
369         self.ovs_dpdk.vm_names = ['vm-0', 'vm-1']
370         self.ovs_dpdk.drivers = []
371         self.ovs_dpdk.networks = self.NETWORKS
372         self.ovs_dpdk.helper.get_mac_address = mock.Mock(return_value="")
373         self.ovs_dpdk.get_vf_datas = mock.Mock(return_value="")
374         self.assertIsNone(self.ovs_dpdk.configure_nics_for_ovs_dpdk())
375
376     @mock.patch.object(model.Libvirt, 'add_ovs_interface')
377     def test__enable_interfaces(self, mock_add_ovs_interface):
378         self.ovs_dpdk.vm_deploy = True
379         self.ovs_dpdk.connection = mock.Mock()
380         self.ovs_dpdk.vm_names = ['vm-0', 'vm-1']
381         self.ovs_dpdk.drivers = []
382         self.ovs_dpdk.networks = self.NETWORKS
383         self.ovs_dpdk.ovs_properties = {'vpath': 'fake_path'}
384         self.ovs_dpdk.get_vf_datas = mock.Mock(return_value="")
385         self.ovs_dpdk._enable_interfaces(0, ["private_0"], 'test')
386         mock_add_ovs_interface.assert_called_once_with(
387             'fake_path', 0, self.NETWORKS['private_0']['vpci'],
388             self.NETWORKS['private_0']['mac'], 'test')
389
390     @mock.patch.object(ovs_dpdk.OvsDpdkContext, '_check_hugepages')
391     @mock.patch.object(common_utils, 'setup_hugepages')
392     @mock.patch.object(model.StandaloneContextHelper, 'check_update_key')
393     @mock.patch.object(model.Libvirt, 'write_file')
394     @mock.patch.object(model.Libvirt, 'build_vm_xml')
395     @mock.patch.object(model.Libvirt, 'check_if_vm_exists_and_delete')
396     @mock.patch.object(model.Libvirt, 'virsh_create_vm')
397     def test_setup_ovs_dpdk_context(self, mock_create_vm, mock_check_if_exists,
398                                     mock_build_xml, mock_write_file,
399                                     mock_check_update_key,
400                                     mock_setup_hugepages,
401                                     mock__check_hugepages):
402         self.ovs_dpdk.vm_deploy = True
403         self.ovs_dpdk.connection = mock.Mock()
404         self.ovs_dpdk.vm_names = ['vm-0', 'vm-1']
405         self.ovs_dpdk.drivers = []
406         self.ovs_dpdk.servers = {
407             'vnf_0': {
408                 'network_ports': {
409                     'mgmt': {'cidr': '152.16.100.10/24'},
410                     'xe0': ['private_0'],
411                     'xe1': ['public_0']
412                 }
413             }
414         }
415         self.ovs_dpdk.networks = self.NETWORKS
416         self.ovs_dpdk.host_mgmt = {}
417         self.ovs_dpdk.vm_flavor = {'ram': '1024'}
418         self.ovs_dpdk.file_path = '/var/lib/libvirt/images/cdrom-0.img'
419         self.ovs_dpdk.configure_nics_for_ovs_dpdk = mock.Mock(return_value="")
420         self.ovs_dpdk._name_task_id = 'fake_name'
421         xml_str = 'vm-0'
422         self.ovs_dpdk.mac = '00:00:00:00:00:01'
423         mock_build_xml.return_value = (xml_str, self.ovs_dpdk.mac)
424         self.ovs_dpdk._enable_interfaces = mock.Mock(return_value=xml_str)
425         vnf_instance = mock.Mock()
426         vnf_instance_2 = mock.Mock()
427         mock_check_update_key.return_value = vnf_instance_2
428         self.ovs_dpdk.vnf_node.generate_vnf_instance = mock.Mock(
429             return_value=vnf_instance)
430
431         self.assertEqual([vnf_instance_2],
432                          self.ovs_dpdk.setup_ovs_dpdk_context())
433         mock_setup_hugepages.assert_called_once_with(self.ovs_dpdk.connection, 1024 * 1024)
434         mock__check_hugepages.assert_called_once()
435         mock_create_vm.assert_called_once_with(
436             self.ovs_dpdk.connection, '/tmp/vm_ovs_0.xml')
437         mock_check_if_exists.assert_called_once_with(
438             'vm-0', self.ovs_dpdk.connection)
439         mock_build_xml.assert_called_once_with(
440             self.ovs_dpdk.connection, self.ovs_dpdk.vm_flavor, 'vm-0', 0, self.ovs_dpdk.file_path)
441         mock_write_file.assert_called_once_with('/tmp/vm_ovs_0.xml', xml_str)
442         mock_check_update_key.assert_called_once_with(self.ovs_dpdk.connection,
443                                                       vnf_instance,
444                                                       xml_str,
445                                                       self.ovs_dpdk._name_task_id,
446                                                       self.ovs_dpdk.file_path,
447                                                       self.ovs_dpdk.mac)
448
449     @mock.patch.object(io, 'BytesIO')
450     def test__check_hugepages(self, mock_bytesio):
451         data = six.BytesIO('HugePages_Total:      20\n'
452                            'HugePages_Free:       20\n'
453                            'HugePages_Rsvd:        0\n'
454                            'HugePages_Surp:        0\n'
455                            'Hugepagesize:    1048576 kB'.encode())
456         mock_bytesio.return_value = data
457         self.ovs_dpdk.connection = mock.Mock()
458         self.ovs_dpdk._check_hugepages()
459
460     @mock.patch.object(io, 'BytesIO')
461     def test__check_hugepages_no_info(self, mock_bytesio):
462         data = six.BytesIO(''.encode())
463         mock_bytesio.return_value = data
464         self.ovs_dpdk.connection = mock.Mock()
465         with self.assertRaises(exceptions.OVSHugepagesInfoError):
466             self.ovs_dpdk._check_hugepages()
467
468     @mock.patch.object(io, 'BytesIO')
469     def test__check_hugepages_no_total_hp(self, mock_bytesio):
470         data = six.BytesIO('HugePages_Total:       0\n'
471                            'HugePages_Free:        0\n'
472                            'HugePages_Rsvd:        0\n'
473                            'HugePages_Surp:        0\n'
474                            'Hugepagesize:    1048576 kB'.encode())
475         mock_bytesio.return_value = data
476         self.ovs_dpdk.connection = mock.Mock()
477         with self.assertRaises(exceptions.OVSHugepagesNotConfigured):
478             self.ovs_dpdk._check_hugepages()
479
480     @mock.patch.object(io, 'BytesIO')
481     def test__check_hugepages_no_free_hp(self, mock_bytesio):
482         data = six.BytesIO('HugePages_Total:      20\n'
483                            'HugePages_Free:        0\n'
484                            'HugePages_Rsvd:        0\n'
485                            'HugePages_Surp:        0\n'
486                            'Hugepagesize:    1048576 kB'.encode())
487         mock_bytesio.return_value = data
488         self.ovs_dpdk.connection = mock.Mock()
489         with self.assertRaises(exceptions.OVSHugepagesZeroFree) as exc:
490             self.ovs_dpdk._check_hugepages()
491         self.assertEqual('There are no HugePages free in this system. Total '
492                          'HugePages configured: 20', exc.exception.msg)