Merge "Add test suite for scenario "opnfv_os-ovn-nofeature-ha""
[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             self.ovs_dpdk.ovs_properties.update(
164                 {'dpdk_pmd-rxq-affinity': {'0': "0:1"}})
165             self.ovs_dpdk.ovs_properties.update(
166                 {'vhost_pmd-rxq-affinity': {'0': "0:1"}})
167             self.NETWORKS['private_0'].update({'port_num': '0'})
168             self.NETWORKS['public_0'].update({'port_num': '1'})
169             self.ovs_dpdk.setup_ovs_bridge_add_flows()
170
171     @mock.patch("yardstick.ssh.SSH")
172     def test_cleanup_ovs_dpdk_env(self, mock_ssh):
173        mock_ssh.execute.return_value = 0, "a", ""
174        self.ovs_dpdk.connection = mock_ssh
175        self.ovs_dpdk.networks = self.NETWORKS
176        self.ovs_dpdk.ovs_properties = {
177            'version': {'ovs': '2.7.0'}
178        }
179        self.ovs_dpdk.wait_for_vswitchd = 0
180        self.assertIsNone(self.ovs_dpdk.cleanup_ovs_dpdk_env())
181
182     @mock.patch.object(utils, 'get_nsb_option')
183     @mock.patch.object(model.OvsDeploy, 'ovs_deploy')
184     def test_check_ovs_dpdk_env(self, mock_ovs_deploy, mock_get_nsb_option):
185         self.ovs_dpdk.connection = mock.Mock()
186         self.ovs_dpdk.connection.execute = mock.Mock(
187             return_value=(1, 0, 0))
188         self.ovs_dpdk.networks = self.NETWORKS
189         self.ovs_dpdk.ovs_properties = {
190             'version': {'ovs': '2.7.0', 'dpdk': '16.11.1'}
191         }
192         self.ovs_dpdk.wait_for_vswitchd = 0
193         self.ovs_dpdk.cleanup_ovs_dpdk_env = mock.Mock()
194         mock_get_nsb_option.return_value = 'fake_path'
195
196         self.ovs_dpdk.check_ovs_dpdk_env()
197         mock_ovs_deploy.assert_called_once()
198         mock_get_nsb_option.assert_called_once_with('bin_path')
199
200     def test_check_ovs_dpdk_env_wrong_version(self):
201         self.ovs_dpdk.connection = mock.Mock()
202         self.ovs_dpdk.connection.execute = mock.Mock(
203             return_value=(1, 0, 0))
204         self.ovs_dpdk.networks = self.NETWORKS
205         self.ovs_dpdk.ovs_properties = {
206             'version': {'ovs': '0.0.1', 'dpdk': '9.8.7'}
207         }
208         self.ovs_dpdk.wait_for_vswitchd = 0
209         self.ovs_dpdk.cleanup_ovs_dpdk_env = mock.Mock()
210
211         with self.assertRaises(exceptions.OVSUnsupportedVersion):
212             self.ovs_dpdk.check_ovs_dpdk_env()
213
214     @mock.patch('yardstick.ssh.SSH')
215     def test_deploy(self, *args):
216         self.ovs_dpdk.vm_deploy = False
217         self.assertIsNone(self.ovs_dpdk.deploy())
218
219         self.ovs_dpdk.vm_deploy = True
220         self.ovs_dpdk.host_mgmt = {}
221         self.ovs_dpdk.install_req_libs = mock.Mock()
222         self.ovs_dpdk.helper.get_nic_details = mock.Mock(return_value={})
223         self.ovs_dpdk.check_ovs_dpdk_env = mock.Mock(return_value={})
224         self.ovs_dpdk.setup_ovs = mock.Mock(return_value={})
225         self.ovs_dpdk.start_ovs_serverswitch = mock.Mock(return_value={})
226         self.ovs_dpdk.setup_ovs_bridge_add_flows = mock.Mock(return_value={})
227         self.ovs_dpdk.setup_ovs_dpdk_context = mock.Mock(return_value={})
228         self.ovs_dpdk.wait_for_vnfs_to_start = mock.Mock(return_value={})
229         # TODO(elfoley): This test should check states/sideeffects instead of
230         # output.
231         self.assertIsNone(self.ovs_dpdk.deploy())
232
233     @mock.patch.object(model.Libvirt, 'check_if_vm_exists_and_delete')
234     def test_undeploy(self, mock_libvirt):
235         self.ovs_dpdk.vm_deploy = True
236         self.ovs_dpdk.connection = mock.Mock()
237         self.ovs_dpdk.vm_names = ['vm-0', 'vm-1']
238         self.ovs_dpdk.drivers = ['vm-0', 'vm-1']
239         self.ovs_dpdk.cleanup_ovs_dpdk_env = mock.Mock()
240         self.ovs_dpdk.networks = self.NETWORKS
241         self.ovs_dpdk.undeploy()
242         mock_libvirt.assert_has_calls([
243             mock.call(self.ovs_dpdk.vm_names[0], self.ovs_dpdk.connection),
244             mock.call(self.ovs_dpdk.vm_names[1], self.ovs_dpdk.connection)
245         ])
246
247     def _get_file_abspath(self, filename):
248         curr_path = os.path.dirname(os.path.abspath(__file__))
249         file_path = os.path.join(curr_path, filename)
250         return file_path
251
252     def test__get_server_with_dic_attr_name(self):
253
254         self.ovs_dpdk.init(self.attrs)
255
256         attr_name = {'name': 'foo.bar'}
257         result = self.ovs_dpdk._get_server(attr_name)
258
259         self.assertEqual(result, None)
260
261     def test__get_server_not_found(self):
262
263         self.ovs_dpdk.helper.parse_pod_file = mock.Mock(
264             return_value=[{}, {}, {}])
265         self.ovs_dpdk.init(self.attrs)
266
267         attr_name = 'bar.foo'
268         result = self.ovs_dpdk._get_server(attr_name)
269
270         self.assertEqual(result, None)
271
272     def test__get_server_mismatch(self):
273
274         self.ovs_dpdk.init(self.attrs)
275
276         attr_name = 'bar.foo1'
277         result = self.ovs_dpdk._get_server(attr_name)
278
279         self.assertEqual(result, None)
280
281     def test__get_server_duplicate(self):
282
283         self.attrs['file'] = self._get_file_abspath(self.NODES_DUPLICATE_SAMPLE)
284
285         self.ovs_dpdk.init(self.attrs)
286
287         attr_name = 'node1.foo-12345678'
288         with self.assertRaises(ValueError):
289             self.ovs_dpdk._get_server(attr_name)
290
291     def test__get_server_found(self):
292
293         self.ovs_dpdk.init(self.attrs)
294
295         attr_name = 'node1.foo-12345678'
296         result = self.ovs_dpdk._get_server(attr_name)
297
298         self.assertEqual(result['ip'], '10.229.47.137')
299         self.assertEqual(result['name'], 'node1.foo-12345678')
300         self.assertEqual(result['user'], 'root')
301         self.assertEqual(result['key_filename'], '/root/.yardstick_key')
302
303     def test__get_physical_node_for_server(self):
304         attrs = self.attrs
305         attrs.update({'servers': {'server1': {}}})
306         self.ovs_dpdk.init(attrs)
307
308         # When server is not from this context
309         result = self.ovs_dpdk._get_physical_node_for_server('server1.another-context')
310         self.assertIsNone(result)
311
312         # When node_name is not from this context
313         result = self.ovs_dpdk._get_physical_node_for_server('fake.foo-12345678')
314         self.assertIsNone(result)
315
316         result = self.ovs_dpdk._get_physical_node_for_server('server1.foo-12345678')
317         self.assertEqual(result, 'node5.foo')
318
319     # TODO(elfoley): Split this test for networks that exist and networks that
320     #                don't
321     def test__get_network(self):
322         network1 = {
323             'name': 'net_1',
324             'vld_id': 'vld111',
325             'segmentation_id': 'seg54',
326             'network_type': 'type_a',
327             'physical_network': 'phys',
328         }
329         network2 = {
330             'name': 'net_2',
331             'vld_id': 'vld999',
332         }
333         self.ovs_dpdk.networks = {
334             'a': network1,
335             'b': network2,
336         }
337
338         # Tests for networks that do not exist
339         attr_name = {}
340         self.assertIsNone(self.ovs_dpdk._get_network(attr_name))
341
342         attr_name = {'vld_id': 'vld777'}
343         self.assertIsNone(self.ovs_dpdk._get_network(attr_name))
344
345         self.assertIsNone(self.ovs_dpdk._get_network(None))
346
347         # TODO(elfoley): Split this test
348         attr_name = 'vld777'
349         self.assertIsNone(self.ovs_dpdk._get_network(attr_name))
350
351         # Tests for networks that exist
352         attr_name = {'vld_id': 'vld999'}
353         expected = {
354             "name": 'net_2',
355             "vld_id": 'vld999',
356             "segmentation_id": None,
357             "network_type": None,
358             "physical_network": None,
359         }
360         result = self.ovs_dpdk._get_network(attr_name)
361         self.assertDictEqual(result, expected)
362
363         attr_name = 'a'
364         expected = network1
365         result = self.ovs_dpdk._get_network(attr_name)
366         self.assertDictEqual(result, expected)
367
368     def test_configure_nics_for_ovs_dpdk(self):
369         with mock.patch("yardstick.ssh.SSH") as ssh:
370             ssh_mock = mock.Mock(autospec=ssh.SSH)
371             ssh_mock.execute = \
372                 mock.Mock(return_value=(0, "a", ""))
373             ssh.return_value = ssh_mock
374         self.ovs_dpdk.vm_deploy = True
375         self.ovs_dpdk.connection = ssh_mock
376         self.ovs_dpdk.vm_names = ['vm-0', 'vm-1']
377         self.ovs_dpdk.drivers = []
378         self.ovs_dpdk.networks = self.NETWORKS
379         self.ovs_dpdk.helper.get_mac_address = mock.Mock(return_value="")
380         self.ovs_dpdk.get_vf_datas = mock.Mock(return_value="")
381         self.assertIsNone(self.ovs_dpdk.configure_nics_for_ovs_dpdk())
382
383     @mock.patch.object(model.Libvirt, 'add_ovs_interface')
384     def test__enable_interfaces(self, mock_add_ovs_interface):
385         self.ovs_dpdk.vm_deploy = True
386         self.ovs_dpdk.connection = mock.Mock()
387         self.ovs_dpdk.vm_names = ['vm-0', 'vm-1']
388         self.ovs_dpdk.drivers = []
389         self.ovs_dpdk.networks = self.NETWORKS
390         self.ovs_dpdk.ovs_properties = {'vpath': 'fake_path'}
391         self.ovs_dpdk.get_vf_datas = mock.Mock(return_value="")
392         self.ovs_dpdk._enable_interfaces(0, ["private_0"], 'test')
393         mock_add_ovs_interface.assert_called_once_with(
394             'fake_path', 0, self.NETWORKS['private_0']['vpci'],
395             self.NETWORKS['private_0']['mac'], 'test', 1)
396
397     @mock.patch.object(ovs_dpdk.OvsDpdkContext, '_check_hugepages')
398     @mock.patch.object(common_utils, 'setup_hugepages')
399     @mock.patch.object(model.StandaloneContextHelper, 'check_update_key')
400     @mock.patch.object(model.Libvirt, 'write_file')
401     @mock.patch.object(model.Libvirt, 'build_vm_xml')
402     @mock.patch.object(model.Libvirt, 'check_if_vm_exists_and_delete')
403     @mock.patch.object(model.Libvirt, 'virsh_create_vm')
404     def test_setup_ovs_dpdk_context(self, mock_create_vm, mock_check_if_exists,
405                                     mock_build_xml, mock_write_file,
406                                     mock_check_update_key,
407                                     mock_setup_hugepages,
408                                     mock__check_hugepages):
409         self.ovs_dpdk.vm_deploy = True
410         self.ovs_dpdk.connection = mock.Mock()
411         self.ovs_dpdk.vm_names = ['vm-0', 'vm-1']
412         self.ovs_dpdk.drivers = []
413         self.ovs_dpdk.servers = {
414             'vnf_0': {
415                 'network_ports': {
416                     'mgmt': {'cidr': '152.16.100.10/24'},
417                     'xe0': ['private_0'],
418                     'xe1': ['public_0']
419                 }
420             }
421         }
422         self.ovs_dpdk.networks = self.NETWORKS
423         self.ovs_dpdk.host_mgmt = {}
424         self.ovs_dpdk.vm_flavor = {'ram': '1024'}
425         self.ovs_dpdk.file_path = '/var/lib/libvirt/images/cdrom-0.img'
426         self.ovs_dpdk.configure_nics_for_ovs_dpdk = mock.Mock(return_value="")
427         self.ovs_dpdk._name_task_id = 'fake_name'
428         xml_str = 'vm-0'
429         self.ovs_dpdk.mac = '00:00:00:00:00:01'
430         mock_build_xml.return_value = (xml_str, self.ovs_dpdk.mac)
431         self.ovs_dpdk._enable_interfaces = mock.Mock(return_value=xml_str)
432         vnf_instance = mock.Mock()
433         vnf_instance_2 = mock.Mock()
434         mock_check_update_key.return_value = vnf_instance_2
435         self.ovs_dpdk.vnf_node.generate_vnf_instance = mock.Mock(
436             return_value=vnf_instance)
437
438         self.assertEqual([vnf_instance_2],
439                          self.ovs_dpdk.setup_ovs_dpdk_context())
440         mock_setup_hugepages.assert_called_once_with(self.ovs_dpdk.connection,
441             (1024 + 4096) * 1024) # ram + dpdk_socket0_mem + dpdk_socket1_mem
442         mock__check_hugepages.assert_called_once()
443         mock_create_vm.assert_called_once_with(
444             self.ovs_dpdk.connection, '/tmp/vm_ovs_0.xml')
445         mock_check_if_exists.assert_called_once_with(
446             'vm-0', self.ovs_dpdk.connection)
447         mock_build_xml.assert_called_once_with(
448             self.ovs_dpdk.connection, self.ovs_dpdk.vm_flavor, 'vm-0', 0, self.ovs_dpdk.file_path)
449         mock_write_file.assert_called_once_with('/tmp/vm_ovs_0.xml', xml_str)
450         mock_check_update_key.assert_called_once_with(self.ovs_dpdk.connection,
451                                                       vnf_instance,
452                                                       xml_str,
453                                                       self.ovs_dpdk._name_task_id,
454                                                       self.ovs_dpdk.file_path,
455                                                       self.ovs_dpdk.mac)
456
457     @mock.patch.object(io, 'BytesIO')
458     def test__check_hugepages(self, mock_bytesio):
459         data = six.BytesIO('HugePages_Total:      20\n'
460                            'HugePages_Free:       20\n'
461                            'HugePages_Rsvd:        0\n'
462                            'HugePages_Surp:        0\n'
463                            'Hugepagesize:    1048576 kB'.encode())
464         mock_bytesio.return_value = data
465         self.ovs_dpdk.connection = mock.Mock()
466         self.ovs_dpdk._check_hugepages()
467
468     @mock.patch.object(io, 'BytesIO')
469     def test__check_hugepages_no_info(self, mock_bytesio):
470         data = six.BytesIO(''.encode())
471         mock_bytesio.return_value = data
472         self.ovs_dpdk.connection = mock.Mock()
473         with self.assertRaises(exceptions.OVSHugepagesInfoError):
474             self.ovs_dpdk._check_hugepages()
475
476     @mock.patch.object(io, 'BytesIO')
477     def test__check_hugepages_no_total_hp(self, mock_bytesio):
478         data = six.BytesIO('HugePages_Total:       0\n'
479                            'HugePages_Free:        0\n'
480                            'HugePages_Rsvd:        0\n'
481                            'HugePages_Surp:        0\n'
482                            'Hugepagesize:    1048576 kB'.encode())
483         mock_bytesio.return_value = data
484         self.ovs_dpdk.connection = mock.Mock()
485         with self.assertRaises(exceptions.OVSHugepagesNotConfigured):
486             self.ovs_dpdk._check_hugepages()
487
488     @mock.patch.object(io, 'BytesIO')
489     def test__check_hugepages_no_free_hp(self, mock_bytesio):
490         data = six.BytesIO('HugePages_Total:      20\n'
491                            'HugePages_Free:        0\n'
492                            'HugePages_Rsvd:        0\n'
493                            'HugePages_Surp:        0\n'
494                            'Hugepagesize:    1048576 kB'.encode())
495         mock_bytesio.return_value = data
496         self.ovs_dpdk.connection = mock.Mock()
497         with self.assertRaises(exceptions.OVSHugepagesZeroFree) as exc:
498             self.ovs_dpdk._check_hugepages()
499         self.assertEqual('There are no HugePages free in this system. Total '
500                          'HugePages configured: 20', exc.exception.msg)