libvirt: Use the mirror element to detect job completion
The mirror element was introduced in Libvirt 0.10.0 [1] and provides a ready attribute that helps to determine when a blockjob is complete. This is now used in addition to checking the progress of a given blockjob to ensure Nova waits until these jobs complete fully. [1] https://libvirt.org/git/?p=libvirt.git;a=commit;h=ae6aa8c3965e9aaa245b8e669c6324d44312ac1b Closes-bug: #1643017 Change-Id: I0c52917a5555a70c4973f37dea1aebf878dd73b4
This commit is contained in:
parent
0195759207
commit
9ed7826419
|
@ -772,6 +772,24 @@ class LibvirtConfigGuestDiskTest(LibvirtConfigBaseTest):
|
|||
<boot order="1"/>
|
||||
</disk>""", xml)
|
||||
|
||||
def test_config_mirror_parse(self):
|
||||
xml = """
|
||||
<disk type="file" device="disk">
|
||||
<driver name="qemu" type="qcow2" cache="none" discard="unmap"/>
|
||||
<source file="/tmp/hello.qcow2"/>
|
||||
<target bus="ide" dev="/dev/hda"/>
|
||||
<serial>7a97c4a3-6f59-41d4-bf47-191d7f97f8e9</serial>
|
||||
<mirror type='file' file='/tmp/new.img' format='raw' job='copy' ready='yes'>
|
||||
<format type='raw'/>
|
||||
<source file='/tmp/new.img'/>
|
||||
</mirror>
|
||||
<boot order="1"/>
|
||||
</disk>"""
|
||||
xmldoc = etree.fromstring(xml)
|
||||
obj = config.LibvirtConfigGuestDisk()
|
||||
obj.parse_dom(xmldoc)
|
||||
self.assertEqual(obj.mirror.ready, "yes")
|
||||
|
||||
def test_config_boot_order_parse(self):
|
||||
xml = """
|
||||
<disk type="file" device="disk">
|
||||
|
|
|
@ -682,14 +682,32 @@ class GuestBlockTestCase(test.NoDBTestCase):
|
|||
is_complete = self.gblock.is_job_complete()
|
||||
self.assertFalse(is_complete)
|
||||
|
||||
def test_is_job_complete_finished(self):
|
||||
self.domain.blockJobInfo.return_value = {
|
||||
"type": 4,
|
||||
"bandwidth": 18,
|
||||
"cur": 100,
|
||||
"end": 100}
|
||||
is_complete = self.gblock.is_job_complete()
|
||||
self.assertTrue(is_complete)
|
||||
def test_is_job_complete_not_ready(self):
|
||||
gblock = self.guest.get_block_device('vda')
|
||||
disk = vconfig.LibvirtConfigGuestDisk()
|
||||
disk.mirror = vconfig.LibvirtConfigGuestDiskMirror()
|
||||
with mock.patch.object(self.guest, 'get_disk', return_value=disk):
|
||||
self.domain.blockJobInfo.return_value = {
|
||||
"type": 4,
|
||||
"bandwidth": 18,
|
||||
"cur": 100,
|
||||
"end": 100}
|
||||
is_complete = gblock.is_job_complete()
|
||||
self.assertFalse(is_complete)
|
||||
|
||||
def test_is_job_complete_ready(self):
|
||||
gblock = self.guest.get_block_device('vda')
|
||||
disk = vconfig.LibvirtConfigGuestDisk()
|
||||
disk.mirror = vconfig.LibvirtConfigGuestDiskMirror()
|
||||
disk.mirror.ready = 'yes'
|
||||
with mock.patch.object(self.guest, 'get_disk', return_value=disk):
|
||||
self.domain.blockJobInfo.return_value = {
|
||||
"type": 4,
|
||||
"bandwidth": 18,
|
||||
"cur": 100,
|
||||
"end": 100}
|
||||
is_complete = gblock.is_job_complete()
|
||||
self.assertTrue(is_complete)
|
||||
|
||||
def test_is_job_complete_no_job(self):
|
||||
self.domain.blockJobInfo.return_value = {}
|
||||
|
|
|
@ -716,6 +716,7 @@ class LibvirtConfigGuestDisk(LibvirtConfigGuestDevice):
|
|||
self.backing_store = None
|
||||
self.device_addr = None
|
||||
self.boot_order = None
|
||||
self.mirror = None
|
||||
|
||||
def format_dom(self):
|
||||
dev = super(LibvirtConfigGuestDisk, self).format_dom()
|
||||
|
@ -875,6 +876,10 @@ class LibvirtConfigGuestDisk(LibvirtConfigGuestDevice):
|
|||
self.device_addr = obj
|
||||
elif c.tag == 'boot':
|
||||
self.boot_order = c.get('order')
|
||||
elif c.tag == 'mirror':
|
||||
m = LibvirtConfigGuestDiskMirror()
|
||||
m.parse_dom(c)
|
||||
self.mirror = m
|
||||
|
||||
|
||||
class LibvirtConfigGuestDiskBackingStore(LibvirtConfigObject):
|
||||
|
@ -1080,6 +1085,16 @@ class LibvirtConfigGuestFilesys(LibvirtConfigGuestDevice):
|
|||
return dev
|
||||
|
||||
|
||||
class LibvirtConfigGuestDiskMirror(LibvirtConfigObject):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(LibvirtConfigGuestDiskMirror, self).__init__(**kwargs)
|
||||
self.ready = None
|
||||
|
||||
def parse_dom(self, xmldoc):
|
||||
self.ready = xmldoc.get('ready')
|
||||
|
||||
|
||||
class LibvirtConfigGuestIDMap(LibvirtConfigObject):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
|
|
@ -315,7 +315,15 @@ class Guest(object):
|
|||
doc = etree.fromstring(self._domain.XMLDesc(0))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
# FIXME(lyarwood): Workaround for the device being either a target dev
|
||||
# when called via swap_volume or source file when called via
|
||||
# live_snapshot. This should be removed once both are refactored to use
|
||||
# only the target dev of the device.
|
||||
node = doc.find("./devices/disk/target[@dev='%s'].." % device)
|
||||
if node is None:
|
||||
node = doc.find("./devices/disk/source[@file='%s'].." % device)
|
||||
|
||||
if node is not None:
|
||||
conf = vconfig.LibvirtConfigGuestDisk()
|
||||
conf.parse_dom(node)
|
||||
|
@ -787,7 +795,14 @@ class BlockDevice(object):
|
|||
# The earliest tag which contains this commit is v2.3.0-rc1, so we
|
||||
# should be able to remove this workaround when MIN_LIBVIRT_VERSION
|
||||
# reaches 2.3.0, or we move to handling job events instead.
|
||||
return status.end != 0 and status.cur == status.end
|
||||
# NOTE(lyarwood): Use the mirror element to determine if we can pivot
|
||||
# to the new disk once blockjobinfo reports progress as complete.
|
||||
if status.end != 0 and status.cur == status.end:
|
||||
disk = self._guest.get_disk(self._disk)
|
||||
if disk and disk.mirror:
|
||||
return disk.mirror.ready == 'yes'
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class VCPUInfo(object):
|
||||
|
|
Loading…
Reference in New Issue