Skip to content

Commit 8525425

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Detach disks using alias when possible"
2 parents 25e4715 + 1ed02d5 commit 8525425

File tree

4 files changed

+146
-8
lines changed

4 files changed

+146
-8
lines changed

nova/tests/unit/virt/libvirt/test_driver.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9931,17 +9931,21 @@ def test_attach_volume_with_vir_domain_affect_live_flag(self,
99319931
@mock.patch('threading.Event', new=mock.Mock())
99329932
@mock.patch('nova.virt.libvirt.host.Host._get_domain')
99339933
def test_detach_volume_with_vir_domain_affect_live_flag(self,
9934-
mock_get_domain):
9934+
mock_get_domain, use_alias=True):
99359935
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
99369936
instance = objects.Instance(**self.test_instance)
9937+
volume_id = uuids.volume
9938+
alias_xml = '<alias name="%s"/>' % vconfig.make_libvirt_device_alias(
9939+
volume_id)
99379940
mock_xml_with_disk = """<domain>
99389941
<devices>
99399942
<disk type='file'>
9943+
%(alias)s
99409944
<source file='/path/to/fake-volume'/>
99419945
<target dev='vdc' bus='virtio'/>
99429946
</disk>
99439947
</devices>
9944-
</domain>"""
9948+
</domain>""" % {'alias': use_alias and alias_xml or ''}
99459949
mock_xml_without_disk = """<domain>
99469950
<devices>
99479951
</devices>
@@ -9953,14 +9957,26 @@ def test_detach_volume_with_vir_domain_affect_live_flag(self,
99539957
mock_xml_with_disk, # persistent domain
99549958
mock_xml_with_disk, # live domain
99559959
mock_xml_without_disk, # persistent gone
9956-
mock_xml_without_disk # live gone
9960+
mock_xml_without_disk, # persistent gone (no-alias retry)
9961+
mock_xml_without_disk, # live gone
9962+
mock_xml_without_disk, # live gone (no-alias retry)
99579963
]
9964+
if not use_alias:
9965+
# If we're not using the alias to detach we will end up with two
9966+
# extra checks of the XML with the alias (which will fail to find
9967+
# anything) before we fall back to by-path and succeed.
9968+
return_list = [
9969+
mock_xml_with_disk, # persistent check for missing alias
9970+
mock_xml_with_disk, # live check for missing alias
9971+
] + return_list
9972+
99589973
# Doubling the size of return list because we test with two guest power
99599974
# states
99609975
mock_dom.XMLDesc.side_effect = return_list + return_list
99619976

99629977
connection_info = {"driver_volume_type": "fake",
99639978
"data": {"device_path": "/fake",
9979+
"volume_id": volume_id,
99649980
"access_mode": "rw"}}
99659981

