mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-10 04:06:47 +00:00
fix(vrtManager): a failed NVRAM copy aborts its libvirt streams
libvirt requires a stream that did not finish to be aborted before it is freed. The NVRAM download of a shut-off move and upload_volume (NVRAM upload, cloud-init seed) freed theirs after a failed send, receive or finish without aborting, on the shared host connection. They abort now; upload_volume opens its stream before it makes the volume, so the cleanup has both.
This commit is contained in:
parent
3911e9e238
commit
d787d587bc
4 changed files with 21 additions and 4 deletions
|
|
@ -1039,17 +1039,20 @@ class wvmConnect(object):
|
|||
"""A new raw volume in pool holding data, written through a libvirt
|
||||
stream, which every connection type carries. A failed write leaves
|
||||
no volume."""
|
||||
stream = self.wvm.newStream(0)
|
||||
vol = pool.createXML(
|
||||
f"<volume><name>{util.xml_escape(name)}</name><capacity>{len(data)}</capacity>"
|
||||
"<target><format type='raw'/></target></volume>",
|
||||
0,
|
||||
)
|
||||
try:
|
||||
stream = self.wvm.newStream(0)
|
||||
vol.upload(stream, 0, len(data), 0)
|
||||
stream.sendAll(lambda _stream, size, buffer: buffer.read(size), io.BytesIO(data))
|
||||
stream.finish()
|
||||
except Exception:
|
||||
# libvirt: a stream that did not finish is aborted before it is freed
|
||||
with contextlib.suppress(libvirtError):
|
||||
stream.abort()
|
||||
vol.delete(0)
|
||||
raise
|
||||
return vol
|
||||
|
|
|
|||
|
|
@ -427,9 +427,14 @@ class wvmInstances(wvmConnect):
|
|||
return None
|
||||
chunks = []
|
||||
stream = conn.wvm.newStream(0)
|
||||
vol.download(stream, 0, 0, 0)
|
||||
stream.recvAll(lambda _stream, data, out: out.append(data), chunks)
|
||||
stream.finish()
|
||||
try:
|
||||
vol.download(stream, 0, 0, 0)
|
||||
stream.recvAll(lambda _stream, data, out: out.append(data), chunks)
|
||||
stream.finish()
|
||||
except Exception:
|
||||
with contextlib.suppress(libvirtError):
|
||||
stream.abort()
|
||||
raise
|
||||
self.upload_volume(target, name, b"".join(chunks))
|
||||
return name
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ class UploadVolumeTestCase(unittest.TestCase):
|
|||
with self.assertRaises(RuntimeError):
|
||||
conn.upload_volume(pool, "a.iso", b"data")
|
||||
pool.createXML.return_value.delete.assert_called_once_with(0)
|
||||
conn.wvm.newStream.return_value.abort.assert_called_once_with()
|
||||
|
||||
|
||||
class DeleteWithDisksTestCase(unittest.TestCase):
|
||||
|
|
|
|||
|
|
@ -465,6 +465,14 @@ class NvramCopyTestCase(unittest.TestCase):
|
|||
copy.upload.assert_called_once_with(self.dest.wvm.newStream.return_value, 0, 4, 0)
|
||||
self.assertEqual(self.host.defined, ["<domain><name>vm</name></domain>"])
|
||||
|
||||
def test_a_failed_download_aborts_its_stream(self):
|
||||
stream = self.source.wvm.newStream.return_value
|
||||
stream.recvAll.side_effect = libvirt.libvirtError("download failed")
|
||||
with self.assertRaises(libvirt.libvirtError):
|
||||
self.move()
|
||||
stream.abort.assert_called_once_with()
|
||||
self.dest_pool.createXML.assert_not_called()
|
||||
|
||||
def test_a_shared_directory_is_not_copied(self):
|
||||
self.shared.stop()
|
||||
with mock.patch.object(wvmInstances, "same_directory", return_value=True):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue