Improve OVS-DPDK boot process
[yardstick.git] / yardstick / tests / unit / benchmark / contexts / standalone / test_model.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 copy
16 import os
17 import unittest
18 import mock
19
20 from xml.etree import ElementTree
21
22 from yardstick import ssh
23 from yardstick.benchmark.contexts.standalone import model
24 from yardstick import constants
25 from yardstick.network_services import utils
26
27
28 XML_SAMPLE = """<?xml version="1.0"?>
29 <domain type="kvm">
30     <devices>
31     </devices>
32 </domain>
33 """
34
35 XML_SAMPLE_INTERFACE = """<?xml version="1.0"?>
36 <domain type="kvm">
37     <devices>
38         <interface>
39         </interface>
40     </devices>
41 </domain>
42 """
43
44
45 class ModelLibvirtTestCase(unittest.TestCase):
46
47     def setUp(self):
48         self.xml = ElementTree.ElementTree(
49             element=ElementTree.fromstring(XML_SAMPLE))
50         self.pci_address_str = '0001:04:03.2'
51         self.pci_address = utils.PciAddress(self.pci_address_str)
52         self.mac = '00:00:00:00:00:01'
53         self._mock_write_xml = mock.patch.object(ElementTree.ElementTree,
54                                                  'write')
55         self.mock_write_xml = self._mock_write_xml.start()
56
57         self.addCleanup(self._cleanup)
58
59     def _cleanup(self):
60         self._mock_write_xml.stop()
61
62     # TODO: Remove mocking of yardstick.ssh.SSH (here and elsewhere)
63     # In this case, we are mocking a param to be passed into other methods
64     # It can be a generic Mock() with return values set for the right methods
65     def test_check_if_vm_exists_and_delete(self):
66         with mock.patch("yardstick.ssh.SSH") as ssh:
67             ssh_mock = mock.Mock(autospec=ssh.SSH)
68             ssh_mock.execute = mock.Mock(return_value=(0, "a", ""))
69             ssh.return_value = ssh_mock
70         # NOTE(ralonsoh): this test doesn't cover function execution.
71         model.Libvirt.check_if_vm_exists_and_delete("vm_0", ssh_mock)
72
73     def test_virsh_create_vm(self):
74         with mock.patch("yardstick.ssh.SSH") as ssh:
75             ssh_mock = mock.Mock(autospec=ssh.SSH)
76             ssh_mock.execute = mock.Mock(return_value=(0, "a", ""))
77             ssh.return_value = ssh_mock
78         # NOTE(ralonsoh): this test doesn't cover function execution.
79         model.Libvirt.virsh_create_vm(ssh_mock, "vm_0")
80
81     def test_virsh_destroy_vm(self):
82         with mock.patch("yardstick.ssh.SSH") as ssh:
83             ssh_mock = mock.Mock(autospec=ssh.SSH)
84             ssh_mock.execute = mock.Mock(return_value=(0, "a", ""))
85             ssh.return_value = ssh_mock
86         # NOTE(ralonsoh): this test doesn't cover function execution.
87         model.Libvirt.virsh_destroy_vm("vm_0", ssh_mock)
88
89     def test_add_interface_address(self):
90         xml = ElementTree.ElementTree(
91             element=ElementTree.fromstring(XML_SAMPLE_INTERFACE))
92         interface = xml.find('devices').find('interface')
93         result = model.Libvirt._add_interface_address(interface, self.pci_address)
94         self.assertEqual('pci', result.get('type'))
95         self.assertEqual('0x{}'.format(self.pci_address.domain),
96                          result.get('domain'))
97         self.assertEqual('0x{}'.format(self.pci_address.bus),
98                          result.get('bus'))
99         self.assertEqual('0x{}'.format(self.pci_address.slot),
100                          result.get('slot'))
101         self.assertEqual('0x{}'.format(self.pci_address.function),
102                          result.get('function'))
103
104     def test_add_ovs_interfaces(self):
105         xml_input = mock.Mock()
106         with mock.patch.object(ElementTree, 'parse', return_value=self.xml) \
107                 as mock_parse:
108             xml = copy.deepcopy(self.xml)
109             mock_parse.return_value = xml
110             model.Libvirt.add_ovs_interface(
111                 '/usr/local', 0, self.pci_address_str, self.mac, xml_input)
112             mock_parse.assert_called_once_with(xml_input)
113             self.mock_write_xml.assert_called_once_with(xml_input)
114             interface = xml.find('devices').find('interface')
115             self.assertEqual('vhostuser', interface.get('type'))
116             mac = interface.find('mac')
117             self.assertEqual(self.mac, mac.get('address'))
118             source = interface.find('source')
119             self.assertEqual('unix', source.get('type'))
120             self.assertEqual('/usr/local/var/run/openvswitch/dpdkvhostuser0',
121                              source.get('path'))
122             self.assertEqual('client', source.get('mode'))
123             _model = interface.find('model')
124             self.assertEqual('virtio', _model.get('type'))
125             driver = interface.find('driver')
126             self.assertEqual('4', driver.get('queues'))
127             host = driver.find('host')
128             self.assertEqual('off', host.get('mrg_rxbuf'))
129             self.assertIsNotNone(interface.find('address'))
130
131     def test_add_sriov_interfaces(self):
132         xml_input = mock.Mock()
133         with mock.patch.object(ElementTree, 'parse', return_value=self.xml) \
134                 as mock_parse:
135             xml = copy.deepcopy(self.xml)
136             mock_parse.return_value = xml
137             vm_pci = '0001:05:04.2'
138             model.Libvirt.add_sriov_interfaces(
139                 vm_pci, self.pci_address_str, self.mac, xml_input)
140             mock_parse.assert_called_once_with(xml_input)
141             self.mock_write_xml.assert_called_once_with(xml_input)
142             interface = xml.find('devices').find('interface')
143             self.assertEqual('yes', interface.get('managed'))
144             self.assertEqual('hostdev', interface.get('type'))
145             mac = interface.find('mac')
146             self.assertEqual(self.mac, mac.get('address'))
147             source = interface.find('source')
148             source_address = source.find('address')
149             self.assertIsNotNone(source.find('address'))
150
151             self.assertEqual('pci', source_address.get('type'))
152             self.assertEqual('0x' + self.pci_address_str.split(':')[0],
153                              source_address.get('domain'))
154             self.assertEqual('0x' + self.pci_address_str.split(':')[1],
155                              source_address.get('bus'))
156             self.assertEqual('0x' + self.pci_address_str.split(':')[2].split('.')[0],
157                              source_address.get('slot'))
158             self.assertEqual('0x' + self.pci_address_str.split(':')[2].split('.')[1],
159                              source_address.get('function'))
160
161             interface_address = interface.find('address')
162             self.assertEqual('pci', interface_address.get('type'))
163             self.assertEqual('0x' + vm_pci.split(':')[0],
164                              interface_address.get('domain'))
165             self.assertEqual('0x' + vm_pci.split(':')[1],
166                              interface_address.get('bus'))
167             self.assertEqual('0x' + vm_pci.split(':')[2].split('.')[0],
168                              interface_address.get('slot'))
169             self.assertEqual('0x' + vm_pci.split(':')[2].split('.')[1],
170                              interface_address.get('function'))
171
172     def test_create_snapshot_qemu(self):
173         result = "/var/lib/libvirt/images/0.qcow2"
174         with mock.patch("yardstick.ssh.SSH") as ssh:
175             ssh_mock = mock.Mock(autospec=ssh.SSH)
176             ssh_mock.execute = \
177                 mock.Mock(return_value=(0, "a", ""))
178             ssh.return_value = ssh_mock
179         image = model.Libvirt.create_snapshot_qemu(ssh_mock, "0", "ubuntu.img")
180         self.assertEqual(image, result)
181
182     @mock.patch.object(model.Libvirt, 'pin_vcpu_for_perf')
183     @mock.patch.object(model.Libvirt, 'create_snapshot_qemu')
184     def test_build_vm_xml(self, mock_create_snapshot_qemu,
185                           *args):
186         # NOTE(ralonsoh): this test doesn't cover function execution. This test
187         # should also check mocked function calls.
188         cfg_file = 'test_config_file.cfg'
189         self.addCleanup(os.remove, cfg_file)
190         result = [4]
191         with mock.patch("yardstick.ssh.SSH") as ssh:
192             ssh_mock = mock.Mock(autospec=ssh.SSH)
193             ssh_mock.execute = \
194                 mock.Mock(return_value=(0, "a", ""))
195             ssh.return_value = ssh_mock
196         mock_create_snapshot_qemu.return_value = "0.img"
197
198         status = model.Libvirt.build_vm_xml(ssh_mock, {}, cfg_file, 'vm_0', 0)
199         self.assertEqual(status[0], result[0])
200
201     # TODO: Edit this test to test state instead of output
202     # update_interrupts_hugepages_perf does not return anything
203     def test_update_interrupts_hugepages_perf(self):
204         with mock.patch("yardstick.ssh.SSH") as ssh:
205             ssh_mock = mock.Mock(autospec=ssh.SSH)
206             ssh_mock.execute = \
207                 mock.Mock(return_value=(0, "a", ""))
208             ssh.return_value = ssh_mock
209         # NOTE(ralonsoh): 'update_interrupts_hugepages_perf' always return
210         # None, this check is trivial.
211         #status = Libvirt.update_interrupts_hugepages_perf(ssh_mock)
212         #self.assertIsNone(status)
213         model.Libvirt.update_interrupts_hugepages_perf(ssh_mock)
214
215     @mock.patch.object(model, 'CpuSysCores')
216     @mock.patch.object(model.Libvirt, 'update_interrupts_hugepages_perf')
217     def test_pin_vcpu_for_perf(self, *args):
218         # NOTE(ralonsoh): test mocked methods/variables.
219         with mock.patch("yardstick.ssh.SSH") as ssh:
220             ssh_mock = mock.Mock(autospec=ssh.SSH)
221             ssh_mock.execute = \
222                 mock.Mock(return_value=(0, "a", ""))
223             ssh.return_value = ssh_mock
224         status = model.Libvirt.pin_vcpu_for_perf(ssh_mock, 4)
225         self.assertIsNotNone(status)
226
227 class StandaloneContextHelperTestCase(unittest.TestCase):
228
229     NODE_SAMPLE = "nodes_sample.yaml"
230     NODE_SRIOV_SAMPLE = "nodes_sriov_sample.yaml"
231
232     NETWORKS = {
233         'mgmt': {'cidr': '152.16.100.10/24'},
234         'private_0': {
235             'phy_port': "0000:05:00.0",
236             'vpci': "0000:00:07.0",
237             'cidr': '152.16.100.10/24',
238             'gateway_ip': '152.16.100.20'},
239         'public_0': {
240             'phy_port': "0000:05:00.1",
241             'vpci': "0000:00:08.0",
242             'cidr': '152.16.40.10/24',
243             'gateway_ip': '152.16.100.20'}
244     }
245
246     def setUp(self):
247         self.helper = model.StandaloneContextHelper()
248
249     def test___init__(self):
250         self.assertIsNone(self.helper.file_path)
251
252     def test_install_req_libs(self):
253         with mock.patch("yardstick.ssh.SSH") as ssh:
254             ssh_mock = mock.Mock(autospec=ssh.SSH)
255             ssh_mock.execute = \
256                 mock.Mock(return_value=(1, "a", ""))
257             ssh.return_value = ssh_mock
258         # NOTE(ralonsoh): this test doesn't cover function execution. This test
259         # should also check mocked function calls.
260         model.StandaloneContextHelper.install_req_libs(ssh_mock)
261
262     def test_get_kernel_module(self):
263         with mock.patch("yardstick.ssh.SSH") as ssh:
264             ssh_mock = mock.Mock(autospec=ssh.SSH)
265             ssh_mock.execute = \
266                 mock.Mock(return_value=(1, "i40e", ""))
267             ssh.return_value = ssh_mock
268         # NOTE(ralonsoh): this test doesn't cover function execution. This test
269         # should also check mocked function calls.
270         model.StandaloneContextHelper.get_kernel_module(
271             ssh_mock, "05:00.0", None)
272
273     @mock.patch.object(model.StandaloneContextHelper, 'get_kernel_module')
274     def test_get_nic_details(self, mock_get_kernel_module):
275         with mock.patch("yardstick.ssh.SSH") as ssh:
276             ssh_mock = mock.Mock(autospec=ssh.SSH)
277             ssh_mock.execute = mock.Mock(return_value=(1, "i40e ixgbe", ""))
278             ssh.return_value = ssh_mock
279         mock_get_kernel_module.return_value = "i40e"
280         # NOTE(ralonsoh): this test doesn't cover function execution. This test
281         # should also check mocked function calls.
282         model.StandaloneContextHelper.get_nic_details(
283             ssh_mock, self.NETWORKS, 'dpdk-devbind.py')
284
285     def test_get_virtual_devices(self):
286         pattern = "PCI_SLOT_NAME=0000:05:00.0"
287         with mock.patch("yardstick.ssh.SSH") as ssh:
288             ssh_mock = mock.Mock(autospec=ssh.SSH)
289             ssh_mock.execute = \
290                 mock.Mock(return_value=(1, pattern, ""))
291             ssh.return_value = ssh_mock
292         # NOTE(ralonsoh): this test doesn't cover function execution. This test
293         # should also check mocked function calls.
294         model.StandaloneContextHelper.get_virtual_devices(
295             ssh_mock, '0000:00:05.0')
296
297     def _get_file_abspath(self, filename):
298         curr_path = os.path.dirname(os.path.abspath(__file__))
299         file_path = os.path.join(curr_path, filename)
300         return file_path
301
302     def test_read_config_file(self):
303         self.helper.file_path = self._get_file_abspath(self.NODE_SAMPLE)
304         status = self.helper.read_config_file()
305         self.assertIsNotNone(status)
306
307     def test_parse_pod_file(self):
308         self.helper.file_path = self._get_file_abspath("dummy")
309         self.assertRaises(IOError, self.helper.parse_pod_file,
310                           self.helper.file_path)
311
312         self.helper.file_path = self._get_file_abspath(self.NODE_SAMPLE)
313         self.assertRaises(TypeError, self.helper.parse_pod_file,
314                           self.helper.file_path)
315
316         self.helper.file_path = self._get_file_abspath(self.NODE_SRIOV_SAMPLE)
317         self.assertIsNotNone(self.helper.parse_pod_file(self.helper.file_path))
318
319     def test_get_mac_address(self):
320         status = model.StandaloneContextHelper.get_mac_address()
321         self.assertIsNotNone(status)
322
323     @mock.patch('yardstick.ssh.SSH')
324     def test_get_mgmt_ip(self, *args):
325         # NOTE(ralonsoh): test mocked methods/variables.
326         with mock.patch("yardstick.ssh.SSH") as ssh:
327             ssh_mock = mock.Mock(autospec=ssh.SSH)
328             ssh_mock.execute = mock.Mock(
329                 return_value=(1, "1.2.3.4 00:00:00:00:00:01", ""))
330             ssh.return_value = ssh_mock
331         # NOTE(ralonsoh): this test doesn't cover function execution. This test
332         # should also check mocked function calls.
333         status = model.StandaloneContextHelper.get_mgmt_ip(
334             ssh_mock, "00:00:00:00:00:01", "1.1.1.1/24", {})
335         self.assertIsNotNone(status)
336
337     @mock.patch('yardstick.ssh.SSH')
338     def test_get_mgmt_ip_no(self, *args):
339         # NOTE(ralonsoh): test mocked methods/variables.
340         with mock.patch("yardstick.ssh.SSH") as ssh:
341             ssh_mock = mock.Mock(autospec=ssh.SSH)
342             ssh_mock.execute = \
343                 mock.Mock(return_value=(1, "", ""))
344             ssh.return_value = ssh_mock
345         # NOTE(ralonsoh): this test doesn't cover function execution. This test
346         # should also check mocked function calls.
347         model.WAIT_FOR_BOOT = 0
348         status = model.StandaloneContextHelper.get_mgmt_ip(
349             ssh_mock, "99", "1.1.1.1/24", {})
350         self.assertIsNone(status)
351
352
353 class ServerTestCase(unittest.TestCase):
354
355     NETWORKS = {
356         'mgmt': {'cidr': '152.16.100.10/24'},
357         'private_0': {
358             'phy_port': "0000:05:00.0",
359             'vpci': "0000:00:07.0",
360             'driver': 'i40e',
361             'mac': '',
362             'cidr': '152.16.100.10/24',
363             'gateway_ip': '152.16.100.20'},
364         'public_0': {
365             'phy_port': "0000:05:00.1",
366             'vpci': "0000:00:08.0",
367             'driver': 'i40e',
368             'mac': '',
369             'cidr': '152.16.40.10/24',
370             'gateway_ip': '152.16.100.20'}
371     }
372
373     def setUp(self):
374         self.server = model.Server()
375
376     def test___init__(self):
377         self.assertIsNotNone(self.server)
378
379     def test_build_vnf_interfaces(self):
380         vnf = {
381             "network_ports": {
382                 'mgmt': {'cidr': '152.16.100.10/24'},
383                 'xe0': ['private_0'],
384                 'xe1': ['public_0'],
385             }
386         }
387         status = model.Server.build_vnf_interfaces(vnf, self.NETWORKS)
388         self.assertIsNotNone(status)
389
390     def test_generate_vnf_instance(self):
391         vnf = {
392             "network_ports": {
393                 'mgmt': {'cidr': '152.16.100.10/24'},
394                 'xe0': ['private_0'],
395                 'xe1': ['public_0'],
396             }
397         }
398         status = self.server.generate_vnf_instance(
399             {}, self.NETWORKS, '1.1.1.1/24', 'vm_0', vnf, '00:00:00:00:00:01')
400         self.assertIsNotNone(status)
401
402
403 class OvsDeployTestCase(unittest.TestCase):
404
405     OVS_DETAILS = {'version': {'ovs': 'ovs_version', 'dpdk': 'dpdk_version'}}
406
407     def setUp(self):
408         self._mock_ssh = mock.patch.object(ssh, 'SSH')
409         self.mock_ssh = self._mock_ssh .start()
410         self.ovs_deploy = model.OvsDeploy(self.mock_ssh,
411                                           '/tmp/dpdk-devbind.py',
412                                           self.OVS_DETAILS)
413         self._mock_path_isfile = mock.patch.object(os.path, 'isfile')
414         self._mock_path_join = mock.patch.object(os.path, 'join')
415         self.mock_path_isfile = self._mock_path_isfile.start()
416         self.mock_path_join = self._mock_path_join.start()
417
418         self.addCleanup(self._stop_mock)
419
420     def _stop_mock(self):
421         self._mock_ssh.stop()
422         self._mock_path_isfile.stop()
423         self._mock_path_join.stop()
424
425     @mock.patch.object(model.StandaloneContextHelper, 'install_req_libs')
426     def test_prerequisite(self, mock_install_req_libs):
427         pkgs = ["git", "build-essential", "pkg-config", "automake",
428                 "autotools-dev", "libltdl-dev", "cmake", "libnuma-dev",
429                 "libpcap-dev"]
430         self.ovs_deploy.prerequisite()
431         mock_install_req_libs.assert_called_once_with(
432             self.ovs_deploy.connection, pkgs)
433
434     def test_ovs_deploy_no_file(self):
435         self.mock_path_isfile.return_value = False
436         mock_file = mock.Mock()
437         self.mock_path_join.return_value = mock_file
438
439         self.ovs_deploy.ovs_deploy()
440         self.mock_path_isfile.assert_called_once_with(mock_file)
441         self.mock_path_join.assert_called_once_with(
442             constants.YARDSTICK_ROOT_PATH,
443             'yardstick/resources/scripts/install/',
444             self.ovs_deploy.OVS_DEPLOY_SCRIPT)
445
446     @mock.patch.object(os.environ, 'get', return_value='test_proxy')
447     def test_ovs_deploy(self, mock_env_get):
448         self.mock_path_isfile.return_value = True
449         mock_deploy_file = mock.Mock()
450         mock_remove_ovs_deploy = mock.Mock()
451         self.mock_path_join.side_effect = [mock_deploy_file,
452                                            mock_remove_ovs_deploy]
453         dpdk_version = self.OVS_DETAILS['version']['dpdk']
454         ovs_version = self.OVS_DETAILS['version']['ovs']
455
456         with mock.patch.object(self.ovs_deploy.connection, 'put') as \
457                 mock_put, \
458                 mock.patch.object(self.ovs_deploy.connection, 'execute') as \
459                 mock_execute, \
460                 mock.patch.object(self.ovs_deploy, 'prerequisite'):
461             mock_execute.return_value = (0, 0, 0)
462             self.ovs_deploy.ovs_deploy()
463
464             self.mock_path_isfile.assert_called_once_with(mock_deploy_file)
465             self.mock_path_join.assert_has_calls([
466                 mock.call(constants.YARDSTICK_ROOT_PATH,
467                           'yardstick/resources/scripts/install/',
468                           self.ovs_deploy.OVS_DEPLOY_SCRIPT),
469                 mock.call(self.ovs_deploy.bin_path,
470                           self.ovs_deploy.OVS_DEPLOY_SCRIPT)
471             ])
472             mock_put.assert_called_once_with(mock_deploy_file,
473                                              mock_remove_ovs_deploy)
474             cmd = ("sudo -E %(remote_ovs_deploy)s --ovs='%(ovs_version)s' "
475                    "--dpdk='%(dpdk_version)s' -p='%(proxy)s'" %
476                    {'remote_ovs_deploy': mock_remove_ovs_deploy,
477                     'ovs_version': ovs_version,
478                     'dpdk_version': dpdk_version,
479                     'proxy': 'test_proxy'})
480             mock_execute.assert_called_once_with(cmd)
481             mock_env_get.assert_called_once_with('http_proxy', '')