1 # Copyright (c) 2016-2017 Intel Corporation
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
21 from xml.etree import ElementTree
23 from yardstick import ssh
24 from yardstick.benchmark.contexts.standalone import model
25 from yardstick.common import exceptions
26 from yardstick import constants
27 from yardstick.network_services import utils
30 XML_SAMPLE = """<?xml version="1.0"?>
37 XML_SAMPLE_INTERFACE = """<?xml version="1.0"?>
47 class ModelLibvirtTestCase(unittest.TestCase):
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_ssh = mock.Mock()
54 self.mock_ssh = self._mock_ssh.start()
55 self.addCleanup(self._cleanup)
60 # TODO: Remove mocking of yardstick.ssh.SSH (here and elsewhere)
61 # In this case, we are mocking a param to be passed into other methods
62 # It can be a generic Mock() with return values set for the right methods
63 def test_check_if_vm_exists_and_delete(self):
64 with mock.patch("yardstick.ssh.SSH") as ssh:
65 ssh_mock = mock.Mock(autospec=ssh.SSH)
66 ssh_mock.execute = mock.Mock(return_value=(0, "a", ""))
67 ssh.return_value = ssh_mock
68 # NOTE(ralonsoh): this test doesn't cover function execution.
69 model.Libvirt.check_if_vm_exists_and_delete("vm_0", ssh_mock)
71 def test_virsh_create_vm(self):
72 self.mock_ssh.execute = mock.Mock(return_value=(0, 0, 0))
73 model.Libvirt.virsh_create_vm(self.mock_ssh, 'vm_0')
74 self.mock_ssh.execute.assert_called_once_with('virsh create vm_0')
76 def test_virsh_create_vm_error(self):
77 self.mock_ssh.execute = mock.Mock(return_value=(1, 0, 'error_create'))
78 with self.assertRaises(exceptions.LibvirtCreateError) as exc:
79 model.Libvirt.virsh_create_vm(self.mock_ssh, 'vm_0')
80 self.assertEqual('Error creating the virtual machine. Error: '
81 'error_create.', str(exc.exception))
82 self.mock_ssh.execute.assert_called_once_with('virsh create vm_0')
84 def test_virsh_destroy_vm(self):
85 self.mock_ssh.execute = mock.Mock(return_value=(0, 0, 0))
86 model.Libvirt.virsh_destroy_vm('vm_0', self.mock_ssh)
87 self.mock_ssh.execute.assert_called_once_with('virsh destroy vm_0')
89 @mock.patch.object(model, 'LOG')
90 def test_virsh_destroy_vm_error(self, mock_logger):
91 self.mock_ssh.execute = mock.Mock(return_value=(1, 0, 'error_destroy'))
92 mock_logger.warning = mock.Mock()
93 model.Libvirt.virsh_destroy_vm('vm_0', self.mock_ssh)
94 mock_logger.warning.assert_called_once_with(
95 'Error destroying VM %s. Error: %s', 'vm_0', 'error_destroy')
96 self.mock_ssh.execute.assert_called_once_with('virsh destroy vm_0')
98 def test_add_interface_address(self):
99 xml = ElementTree.ElementTree(
100 element=ElementTree.fromstring(XML_SAMPLE_INTERFACE))
101 interface = xml.find('devices').find('interface')
102 result = model.Libvirt._add_interface_address(interface, self.pci_address)
103 self.assertEqual('pci', result.get('type'))
104 self.assertEqual('0x{}'.format(self.pci_address.domain),
105 result.get('domain'))
106 self.assertEqual('0x{}'.format(self.pci_address.bus),
108 self.assertEqual('0x{}'.format(self.pci_address.slot),
110 self.assertEqual('0x{}'.format(self.pci_address.function),
111 result.get('function'))
113 def test_add_ovs_interfaces(self):
114 xml_input = copy.deepcopy(XML_SAMPLE)
115 xml_output = model.Libvirt.add_ovs_interface(
116 '/usr/local', 0, self.pci_address_str, self.mac, xml_input)
118 root = ElementTree.fromstring(xml_output)
119 et_out = ElementTree.ElementTree(element=root)
120 interface = et_out.find('devices').find('interface')
121 self.assertEqual('vhostuser', interface.get('type'))
122 mac = interface.find('mac')
123 self.assertEqual(self.mac, mac.get('address'))
124 source = interface.find('source')
125 self.assertEqual('unix', source.get('type'))
126 self.assertEqual('/usr/local/var/run/openvswitch/dpdkvhostuser0',
128 self.assertEqual('client', source.get('mode'))
129 _model = interface.find('model')
130 self.assertEqual('virtio', _model.get('type'))
131 driver = interface.find('driver')
132 self.assertEqual('4', driver.get('queues'))
133 host = driver.find('host')
134 self.assertEqual('off', host.get('mrg_rxbuf'))
135 self.assertIsNotNone(interface.find('address'))
137 def test_add_sriov_interfaces(self):
138 xml_input = copy.deepcopy(XML_SAMPLE)
139 vm_pci = '0001:05:04.2'
140 xml_output = model.Libvirt.add_sriov_interfaces(
141 vm_pci, self.pci_address_str, self.mac, xml_input)
142 root = ElementTree.fromstring(xml_output)
143 et_out = ElementTree.ElementTree(element=root)
144 interface = et_out.find('devices').find('interface')
145 self.assertEqual('yes', interface.get('managed'))
146 self.assertEqual('hostdev', interface.get('type'))
147 mac = interface.find('mac')
148 self.assertEqual(self.mac, mac.get('address'))
149 source = interface.find('source')
150 source_address = source.find('address')
151 self.assertIsNotNone(source.find('address'))
153 self.assertEqual('pci', source_address.get('type'))
154 self.assertEqual('0x' + self.pci_address_str.split(':')[0],
155 source_address.get('domain'))
156 self.assertEqual('0x' + self.pci_address_str.split(':')[1],
157 source_address.get('bus'))
158 self.assertEqual('0x' + self.pci_address_str.split(':')[2].split('.')[0],
159 source_address.get('slot'))
160 self.assertEqual('0x' + self.pci_address_str.split(':')[2].split('.')[1],
161 source_address.get('function'))
163 interface_address = interface.find('address')
164 self.assertEqual('pci', interface_address.get('type'))
165 self.assertEqual('0x' + vm_pci.split(':')[0],
166 interface_address.get('domain'))
167 self.assertEqual('0x' + vm_pci.split(':')[1],
168 interface_address.get('bus'))
169 self.assertEqual('0x' + vm_pci.split(':')[2].split('.')[0],
170 interface_address.get('slot'))
171 self.assertEqual('0x' + vm_pci.split(':')[2].split('.')[1],
172 interface_address.get('function'))
174 def test_create_snapshot_qemu(self):
175 self.mock_ssh.execute = mock.Mock(return_value=(0, 0, 0))
177 vm_image = '/var/lib/libvirt/images/%s.qcow2' % index
178 base_image = '/tmp/base_image'
180 model.Libvirt.create_snapshot_qemu(self.mock_ssh, index, base_image)
181 self.mock_ssh.execute.assert_has_calls([
182 mock.call('rm -- "%s"' % vm_image),
183 mock.call('test -r %s' % base_image),
184 mock.call('qemu-img create -f qcow2 -o backing_file=%s %s' %
185 (base_image, vm_image))
188 @mock.patch.object(os.path, 'basename', return_value='base_image')
189 @mock.patch.object(os.path, 'normpath')
190 @mock.patch.object(os, 'access', return_value=True)
191 def test_create_snapshot_qemu_no_image_remote(self,
192 mock_os_access, mock_normpath, mock_basename):
193 self.mock_ssh.execute = mock.Mock(
194 side_effect=[(0, 0, 0), (1, 0, 0), (0, 0, 0), (0, 0, 0)])
196 vm_image = '/var/lib/libvirt/images/%s.qcow2' % index
197 base_image = '/tmp/base_image'
198 mock_normpath.return_value = base_image
200 model.Libvirt.create_snapshot_qemu(self.mock_ssh, index, base_image)
201 self.mock_ssh.execute.assert_has_calls([
202 mock.call('rm -- "%s"' % vm_image),
203 mock.call('test -r %s' % base_image),
204 mock.call('mv -- "/tmp/%s" "%s"' % ('base_image', base_image)),
205 mock.call('qemu-img create -f qcow2 -o backing_file=%s %s' %
206 (base_image, vm_image))
208 mock_os_access.assert_called_once_with(base_image, os.R_OK)
209 mock_normpath.assert_called_once_with(base_image)
210 mock_basename.assert_has_calls([mock.call(base_image)])
211 self.mock_ssh.put_file.assert_called_once_with(base_image,
214 @mock.patch.object(os, 'access', return_value=False)
215 def test_create_snapshot_qemu_no_image_local(self, mock_os_access):
216 self.mock_ssh.execute = mock.Mock(side_effect=[(0, 0, 0), (1, 0, 0)])
217 base_image = '/tmp/base_image'
219 with self.assertRaises(exceptions.LibvirtQemuImageBaseImageNotPresent):
220 model.Libvirt.create_snapshot_qemu(self.mock_ssh, 3, base_image)
221 mock_os_access.assert_called_once_with(base_image, os.R_OK)
223 def test_create_snapshot_qemu_error_qemuimg_command(self):
224 self.mock_ssh.execute = mock.Mock(
225 side_effect=[(0, 0, 0), (0, 0, 0), (1, 0, 0)])
227 vm_image = '/var/lib/libvirt/images/%s.qcow2' % index
228 base_image = '/tmp/base_image'
230 with self.assertRaises(exceptions.LibvirtQemuImageCreateError):
231 model.Libvirt.create_snapshot_qemu(self.mock_ssh, index,
233 self.mock_ssh.execute.assert_has_calls([
234 mock.call('rm -- "%s"' % vm_image),
235 mock.call('test -r %s' % base_image),
236 mock.call('qemu-img create -f qcow2 -o backing_file=%s %s' %
237 (base_image, vm_image))
240 @mock.patch.object(model.Libvirt, 'pin_vcpu_for_perf', return_value='4,5')
241 @mock.patch.object(model.Libvirt, 'create_snapshot_qemu',
242 return_value='qemu_image')
243 def test_build_vm_xml(self, mock_create_snapshot_qemu,
244 mock_pin_vcpu_for_perf):
245 extra_specs = {'hw:cpu_cores': '4',
246 'hw:cpu_sockets': '3',
247 'hw:cpu_threads': '2',
249 flavor = {'ram': '1024',
250 'extra_specs': extra_specs,
253 mac = model.StandaloneContextHelper.get_mac_address(0x00)
255 connection = mock.Mock()
256 with mock.patch.object(model.StandaloneContextHelper,
257 'get_mac_address', return_value=mac) as \
258 mock_get_mac_address, \
259 mock.patch.object(uuid, 'uuid4', return_value=_uuid):
260 xml_out, mac = model.Libvirt.build_vm_xml(
261 connection, flavor, 'vm_name', 100)
263 xml_ref = model.VM_TEMPLATE.format(vm_name='vm_name',
264 random_uuid=_uuid, mac_addr=mac, memory='1024', vcpu='8', cpu='4',
265 numa_cpus='0-7', socket='3', threads='2',
266 vm_image='qemu_image', cpuset='4,5', cputune='cool')
267 self.assertEqual(xml_ref, xml_out)
268 mock_get_mac_address.assert_called_once_with(0x00)
269 mock_create_snapshot_qemu.assert_called_once_with(
270 connection, 100, 'images')
271 mock_pin_vcpu_for_perf.assert_called_once_with(connection, '1')
273 # TODO: Edit this test to test state instead of output
274 # update_interrupts_hugepages_perf does not return anything
275 def test_update_interrupts_hugepages_perf(self):
276 with mock.patch("yardstick.ssh.SSH") as ssh:
277 ssh_mock = mock.Mock(autospec=ssh.SSH)
279 mock.Mock(return_value=(0, "a", ""))
280 ssh.return_value = ssh_mock
281 # NOTE(ralonsoh): 'update_interrupts_hugepages_perf' always return
282 # None, this check is trivial.
283 #status = Libvirt.update_interrupts_hugepages_perf(ssh_mock)
284 #self.assertIsNone(status)
285 model.Libvirt.update_interrupts_hugepages_perf(ssh_mock)
287 @mock.patch.object(model, 'CpuSysCores')
288 @mock.patch.object(model.Libvirt, 'update_interrupts_hugepages_perf')
289 def test_pin_vcpu_for_perf(self, *args):
290 # NOTE(ralonsoh): test mocked methods/variables.
291 with mock.patch("yardstick.ssh.SSH") as ssh:
292 ssh_mock = mock.Mock(autospec=ssh.SSH)
294 mock.Mock(return_value=(0, "a", ""))
295 ssh.return_value = ssh_mock
296 status = model.Libvirt.pin_vcpu_for_perf(ssh_mock, 4)
297 self.assertIsNotNone(status)
299 class StandaloneContextHelperTestCase(unittest.TestCase):
301 NODE_SAMPLE = "nodes_sample.yaml"
302 NODE_SRIOV_SAMPLE = "nodes_sriov_sample.yaml"
305 'mgmt': {'cidr': '152.16.100.10/24'},
307 'phy_port': "0000:05:00.0",
308 'vpci': "0000:00:07.0",
309 'cidr': '152.16.100.10/24',
310 'gateway_ip': '152.16.100.20'},
312 'phy_port': "0000:05:00.1",
313 'vpci': "0000:00:08.0",
314 'cidr': '152.16.40.10/24',
315 'gateway_ip': '152.16.100.20'}
319 self.helper = model.StandaloneContextHelper()
321 def test___init__(self):
322 self.assertIsNone(self.helper.file_path)
324 def test_install_req_libs(self):
325 with mock.patch("yardstick.ssh.SSH") as ssh:
326 ssh_mock = mock.Mock(autospec=ssh.SSH)
328 mock.Mock(return_value=(1, "a", ""))
329 ssh.return_value = ssh_mock
330 # NOTE(ralonsoh): this test doesn't cover function execution. This test
331 # should also check mocked function calls.
332 model.StandaloneContextHelper.install_req_libs(ssh_mock)
334 def test_get_kernel_module(self):
335 with mock.patch("yardstick.ssh.SSH") as ssh:
336 ssh_mock = mock.Mock(autospec=ssh.SSH)
338 mock.Mock(return_value=(1, "i40e", ""))
339 ssh.return_value = ssh_mock
340 # NOTE(ralonsoh): this test doesn't cover function execution. This test
341 # should also check mocked function calls.
342 model.StandaloneContextHelper.get_kernel_module(
343 ssh_mock, "05:00.0", None)
345 @mock.patch.object(model.StandaloneContextHelper, 'get_kernel_module')
346 def test_get_nic_details(self, mock_get_kernel_module):
347 with mock.patch("yardstick.ssh.SSH") as ssh:
348 ssh_mock = mock.Mock(autospec=ssh.SSH)
349 ssh_mock.execute = mock.Mock(return_value=(1, "i40e ixgbe", ""))
350 ssh.return_value = ssh_mock
351 mock_get_kernel_module.return_value = "i40e"
352 # NOTE(ralonsoh): this test doesn't cover function execution. This test
353 # should also check mocked function calls.
354 model.StandaloneContextHelper.get_nic_details(
355 ssh_mock, self.NETWORKS, 'dpdk-devbind.py')
357 def test_get_virtual_devices(self):
358 pattern = "PCI_SLOT_NAME=0000:05:00.0"
359 with mock.patch("yardstick.ssh.SSH") as ssh:
360 ssh_mock = mock.Mock(autospec=ssh.SSH)
362 mock.Mock(return_value=(1, pattern, ""))
363 ssh.return_value = ssh_mock
364 # NOTE(ralonsoh): this test doesn't cover function execution. This test
365 # should also check mocked function calls.
366 model.StandaloneContextHelper.get_virtual_devices(
367 ssh_mock, '0000:00:05.0')
369 def _get_file_abspath(self, filename):
370 curr_path = os.path.dirname(os.path.abspath(__file__))
371 file_path = os.path.join(curr_path, filename)
374 def test_read_config_file(self):
375 self.helper.file_path = self._get_file_abspath(self.NODE_SAMPLE)
376 status = self.helper.read_config_file()
377 self.assertIsNotNone(status)
379 def test_parse_pod_file(self):
380 self.helper.file_path = self._get_file_abspath("dummy")
381 self.assertRaises(IOError, self.helper.parse_pod_file,
382 self.helper.file_path)
384 self.helper.file_path = self._get_file_abspath(self.NODE_SAMPLE)
385 self.assertRaises(TypeError, self.helper.parse_pod_file,
386 self.helper.file_path)
388 self.helper.file_path = self._get_file_abspath(self.NODE_SRIOV_SAMPLE)
389 self.assertIsNotNone(self.helper.parse_pod_file(self.helper.file_path))
391 def test_get_mac_address(self):
392 status = model.StandaloneContextHelper.get_mac_address()
393 self.assertIsNotNone(status)
395 @mock.patch('yardstick.ssh.SSH')
396 def test_get_mgmt_ip(self, *args):
397 # NOTE(ralonsoh): test mocked methods/variables.
398 with mock.patch("yardstick.ssh.SSH") as ssh:
399 ssh_mock = mock.Mock(autospec=ssh.SSH)
400 ssh_mock.execute = mock.Mock(
401 return_value=(1, "1.2.3.4 00:00:00:00:00:01", ""))
402 ssh.return_value = ssh_mock
403 # NOTE(ralonsoh): this test doesn't cover function execution. This test
404 # should also check mocked function calls.
405 status = model.StandaloneContextHelper.get_mgmt_ip(
406 ssh_mock, "00:00:00:00:00:01", "1.1.1.1/24", {})
407 self.assertIsNotNone(status)
409 @mock.patch('yardstick.ssh.SSH')
410 def test_get_mgmt_ip_no(self, *args):
411 # NOTE(ralonsoh): test mocked methods/variables.
412 with mock.patch("yardstick.ssh.SSH") as ssh:
413 ssh_mock = mock.Mock(autospec=ssh.SSH)
415 mock.Mock(return_value=(1, "", ""))
416 ssh.return_value = ssh_mock
417 # NOTE(ralonsoh): this test doesn't cover function execution. This test
418 # should also check mocked function calls.
419 model.WAIT_FOR_BOOT = 0
420 status = model.StandaloneContextHelper.get_mgmt_ip(
421 ssh_mock, "99", "1.1.1.1/24", {})
422 self.assertIsNone(status)
425 class ServerTestCase(unittest.TestCase):
428 'mgmt': {'cidr': '152.16.100.10/24'},
430 'phy_port': "0000:05:00.0",
431 'vpci': "0000:00:07.0",
434 'cidr': '152.16.100.10/24',
435 'gateway_ip': '152.16.100.20'},
437 'phy_port': "0000:05:00.1",
438 'vpci': "0000:00:08.0",
441 'cidr': '152.16.40.10/24',
442 'gateway_ip': '152.16.100.20'}
446 self.server = model.Server()
448 def test___init__(self):
449 self.assertIsNotNone(self.server)
451 def test_build_vnf_interfaces(self):
454 'mgmt': {'cidr': '152.16.100.10/24'},
455 'xe0': ['private_0'],
459 status = model.Server.build_vnf_interfaces(vnf, self.NETWORKS)
460 self.assertIsNotNone(status)
462 def test_generate_vnf_instance(self):
465 'mgmt': {'cidr': '152.16.100.10/24'},
466 'xe0': ['private_0'],
470 status = self.server.generate_vnf_instance(
471 {}, self.NETWORKS, '1.1.1.1/24', 'vm_0', vnf, '00:00:00:00:00:01')
472 self.assertIsNotNone(status)
475 class OvsDeployTestCase(unittest.TestCase):
477 OVS_DETAILS = {'version': {'ovs': 'ovs_version', 'dpdk': 'dpdk_version'}}
480 self._mock_ssh = mock.patch.object(ssh, 'SSH')
481 self.mock_ssh = self._mock_ssh.start()
482 self.ovs_deploy = model.OvsDeploy(self.mock_ssh,
483 '/tmp/dpdk-devbind.py',
485 self._mock_path_isfile = mock.patch.object(os.path, 'isfile')
486 self._mock_path_join = mock.patch.object(os.path, 'join')
487 self.mock_path_isfile = self._mock_path_isfile.start()
488 self.mock_path_join = self._mock_path_join.start()
490 self.addCleanup(self._stop_mock)
492 def _stop_mock(self):
493 self._mock_ssh.stop()
494 self._mock_path_isfile.stop()
495 self._mock_path_join.stop()
497 @mock.patch.object(model.StandaloneContextHelper, 'install_req_libs')
498 def test_prerequisite(self, mock_install_req_libs):
499 pkgs = ["git", "build-essential", "pkg-config", "automake",
500 "autotools-dev", "libltdl-dev", "cmake", "libnuma-dev",
502 self.ovs_deploy.prerequisite()
503 mock_install_req_libs.assert_called_once_with(
504 self.ovs_deploy.connection, pkgs)
506 def test_ovs_deploy_no_file(self):
507 self.mock_path_isfile.return_value = False
508 mock_file = mock.Mock()
509 self.mock_path_join.return_value = mock_file
511 self.ovs_deploy.ovs_deploy()
512 self.mock_path_isfile.assert_called_once_with(mock_file)
513 self.mock_path_join.assert_called_once_with(
514 constants.YARDSTICK_ROOT_PATH,
515 'yardstick/resources/scripts/install/',
516 self.ovs_deploy.OVS_DEPLOY_SCRIPT)
518 @mock.patch.object(os.environ, 'get', return_value='test_proxy')
519 def test_ovs_deploy(self, mock_env_get):
520 self.mock_path_isfile.return_value = True
521 mock_deploy_file = mock.Mock()
522 mock_remove_ovs_deploy = mock.Mock()
523 self.mock_path_join.side_effect = [mock_deploy_file,
524 mock_remove_ovs_deploy]
525 dpdk_version = self.OVS_DETAILS['version']['dpdk']
526 ovs_version = self.OVS_DETAILS['version']['ovs']
528 with mock.patch.object(self.ovs_deploy.connection, 'put') as \
530 mock.patch.object(self.ovs_deploy.connection, 'execute') as \
532 mock.patch.object(self.ovs_deploy, 'prerequisite'):
533 mock_execute.return_value = (0, 0, 0)
534 self.ovs_deploy.ovs_deploy()
536 self.mock_path_isfile.assert_called_once_with(mock_deploy_file)
537 self.mock_path_join.assert_has_calls([
538 mock.call(constants.YARDSTICK_ROOT_PATH,
539 'yardstick/resources/scripts/install/',
540 self.ovs_deploy.OVS_DEPLOY_SCRIPT),
541 mock.call(self.ovs_deploy.bin_path,
542 self.ovs_deploy.OVS_DEPLOY_SCRIPT)
544 mock_put.assert_called_once_with(mock_deploy_file,
545 mock_remove_ovs_deploy)
546 cmd = ("sudo -E %(remote_ovs_deploy)s --ovs='%(ovs_version)s' "
547 "--dpdk='%(dpdk_version)s' -p='%(proxy)s'" %
548 {'remote_ovs_deploy': mock_remove_ovs_deploy,
549 'ovs_version': ovs_version,
550 'dpdk_version': dpdk_version,
551 'proxy': 'test_proxy'})
552 mock_execute.assert_called_once_with(cmd)
553 mock_env_get.assert_has_calls([mock.call('http_proxy', '')])