libvirt: Wrap un-proxied listDevices() and listAllDevices()
This is similar to change I668643c836d46a25df46d4c99a973af5e50a39db where the objects returned in a list from a libvirt call were not tpool.Proxy wrapped. Because the objects are not wrapped, calling methods on them such as listCaps() can block all other greenthreads and can cause nova-compute to freeze for hours in certain scenarios. This adds the same wrapping to libvirt calls which return lists of virNodeDevice. Closes-Bug: #2091033 Change-Id: I60d6f04d374e9ede5895a43b7a75e955b0fea3c5
This commit is contained in:
		 melanie witt
					melanie witt
				
			
				
					committed by
					
						 Stephen Finucane
						Stephen Finucane
					
				
			
			
				
	
			
			
			 Stephen Finucane
						Stephen Finucane
					
				
			
						parent
						
							a67ab85678
						
					
				
				
					commit
					f304b9eaad
				
			| @@ -2245,6 +2245,48 @@ class LibvirtTpoolProxyTestCase(test.NoDBTestCase): | ||||
|             self.assertIsInstance(domain, tpool.Proxy) | ||||
|             self.assertIn(domain.UUIDString(), (uuids.vm1, uuids.vm2)) | ||||
|  | ||||
|     def _add_fake_host_devices(self): | ||||
|         self.conn._obj.pci_info = fakelibvirt.HostPCIDevicesInfo( | ||||
|             num_pci=1, num_pfs=2, num_vfs=2, num_mdevcap=3) | ||||
|         mdevs = { | ||||
|             'mdev_4b20d080_1b54_4048_85b3_a6a62d165c01': | ||||
|                 fakelibvirt.FakeMdevDevice( | ||||
|                     dev_name='mdev_4b20d080_1b54_4048_85b3_a6a62d165c01', | ||||
|                     type_id=fakelibvirt.NVIDIA_11_VGPU_TYPE, | ||||
|                     parent=fakelibvirt.MDEVCAP_DEV1_PCI_ADDR), | ||||
|         } | ||||
|         self.conn._obj.mdev_info = fakelibvirt.HostMdevDevicesInfo( | ||||
|             devices=mdevs) | ||||
|  | ||||
|     def test_tpool_list_all_devices(self): | ||||
|         self._add_fake_host_devices() | ||||
|         devs = self.host.list_all_devices( | ||||
|             fakelibvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_PCI_DEV) | ||||
|         self.assertEqual(8, len(devs)) | ||||
|         for dev in devs: | ||||
|             self.assertIsInstance(dev, tpool.Proxy) | ||||
|  | ||||
|     def test_tpool_list_pci_devices(self): | ||||
|         self._add_fake_host_devices() | ||||
|         devs = self.host.list_pci_devices() | ||||
|         self.assertEqual(8, len(devs)) | ||||
|         for dev in devs: | ||||
|             self.assertIsInstance(dev, tpool.Proxy) | ||||
|  | ||||
|     def test_tpool_list_mdev_capable_devices(self): | ||||
|         self._add_fake_host_devices() | ||||
|         devs = self.host.list_mdev_capable_devices() | ||||
|         self.assertEqual(3, len(devs)) | ||||
|         for dev in devs: | ||||
|             self.assertIsInstance(dev, tpool.Proxy) | ||||
|  | ||||
|     def test_tpool_list_mediated_devices(self): | ||||
|         self._add_fake_host_devices() | ||||
|         devs = self.host.list_mediated_devices() | ||||
|         self.assertEqual(1, len(devs)) | ||||
|         for dev in devs: | ||||
|             self.assertIsInstance(dev, tpool.Proxy) | ||||
|  | ||||
|  | ||||
| class LoadersTestCase(test.NoDBTestCase): | ||||
|  | ||||
|   | ||||
| @@ -1647,7 +1647,9 @@ class Host(object): | ||||
|         :returns: a list of virNodeDevice instance | ||||
|         """ | ||||
|         try: | ||||
|             return self.get_connection().listDevices(cap, flags) | ||||
|             devs = [self._wrap_libvirt_proxy(dev) | ||||
|                     for dev in self.get_connection().listDevices(cap, flags)] | ||||
|             return devs | ||||
|         except libvirt.libvirtError as ex: | ||||
|             error_code = ex.get_error_code() | ||||
|             if error_code == libvirt.VIR_ERR_NO_SUPPORT: | ||||
| @@ -1667,7 +1669,10 @@ class Host(object): | ||||
|         :returns: a list of virNodeDevice instances. | ||||
|         """ | ||||
|         try: | ||||
|             return self.get_connection().listAllDevices(flags) or [] | ||||
|             alldevs = [ | ||||
|                 self._wrap_libvirt_proxy(dev) | ||||
|                 for dev in self.get_connection().listAllDevices(flags)] or [] | ||||
|             return alldevs | ||||
|         except libvirt.libvirtError as ex: | ||||
|             LOG.warning(ex) | ||||
|             return [] | ||||
|   | ||||
| @@ -0,0 +1,11 @@ | ||||
| fixes: | ||||
|   - | | ||||
|     `Bug #2091033`_: Fixed calls to libvirt ``listDevices()`` and | ||||
|     ``listAllDevices()`` from potentially blocking all other greenthreads | ||||
|     in ``nova-compute``. Under certain circumstances, it was possible for | ||||
|     the ``nova-compute`` service to freeze with all other greenthreads | ||||
|     blocked and unable to perform any other activities including logging. | ||||
|     This issue has been fixed by wrapping the libvirt ``listDevices()`` | ||||
|     and ``listAllDevices()`` calls with ``eventlet.tpool.Proxy``. | ||||
|  | ||||
|     .. _Bug #2091033: https://bugs.launchpad.net/nova/+bug/2091033 | ||||
		Reference in New Issue
	
	Block a user