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