99669982
with mock.patch.object(drvr, '_disconnect_volume') as \
@@ -9974,9 +9990,10 @@ def test_detach_volume_with_vir_domain_affect_live_flag(self,
99749990

99759991
mock_get_domain.assert_called_with(instance)
99769992
xml = """<disk type="file" device="disk">
9993+
%(alias)s
99779994
<source file="/path/to/fake-volume"/>
99789995
<target bus="virtio" dev="vdc"/>
9979-
</disk>"""
9996+
</disk>""" % {'alias': use_alias and alias_xml or ''}
99809997
# we expect two separate detach calls
99819998
self.assertEqual(2, mock_dom.detachDeviceFlags.call_count)
99829999
# one for the persistent domain
@@ -9995,6 +10012,10 @@ def test_detach_volume_with_vir_domain_affect_live_flag(self,
999510012
mock_disconnect_volume.assert_called_with(
999610013
self.context, connection_info, instance, encryption=None)
999710014

10015+
def test_detach_volume_with_vir_domain_affect_live_flag_legacy(self):
10016+
self.test_detach_volume_with_vir_domain_affect_live_flag(
10017+
use_alias=False)
10018+
999810019
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume')
999910020
@mock.patch('nova.virt.libvirt.host.Host._get_domain')
1000010021
def test_detach_volume_disk_not_found(self, mock_get_domain,
@@ -10092,7 +10113,7 @@ def test_detach_volume_order_with_encryptors(self, mock_get_guest,
1009210113
mock.call.detach_volume(
1009310114
mock_guest,
1009410115
instance.uuid,
10095-
# it is functools.partial(mock_guest.get_disk, 'vdc')
10116+
# it is functools.partial(driver._get_guest_disk_device, 'vdc')
1009610117
# see assert below
1009710118
mock.ANY,
1009810119
device_name='vdc',
@@ -10105,8 +10126,10 @@ def test_detach_volume_order_with_encryptors(self, mock_get_guest,
1010510126
)
1010610127
])
1010710128
get_device_conf_func = mock_detach_with_retry.mock_calls[0][1][2]
10108-
self.assertEqual(mock_guest.get_disk, get_device_conf_func.func)
10109-
self.assertEqual(('vdc',), get_device_conf_func.args)
10129+
self.assertEqual(drvr._get_guest_disk_device,
10130+
get_device_conf_func.func)
10131+
self.assertEqual((mock_get_guest.return_value, 'vdc'),
10132+
get_device_conf_func.args)
1011010133

1011110134
def test_extend_volume(self):
1011210135
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)

nova/tests/unit/virt/libvirt/test_guest.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,84 @@ def test_get_config(self):
260260
self.assertEqual('kvm', result.virt_type)
261261
self.assertEqual('fake', result.name)
262262

263+
def test_get_device_by_alias(self):
264+
xml = """
265+
<domain type='qemu'>
266+
<name>QEMUGuest1</name>
267+
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
268+
<memory unit='KiB'>219136</memory>
269+
<currentMemory unit='KiB'>219136</currentMemory>
270+
<vcpu placement='static'>1</vcpu>
271+
<os>
272+
<type arch='i686' machine='pc'>hvm</type>
273+
<boot dev='hd'/>
274+
</os>
275+
<clock offset='utc'/>
276+
<on_poweroff>destroy</on_poweroff>
277+
<on_reboot>restart</on_reboot>
278+
<on_crash>destroy</on_crash>
279+
<devices>
280+
<emulator>/usr/bin/qemu</emulator>
281+
<disk type='block' device='disk'>
282+
<alias name="qemu-disk1"/>
283+
<driver name='qemu' type='raw'/>
284+
<source dev='/dev/HostVG/QEMUGuest2'/>
285+
<target dev='hda' bus='ide'/>
286+
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
287+
</disk>
288+
<disk type='network' device='disk'>
289+
<alias name="ua-netdisk"/>
290+
<driver name='qemu' type='raw'/>
291+
<auth username='myname'>
292+
<secret type='iscsi' usage='mycluster_myname'/>
293+
</auth>
294+
<source protocol='iscsi' name='iqn.1992-01.com.example'>
295+
<host name='example.org' port='6000'/>
296+
</source>
297+
<target dev='vda' bus='virtio'/>
298+
</disk>
299+
<disk type='network' device='disk'>
300+
<driver name='qemu' type='raw'/>
301+
<source protocol='iscsi' name='iqn.1992-01.com.example/1'>
302+
<host name='example.org' port='6000'/>
303+
</source>
304+
<target dev='vdb' bus='virtio'/>
305+
</disk>
306+
<hostdev mode='subsystem' type='pci' managed='yes'>
307+
<source>
308+
<address domain='0x0000' bus='0x06' slot='0x12' function='0x5'/>
309+
</source>
310+
</hostdev>
311+
<hostdev mode='subsystem' type='pci' managed='yes'>
312+
<source>
313+
<address domain='0x0000' bus='0x06' slot='0x12' function='0x6'/>
314+
</source>
315+
</hostdev>
316+
<interface type="bridge">
317+
<mac address="fa:16:3e:f9:af:ae"/>
318+
<model type="virtio"/>
319+
<driver name="qemu"/>
320+
<source bridge="qbr84008d03-11"/>
321+
<target dev="tap84008d03-11"/>
322+
</interface>
323+
<controller type='usb' index='0'/>
324+
<controller type='pci' index='0' model='pci-root'/>
325+
<memballoon model='none'/>
326+
</devices>
327+
</domain>
328+
"""
329+
self.domain.XMLDesc.return_value = xml
330+
331+
dev = self.guest.get_device_by_alias('qemu-disk1')
332+
self.assertIsInstance(dev, vconfig.LibvirtConfigGuestDisk)
333+
self.assertEqual('hda', dev.target_dev)
334+
335+
dev = self.guest.get_device_by_alias('ua-netdisk')
336+
self.assertIsInstance(dev, vconfig.LibvirtConfigGuestDisk)
337+
self.assertEqual('vda', dev.target_dev)
338+
339+
self.assertIsNone(self.guest.get_device_by_alias('nope'))
340+
263341
def test_get_devices(self):
264342
xml = """
265343
<domain type='qemu'>

nova/virt/libvirt/driver.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2715,6 +2715,33 @@ def _detach_sync(
27152715
'instance %s: %s', device_name, instance_uuid, str(ex))
27162716
raise
27172717

2718+
def _get_guest_disk_device(self, guest, disk_dev, volume_uuid=None,
2719+
from_persistent_config=False):
2720+
"""Attempt to find the guest disk
2721+
2722+
If a volume_uuid is provided, we will look for the device based
2723+
on the nova-specified alias. If not, or we do not find it that way,
2724+
fall back to the old way of using the disk_dev.
2725+
"""
2726+
if volume_uuid is not None:
2727+
dev_alias = vconfig.make_libvirt_device_alias(volume_uuid)
2728+
dev = guest.get_device_by_alias(
2729+
dev_alias,
2730+
from_persistent_config=from_persistent_config)
2731+
if dev:
2732+
LOG.debug('Found disk %s by alias %s', disk_dev, dev_alias)
2733+
return dev
2734+
dev = guest.get_disk(disk_dev,
2735+
from_persistent_config=from_persistent_config)
2736+
if dev:
2737+
# NOTE(danms): Only log that we fell back to the old way if it
2738+
# worked. Since we call this method after detach is done to
2739+
# ensure it is gone, we will always "fall back" to make sure it
2740+
# is gone by the "old way" and thus shouldn't announce it.
2741+
LOG.info('Device %s not found by alias %s, falling back',
2742+
disk_dev, dev_alias)
2743+
return dev
2744+
27182745
def detach_volume(self, context, connection_info, instance, mountpoint,
27192746
encryption=None):
27202747
disk_dev = mountpoint.rpartition("/")[2]
@@ -2725,7 +2752,11 @@ def detach_volume(self, context, connection_info, instance, mountpoint,
27252752
# detaching any attached encryptors or disconnecting the underlying
27262753
# volume in _disconnect_volume. Otherwise, the encryptor or volume
27272754
# driver may report that the volume is still in use.
2728-
get_dev = functools.partial(guest.get_disk, disk_dev)
2755+
volume_id = driver_block_device.get_volume_id(connection_info)
2756+
get_dev = functools.partial(self._get_guest_disk_device,
2757+
guest,
2758+
disk_dev,
2759+
volume_uuid=volume_id)
27292760
self._detach_with_retry(
27302761
guest,
27312762
instance.uuid,

nova/virt/libvirt/guest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,12 @@ def get_all_disks(self):
414414

415415
return self.get_all_devices(vconfig.LibvirtConfigGuestDisk)
416416

417+
def get_device_by_alias(self, devalias, devtype=None,
418+
from_persistent_config=False):
419+
for dev in self.get_all_devices(devtype):
420+
if dev.alias == devalias:
421+
return dev
422+
417423
def get_all_devices(
418424
self,
419425
devtype: vconfig.LibvirtConfigGuestDevice = None,

0 commit comments

Comments
 (0)