Merge "Fix OvS-DPDK context mem allocation problem"
[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', 1)
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,
434             (1024 + 4096) * 1024) # ram + dpdk_socket0_mem + dpdk_socket1_mem
435         mock__check_hugepages.assert_called_once()
436         mock_create_vm.assert_called_once_with(
437             self.ovs_dpdk.connection, '/tmp/vm_ovs_0.xml')
438         mock_check_if_exists.assert_called_once_with(
439             'vm-0', self.ovs_dpdk.connection)
440         mock_build_xml.assert_called_once_with(
441             self.ovs_dpdk.connection, self.ovs_dpdk.vm_flavor, 'vm-0', 0, self.ovs_dpdk.file_path)
442         mock_write_file.assert_called_once_with('/tmp/vm_ovs_0.xml', xml_str)
443         mock_check_update_key.assert_called_once_with(self.ovs_dpdk.connection,
444                                                       vnf_instance,
445                                                       xml_str,
446                                                       self.ovs_dpdk._name_task_id,
447                                                       self.ovs_dpdk.file_path,
448                                                       self.ovs_dpdk.mac)
449
450     @mock.patch.object(io, 'BytesIO')
451     def test__check_hugepages(self, mock_bytesio):
452         data = six.BytesIO('HugePages_Total:      20\n'
453                            'HugePages_Free:       20\n'
454                            'HugePages_Rsvd:        0\n'
455                            'HugePages_Surp:        0\n'
456                            'Hugepagesize:    1048576 kB'.encode())
457         mock_bytesio.return_value = data
458         self.ovs_dpdk.connection = mock.Mock()
459         self.ovs_dpdk._check_hugepages()
460
461     @mock.patch.object(io, 'BytesIO')
462     def test__check_hugepages_no_info(self, mock_bytesio):
463         data = six.BytesIO(''.encode())
464         mock_bytesio.return_value = data
465         self.ovs_dpdk.connection = mock.Mock()
466         with self.assertRaises(exceptions.OVSHugepagesInfoError):
467             self.ovs_dpdk._check_hugepages()
468
469     @mock.patch.object(io, 'BytesIO')
470     def test__check_hugepages_no_total_hp(self, mock_bytesio):
471         data = six.BytesIO('HugePages_Total:       0\n'
472                            'HugePages_Free:        0\n'
473                            'HugePages_Rsvd:        0\n'
474                            'HugePages_Surp:        0\n'
475                            'Hugepagesize:    1048576 kB'.encode())
476         mock_bytesio.return_value = data
477         self.ovs_dpdk.connection = mock.Mock()
478         with self.assertRaises(exceptions.OVSHugepagesNotConfigured):
479             self.ovs_dpdk._check_hugepages()
480
481     @mock.patch.object(io, 'BytesIO')
482     def test__check_hugepages_no_free_hp(self, mock_bytesio):
483         data = six.BytesIO('HugePages_Total:      20\n'
484                            'HugePages_Free:        0\n'
485                            'HugePages_Rsvd:        0\n'
486                            'HugePages_Surp:        0\n'
487                            'Hugepagesize:    1048576 kB'.encode())
488         mock_bytesio.return_value = data
489         self.ovs_dpdk.connection = mock.Mock()
490         with self.assertRaises(exceptions.OVSHugepagesZeroFree) as exc:
491             self.ovs_dpdk._check_hugepages()
492         self.assertEqual('There are no HugePages free in this system. Total '
493                          'HugePages configured: 20', exc.exception.msg)